1# Copyright 2014 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 import test 10 11 12# Timeout for commands run on the host. 13_COMMAND_TIMEOUT = 60 14 15# Lock file created by flashrom to tell powerd not to suspend or shut down the 16# system. 17_LOCK_FILE = '/run/lock/power_override/flashrom.lock' 18 19# Time in seconds to perform a flashrom write and to wait for the system to 20# suspend and resume. 21_SUSPEND_FLASHROM_SEC = 15 22_SUSPEND_DOWN_SEC = 60 23_SUSPEND_UP_SEC = 60 24 25# Time in seconds to perform a flashrom write and to wait for the system to 26# power down and reboot. 27_REBOOT_FLASHROM_SEC = 15 28_REBOOT_DOWN_SEC = 60 29_REBOOT_UP_SEC = 60 30 31 32class power_DeferForFlashrom(test.test): 33 """Test that powerd defers suspend and shutdown for flashrom.""" 34 version = 1 35 36 def initialize(self, host): 37 """ 38 Initial settings before running test. 39 40 @param host: Host/DUT object to use for test. 41 """ 42 self.host = host 43 44 45 def create_temp_file(self, base_name, source_path, size): 46 """ 47 Create a temporary file on the host and returns its path. 48 49 @param base_name: String containing the base name for the temp file. 50 @param source_path: String containing the path to the device from 51 which file contents should be read. 52 @param size: Number of bytes to write to the file. 53 """ 54 logging.info('Creating %d-byte temp file from %s', size, source_path) 55 temp_file = self.host.run( 56 'mktemp --tmpdir %s.XXXXXXXXXX' % base_name, 57 timeout=_COMMAND_TIMEOUT).stdout.strip() 58 self.host.run('dd if=%s of=%s bs=%d count=1 2>&1' % 59 (source_path, temp_file, size)) 60 logging.info('Created %s', temp_file) 61 return temp_file 62 63 64 def run_in_background(self, cmd): 65 """ 66 Asynchronously run a command on the host. 67 68 @param cmd: Command to run (as a string). 69 """ 70 bg_cmd = '(%s) </dev/null >/dev/null 2>&1 &' % (cmd) 71 logging.info("Running %s", bg_cmd) 72 self.host.run(bg_cmd, timeout=_COMMAND_TIMEOUT) 73 74 75 def start_fake_flashrom_write(self, duration_sec): 76 """ 77 Start a fake flashrom write. 78 79 @param duration_sec: Duration for the write in seconds. 80 """ 81 # flashrom simulates a 4096-byte block size, so the file size needs to 82 # be a multiple of that. 83 BLOCK_SIZE = 4096 84 85 # flashrom will write one bit per cycle. Convert the block size to bits 86 # (yielding the frequency for a one-second write) and then scale it as 87 # needed. 88 frequency_hz = int(BLOCK_SIZE * 8 / float(duration_sec)) 89 90 # To avoid flashrom needing to read (slowly) from the dummy device, pass 91 # a custom diff file filled with zeroes. 92 zero_file = self.create_temp_file( 93 'power_DeferForFlashrom.zero', '/dev/zero', BLOCK_SIZE) 94 rand_file = self.create_temp_file( 95 'power_DeferForFlashrom.rand', '/dev/urandom', BLOCK_SIZE) 96 97 # Start flashrom in the background and wait for it to create its lock 98 # file. 99 self.run_in_background( 100 ('flashrom -w %s --diff %s --noverify ' 101 '-p dummy:freq=%d,emulate=VARIABLE_SIZE,size=%d,' 102 'erase_to_zero=yes') % 103 (rand_file, zero_file, frequency_hz, BLOCK_SIZE)) 104 105 logging.info("Waiting for flashrom to create %s...", _LOCK_FILE) 106 self.host.run( 107 'while [ ! -e %s ]; do sleep 0.1; done' % (_LOCK_FILE), 108 timeout=_COMMAND_TIMEOUT) 109 110 111 def send_suspend_request(self, wake_sec): 112 """ 113 Asynchronously ask powerd to suspend the system immediately. 114 115 @param wake_sec: Integer delay in seconds to use for setting a wake 116 alarm. Note that the alarm starts when the request is sent to 117 powerd, not when the system actually suspends. 118 """ 119 self.run_in_background( 120 'powerd_dbus_suspend --delay=0 --wakeup_timeout=%d' % (wake_sec)) 121 122 123 def send_reboot_request(self): 124 """Ask powerd to reboot the system immediately.""" 125 logging.info('Calling powerd\'s RequestRestart method') 126 self.host.run( 127 ('dbus-send --type=method_call --system ' 128 '--dest=org.chromium.PowerManager /org/chromium/PowerManager ' 129 'org.chromium.PowerManager.RequestRestart'), 130 timeout=_COMMAND_TIMEOUT) 131 132 133 def wait_for_system_to_cycle(self, down_sec, up_sec): 134 """ 135 Wait for the system to stop and then start responding to pings. 136 137 @param down_sec: Maximum delay for the system to go down. 138 @param up_sec: Maximum delay for the system to come back up. 139 140 @return: Floating-point time when system went down. 141 """ 142 logging.info("Waiting for host to go down...") 143 if not self.host.ping_wait_down(timeout=down_sec): 144 raise error.TestError( 145 'System hasn\'t gone down after %d seconds' % (down_sec)) 146 down_timestamp = time.time() 147 logging.info("System went down at %.2f", down_timestamp) 148 149 logging.info("Waiting for host to come back up...") 150 if not self.host.ping_wait_up(timeout=up_sec) or \ 151 not self.host.wait_up(timeout=up_sec): 152 raise error.TestError('System didn\'t come back up') 153 154 return down_timestamp 155 156 157 def run_once(self): 158 # Start flashrom and then request that the system be suspended. The 159 # suspend should be deferred until flashrom finishes writing but should 160 # happen eventually. 161 flashrom_time = time.time() 162 self.start_fake_flashrom_write(_SUSPEND_FLASHROM_SEC) 163 self.send_suspend_request(_SUSPEND_DOWN_SEC) 164 delay_sec = self.wait_for_system_to_cycle( 165 _SUSPEND_DOWN_SEC, _SUSPEND_UP_SEC) - flashrom_time 166 167 # Check that powerd waited for flashrom to finish. 168 if delay_sec < _SUSPEND_FLASHROM_SEC: 169 raise error.TestError( 170 ('Suspend was blocked for %.2f sec; expected it to be blocked ' 171 'for at least %d sec') % (delay_sec, _SUSPEND_FLASHROM_SEC)) 172 173 # Now do the same thing, but with a reboot request. 174 flashrom_time = time.time() 175 self.start_fake_flashrom_write(_REBOOT_FLASHROM_SEC) 176 self.send_reboot_request() 177 delay_sec = self.wait_for_system_to_cycle( 178 _REBOOT_DOWN_SEC, _REBOOT_UP_SEC) - flashrom_time 179 if delay_sec < _REBOOT_FLASHROM_SEC: 180 raise error.TestError( 181 ('Reboot was blocked for %.2f sec; expected it to be blocked ' 182 'for at least %d sec') % (delay_sec, _REBOOT_FLASHROM_SEC)) 183