176b46939SStephen Warren# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. 276b46939SStephen Warren# 376b46939SStephen Warren# SPDX-License-Identifier: GPL-2.0 476b46939SStephen Warren 576b46939SStephen Warren# Utility code shared across multiple tests. 676b46939SStephen Warren 776b46939SStephen Warrenimport hashlib 876b46939SStephen Warrenimport os 976b46939SStephen Warrenimport os.path 10b8218a91SHeiko Schocherimport pytest 1176b46939SStephen Warrenimport sys 1276b46939SStephen Warrenimport time 137e6621a1SHeiko Schocherimport pytest 1476b46939SStephen Warren 1576b46939SStephen Warrendef md5sum_data(data): 16e8debf39SStephen Warren """Calculate the MD5 hash of some data. 1776b46939SStephen Warren 1876b46939SStephen Warren Args: 1976b46939SStephen Warren data: The data to hash. 2076b46939SStephen Warren 2176b46939SStephen Warren Returns: 2276b46939SStephen Warren The hash of the data, as a binary string. 23e8debf39SStephen Warren """ 2476b46939SStephen Warren 2576b46939SStephen Warren h = hashlib.md5() 2676b46939SStephen Warren h.update(data) 2776b46939SStephen Warren return h.digest() 2876b46939SStephen Warren 2976b46939SStephen Warrendef md5sum_file(fn, max_length=None): 30e8debf39SStephen Warren """Calculate the MD5 hash of the contents of a file. 3176b46939SStephen Warren 3276b46939SStephen Warren Args: 3376b46939SStephen Warren fn: The filename of the file to hash. 3476b46939SStephen Warren max_length: The number of bytes to hash. If the file has more 3576b46939SStephen Warren bytes than this, they will be ignored. If None or omitted, the 3676b46939SStephen Warren entire file will be hashed. 3776b46939SStephen Warren 3876b46939SStephen Warren Returns: 3976b46939SStephen Warren The hash of the file content, as a binary string. 40e8debf39SStephen Warren """ 4176b46939SStephen Warren 4276b46939SStephen Warren with open(fn, 'rb') as fh: 4376b46939SStephen Warren if max_length: 4476b46939SStephen Warren params = [max_length] 4576b46939SStephen Warren else: 4676b46939SStephen Warren params = [] 4776b46939SStephen Warren data = fh.read(*params) 4876b46939SStephen Warren return md5sum_data(data) 4976b46939SStephen Warren 5076b46939SStephen Warrenclass PersistentRandomFile(object): 51e8debf39SStephen Warren """Generate and store information about a persistent file containing 52e8debf39SStephen Warren random data.""" 5376b46939SStephen Warren 5476b46939SStephen Warren def __init__(self, u_boot_console, fn, size): 55e8debf39SStephen Warren """Create or process the persistent file. 5676b46939SStephen Warren 5776b46939SStephen Warren If the file does not exist, it is generated. 5876b46939SStephen Warren 5976b46939SStephen Warren If the file does exist, its content is hashed for later comparison. 6076b46939SStephen Warren 6176b46939SStephen Warren These files are always located in the "persistent data directory" of 6276b46939SStephen Warren the current test run. 6376b46939SStephen Warren 6476b46939SStephen Warren Args: 6576b46939SStephen Warren u_boot_console: A console connection to U-Boot. 6676b46939SStephen Warren fn: The filename (without path) to create. 6776b46939SStephen Warren size: The desired size of the file in bytes. 6876b46939SStephen Warren 6976b46939SStephen Warren Returns: 7076b46939SStephen Warren Nothing. 71e8debf39SStephen Warren """ 7276b46939SStephen Warren 7376b46939SStephen Warren self.fn = fn 7476b46939SStephen Warren 7576b46939SStephen Warren self.abs_fn = u_boot_console.config.persistent_data_dir + '/' + fn 7676b46939SStephen Warren 7776b46939SStephen Warren if os.path.exists(self.abs_fn): 7876b46939SStephen Warren u_boot_console.log.action('Persistent data file ' + self.abs_fn + 7976b46939SStephen Warren ' already exists') 8076b46939SStephen Warren self.content_hash = md5sum_file(self.abs_fn) 8176b46939SStephen Warren else: 8276b46939SStephen Warren u_boot_console.log.action('Generating ' + self.abs_fn + 8376b46939SStephen Warren ' (random, persistent, %d bytes)' % size) 8476b46939SStephen Warren data = os.urandom(size) 8576b46939SStephen Warren with open(self.abs_fn, 'wb') as fh: 8676b46939SStephen Warren fh.write(data) 8776b46939SStephen Warren self.content_hash = md5sum_data(data) 8876b46939SStephen Warren 8976b46939SStephen Warrendef attempt_to_open_file(fn): 90e8debf39SStephen Warren """Attempt to open a file, without throwing exceptions. 9176b46939SStephen Warren 9276b46939SStephen Warren Any errors (exceptions) that occur during the attempt to open the file 9376b46939SStephen Warren are ignored. This is useful in order to test whether a file (in 9476b46939SStephen Warren particular, a device node) exists and can be successfully opened, in order 9576b46939SStephen Warren to poll for e.g. USB enumeration completion. 9676b46939SStephen Warren 9776b46939SStephen Warren Args: 9876b46939SStephen Warren fn: The filename to attempt to open. 9976b46939SStephen Warren 10076b46939SStephen Warren Returns: 10176b46939SStephen Warren An open file handle to the file, or None if the file could not be 10276b46939SStephen Warren opened. 103e8debf39SStephen Warren """ 10476b46939SStephen Warren 10576b46939SStephen Warren try: 10676b46939SStephen Warren return open(fn, 'rb') 10776b46939SStephen Warren except: 10876b46939SStephen Warren return None 10976b46939SStephen Warren 11076b46939SStephen Warrendef wait_until_open_succeeds(fn): 111e8debf39SStephen Warren """Poll until a file can be opened, or a timeout occurs. 11276b46939SStephen Warren 11376b46939SStephen Warren Continually attempt to open a file, and return when this succeeds, or 11476b46939SStephen Warren raise an exception after a timeout. 11576b46939SStephen Warren 11676b46939SStephen Warren Args: 11776b46939SStephen Warren fn: The filename to attempt to open. 11876b46939SStephen Warren 11976b46939SStephen Warren Returns: 12076b46939SStephen Warren An open file handle to the file. 121e8debf39SStephen Warren """ 12276b46939SStephen Warren 12376b46939SStephen Warren for i in xrange(100): 12476b46939SStephen Warren fh = attempt_to_open_file(fn) 12576b46939SStephen Warren if fh: 12676b46939SStephen Warren return fh 12776b46939SStephen Warren time.sleep(0.1) 12876b46939SStephen Warren raise Exception('File could not be opened') 12976b46939SStephen Warren 13076b46939SStephen Warrendef wait_until_file_open_fails(fn, ignore_errors): 131e8debf39SStephen Warren """Poll until a file cannot be opened, or a timeout occurs. 13276b46939SStephen Warren 13376b46939SStephen Warren Continually attempt to open a file, and return when this fails, or 13476b46939SStephen Warren raise an exception after a timeout. 13576b46939SStephen Warren 13676b46939SStephen Warren Args: 13776b46939SStephen Warren fn: The filename to attempt to open. 13876b46939SStephen Warren ignore_errors: Indicate whether to ignore timeout errors. If True, the 13976b46939SStephen Warren function will simply return if a timeout occurs, otherwise an 14076b46939SStephen Warren exception will be raised. 14176b46939SStephen Warren 14276b46939SStephen Warren Returns: 14376b46939SStephen Warren Nothing. 144e8debf39SStephen Warren """ 14576b46939SStephen Warren 14676b46939SStephen Warren for i in xrange(100): 14776b46939SStephen Warren fh = attempt_to_open_file(fn) 14876b46939SStephen Warren if not fh: 14976b46939SStephen Warren return 15076b46939SStephen Warren fh.close() 15176b46939SStephen Warren time.sleep(0.1) 15276b46939SStephen Warren if ignore_errors: 15376b46939SStephen Warren return 15476b46939SStephen Warren raise Exception('File can still be opened') 15576b46939SStephen Warren 15676b46939SStephen Warrendef run_and_log(u_boot_console, cmd, ignore_errors=False): 157e8debf39SStephen Warren """Run a command and log its output. 15876b46939SStephen Warren 15976b46939SStephen Warren Args: 16076b46939SStephen Warren u_boot_console: A console connection to U-Boot. 16176b46939SStephen Warren cmd: The command to run, as an array of argv[]. 16276b46939SStephen Warren ignore_errors: Indicate whether to ignore errors. If True, the function 16376b46939SStephen Warren will simply return if the command cannot be executed or exits with 16476b46939SStephen Warren an error code, otherwise an exception will be raised if such 16576b46939SStephen Warren problems occur. 16676b46939SStephen Warren 16776b46939SStephen Warren Returns: 168f3d3e95cSSimon Glass The output as a string. 169e8debf39SStephen Warren """ 17076b46939SStephen Warren 17176b46939SStephen Warren runner = u_boot_console.log.get_runner(cmd[0], sys.stdout) 172f3d3e95cSSimon Glass output = runner.run(cmd, ignore_errors=ignore_errors) 17376b46939SStephen Warren runner.close() 174f3d3e95cSSimon Glass return output 17505266103SStephen Warren 1768b304a37SSimon Glassdef cmd(u_boot_console, cmd_str): 1778b304a37SSimon Glass """Run a single command string and log its output. 1788b304a37SSimon Glass 1798b304a37SSimon Glass Args: 1808b304a37SSimon Glass u_boot_console: A console connection to U-Boot. 1818b304a37SSimon Glass cmd: The command to run, as a string. 1828b304a37SSimon Glass 1838b304a37SSimon Glass Returns: 1848b304a37SSimon Glass The output as a string. 1858b304a37SSimon Glass """ 1868b304a37SSimon Glass return run_and_log(u_boot_console, cmd_str.split()) 1878b304a37SSimon Glass 1889e17b034SSimon Glassdef run_and_log_expect_exception(u_boot_console, cmd, retcode, msg): 189*72f52268SSimon Glass """Run a command that is expected to fail. 1909e17b034SSimon Glass 1919e17b034SSimon Glass This runs a command and checks that it fails with the expected return code 1929e17b034SSimon Glass and exception method. If not, an exception is raised. 1939e17b034SSimon Glass 1949e17b034SSimon Glass Args: 1959e17b034SSimon Glass u_boot_console: A console connection to U-Boot. 1969e17b034SSimon Glass cmd: The command to run, as an array of argv[]. 1979e17b034SSimon Glass retcode: Expected non-zero return code from the command. 198*72f52268SSimon Glass msg: String that should be contained within the command's output. 1999e17b034SSimon Glass """ 2009e17b034SSimon Glass try: 2019e17b034SSimon Glass runner = u_boot_console.log.get_runner(cmd[0], sys.stdout) 2029e17b034SSimon Glass runner.run(cmd) 2039e17b034SSimon Glass except Exception as e: 2047f64b187SSimon Glass assert(retcode == runner.exit_status) 2059e17b034SSimon Glass assert(msg in runner.output) 2069e17b034SSimon Glass else: 2077f64b187SSimon Glass raise Exception("Expected an exception with retcode %d message '%s'," 2087f64b187SSimon Glass "but it was not raised" % (retcode, msg)) 2099e17b034SSimon Glass finally: 2109e17b034SSimon Glass runner.close() 2119e17b034SSimon Glass 21205266103SStephen Warrenram_base = None 21305266103SStephen Warrendef find_ram_base(u_boot_console): 214e8debf39SStephen Warren """Find the running U-Boot's RAM location. 21505266103SStephen Warren 21605266103SStephen Warren Probe the running U-Boot to determine the address of the first bank 21705266103SStephen Warren of RAM. This is useful for tests that test reading/writing RAM, or 21805266103SStephen Warren load/save files that aren't associated with some standard address 21905266103SStephen Warren typically represented in an environment variable such as 22005266103SStephen Warren ${kernel_addr_r}. The value is cached so that it only needs to be 22105266103SStephen Warren actively read once. 22205266103SStephen Warren 22305266103SStephen Warren Args: 22405266103SStephen Warren u_boot_console: A console connection to U-Boot. 22505266103SStephen Warren 22605266103SStephen Warren Returns: 22705266103SStephen Warren The address of U-Boot's first RAM bank, as an integer. 228e8debf39SStephen Warren """ 22905266103SStephen Warren 23005266103SStephen Warren global ram_base 23105266103SStephen Warren if u_boot_console.config.buildconfig.get('config_cmd_bdi', 'n') != 'y': 23205266103SStephen Warren pytest.skip('bdinfo command not supported') 23305266103SStephen Warren if ram_base == -1: 23405266103SStephen Warren pytest.skip('Previously failed to find RAM bank start') 23505266103SStephen Warren if ram_base is not None: 23605266103SStephen Warren return ram_base 23705266103SStephen Warren 23805266103SStephen Warren with u_boot_console.log.section('find_ram_base'): 23905266103SStephen Warren response = u_boot_console.run_command('bdinfo') 24005266103SStephen Warren for l in response.split('\n'): 241d56dd0b1SDaniel Schwierzeck if '-> start' in l or 'memstart =' in l: 24205266103SStephen Warren ram_base = int(l.split('=')[1].strip(), 16) 24305266103SStephen Warren break 24405266103SStephen Warren if ram_base is None: 24505266103SStephen Warren ram_base = -1 24605266103SStephen Warren raise Exception('Failed to find RAM bank start in `bdinfo`') 24705266103SStephen Warren 24805266103SStephen Warren return ram_base 249