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 1076b46939SStephen Warrenimport sys 1176b46939SStephen Warrenimport time 1276b46939SStephen Warren 1376b46939SStephen Warrendef md5sum_data(data): 14*e8debf39SStephen Warren """Calculate the MD5 hash of some data. 1576b46939SStephen Warren 1676b46939SStephen Warren Args: 1776b46939SStephen Warren data: The data to hash. 1876b46939SStephen Warren 1976b46939SStephen Warren Returns: 2076b46939SStephen Warren The hash of the data, as a binary string. 21*e8debf39SStephen Warren """ 2276b46939SStephen Warren 2376b46939SStephen Warren h = hashlib.md5() 2476b46939SStephen Warren h.update(data) 2576b46939SStephen Warren return h.digest() 2676b46939SStephen Warren 2776b46939SStephen Warrendef md5sum_file(fn, max_length=None): 28*e8debf39SStephen Warren """Calculate the MD5 hash of the contents of a file. 2976b46939SStephen Warren 3076b46939SStephen Warren Args: 3176b46939SStephen Warren fn: The filename of the file to hash. 3276b46939SStephen Warren max_length: The number of bytes to hash. If the file has more 3376b46939SStephen Warren bytes than this, they will be ignored. If None or omitted, the 3476b46939SStephen Warren entire file will be hashed. 3576b46939SStephen Warren 3676b46939SStephen Warren Returns: 3776b46939SStephen Warren The hash of the file content, as a binary string. 38*e8debf39SStephen Warren """ 3976b46939SStephen Warren 4076b46939SStephen Warren with open(fn, 'rb') as fh: 4176b46939SStephen Warren if max_length: 4276b46939SStephen Warren params = [max_length] 4376b46939SStephen Warren else: 4476b46939SStephen Warren params = [] 4576b46939SStephen Warren data = fh.read(*params) 4676b46939SStephen Warren return md5sum_data(data) 4776b46939SStephen Warren 4876b46939SStephen Warrenclass PersistentRandomFile(object): 49*e8debf39SStephen Warren """Generate and store information about a persistent file containing 50*e8debf39SStephen Warren random data.""" 5176b46939SStephen Warren 5276b46939SStephen Warren def __init__(self, u_boot_console, fn, size): 53*e8debf39SStephen Warren """Create or process the persistent file. 5476b46939SStephen Warren 5576b46939SStephen Warren If the file does not exist, it is generated. 5676b46939SStephen Warren 5776b46939SStephen Warren If the file does exist, its content is hashed for later comparison. 5876b46939SStephen Warren 5976b46939SStephen Warren These files are always located in the "persistent data directory" of 6076b46939SStephen Warren the current test run. 6176b46939SStephen Warren 6276b46939SStephen Warren Args: 6376b46939SStephen Warren u_boot_console: A console connection to U-Boot. 6476b46939SStephen Warren fn: The filename (without path) to create. 6576b46939SStephen Warren size: The desired size of the file in bytes. 6676b46939SStephen Warren 6776b46939SStephen Warren Returns: 6876b46939SStephen Warren Nothing. 69*e8debf39SStephen Warren """ 7076b46939SStephen Warren 7176b46939SStephen Warren self.fn = fn 7276b46939SStephen Warren 7376b46939SStephen Warren self.abs_fn = u_boot_console.config.persistent_data_dir + '/' + fn 7476b46939SStephen Warren 7576b46939SStephen Warren if os.path.exists(self.abs_fn): 7676b46939SStephen Warren u_boot_console.log.action('Persistent data file ' + self.abs_fn + 7776b46939SStephen Warren ' already exists') 7876b46939SStephen Warren self.content_hash = md5sum_file(self.abs_fn) 7976b46939SStephen Warren else: 8076b46939SStephen Warren u_boot_console.log.action('Generating ' + self.abs_fn + 8176b46939SStephen Warren ' (random, persistent, %d bytes)' % size) 8276b46939SStephen Warren data = os.urandom(size) 8376b46939SStephen Warren with open(self.abs_fn, 'wb') as fh: 8476b46939SStephen Warren fh.write(data) 8576b46939SStephen Warren self.content_hash = md5sum_data(data) 8676b46939SStephen Warren 8776b46939SStephen Warrendef attempt_to_open_file(fn): 88*e8debf39SStephen Warren """Attempt to open a file, without throwing exceptions. 8976b46939SStephen Warren 9076b46939SStephen Warren Any errors (exceptions) that occur during the attempt to open the file 9176b46939SStephen Warren are ignored. This is useful in order to test whether a file (in 9276b46939SStephen Warren particular, a device node) exists and can be successfully opened, in order 9376b46939SStephen Warren to poll for e.g. USB enumeration completion. 9476b46939SStephen Warren 9576b46939SStephen Warren Args: 9676b46939SStephen Warren fn: The filename to attempt to open. 9776b46939SStephen Warren 9876b46939SStephen Warren Returns: 9976b46939SStephen Warren An open file handle to the file, or None if the file could not be 10076b46939SStephen Warren opened. 101*e8debf39SStephen Warren """ 10276b46939SStephen Warren 10376b46939SStephen Warren try: 10476b46939SStephen Warren return open(fn, 'rb') 10576b46939SStephen Warren except: 10676b46939SStephen Warren return None 10776b46939SStephen Warren 10876b46939SStephen Warrendef wait_until_open_succeeds(fn): 109*e8debf39SStephen Warren """Poll until a file can be opened, or a timeout occurs. 11076b46939SStephen Warren 11176b46939SStephen Warren Continually attempt to open a file, and return when this succeeds, or 11276b46939SStephen Warren raise an exception after a timeout. 11376b46939SStephen Warren 11476b46939SStephen Warren Args: 11576b46939SStephen Warren fn: The filename to attempt to open. 11676b46939SStephen Warren 11776b46939SStephen Warren Returns: 11876b46939SStephen Warren An open file handle to the file. 119*e8debf39SStephen Warren """ 12076b46939SStephen Warren 12176b46939SStephen Warren for i in xrange(100): 12276b46939SStephen Warren fh = attempt_to_open_file(fn) 12376b46939SStephen Warren if fh: 12476b46939SStephen Warren return fh 12576b46939SStephen Warren time.sleep(0.1) 12676b46939SStephen Warren raise Exception('File could not be opened') 12776b46939SStephen Warren 12876b46939SStephen Warrendef wait_until_file_open_fails(fn, ignore_errors): 129*e8debf39SStephen Warren """Poll until a file cannot be opened, or a timeout occurs. 13076b46939SStephen Warren 13176b46939SStephen Warren Continually attempt to open a file, and return when this fails, or 13276b46939SStephen Warren raise an exception after a timeout. 13376b46939SStephen Warren 13476b46939SStephen Warren Args: 13576b46939SStephen Warren fn: The filename to attempt to open. 13676b46939SStephen Warren ignore_errors: Indicate whether to ignore timeout errors. If True, the 13776b46939SStephen Warren function will simply return if a timeout occurs, otherwise an 13876b46939SStephen Warren exception will be raised. 13976b46939SStephen Warren 14076b46939SStephen Warren Returns: 14176b46939SStephen Warren Nothing. 142*e8debf39SStephen Warren """ 14376b46939SStephen Warren 14476b46939SStephen Warren for i in xrange(100): 14576b46939SStephen Warren fh = attempt_to_open_file(fn) 14676b46939SStephen Warren if not fh: 14776b46939SStephen Warren return 14876b46939SStephen Warren fh.close() 14976b46939SStephen Warren time.sleep(0.1) 15076b46939SStephen Warren if ignore_errors: 15176b46939SStephen Warren return 15276b46939SStephen Warren raise Exception('File can still be opened') 15376b46939SStephen Warren 15476b46939SStephen Warrendef run_and_log(u_boot_console, cmd, ignore_errors=False): 155*e8debf39SStephen Warren """Run a command and log its output. 15676b46939SStephen Warren 15776b46939SStephen Warren Args: 15876b46939SStephen Warren u_boot_console: A console connection to U-Boot. 15976b46939SStephen Warren cmd: The command to run, as an array of argv[]. 16076b46939SStephen Warren ignore_errors: Indicate whether to ignore errors. If True, the function 16176b46939SStephen Warren will simply return if the command cannot be executed or exits with 16276b46939SStephen Warren an error code, otherwise an exception will be raised if such 16376b46939SStephen Warren problems occur. 16476b46939SStephen Warren 16576b46939SStephen Warren Returns: 16676b46939SStephen Warren Nothing. 167*e8debf39SStephen Warren """ 16876b46939SStephen Warren 16976b46939SStephen Warren runner = u_boot_console.log.get_runner(cmd[0], sys.stdout) 17076b46939SStephen Warren runner.run(cmd, ignore_errors=ignore_errors) 17176b46939SStephen Warren runner.close() 17205266103SStephen Warren 17305266103SStephen Warrenram_base = None 17405266103SStephen Warrendef find_ram_base(u_boot_console): 175*e8debf39SStephen Warren """Find the running U-Boot's RAM location. 17605266103SStephen Warren 17705266103SStephen Warren Probe the running U-Boot to determine the address of the first bank 17805266103SStephen Warren of RAM. This is useful for tests that test reading/writing RAM, or 17905266103SStephen Warren load/save files that aren't associated with some standard address 18005266103SStephen Warren typically represented in an environment variable such as 18105266103SStephen Warren ${kernel_addr_r}. The value is cached so that it only needs to be 18205266103SStephen Warren actively read once. 18305266103SStephen Warren 18405266103SStephen Warren Args: 18505266103SStephen Warren u_boot_console: A console connection to U-Boot. 18605266103SStephen Warren 18705266103SStephen Warren Returns: 18805266103SStephen Warren The address of U-Boot's first RAM bank, as an integer. 189*e8debf39SStephen Warren """ 19005266103SStephen Warren 19105266103SStephen Warren global ram_base 19205266103SStephen Warren if u_boot_console.config.buildconfig.get('config_cmd_bdi', 'n') != 'y': 19305266103SStephen Warren pytest.skip('bdinfo command not supported') 19405266103SStephen Warren if ram_base == -1: 19505266103SStephen Warren pytest.skip('Previously failed to find RAM bank start') 19605266103SStephen Warren if ram_base is not None: 19705266103SStephen Warren return ram_base 19805266103SStephen Warren 19905266103SStephen Warren with u_boot_console.log.section('find_ram_base'): 20005266103SStephen Warren response = u_boot_console.run_command('bdinfo') 20105266103SStephen Warren for l in response.split('\n'): 20205266103SStephen Warren if '-> start' in l: 20305266103SStephen Warren ram_base = int(l.split('=')[1].strip(), 16) 20405266103SStephen Warren break 20505266103SStephen Warren if ram_base is None: 20605266103SStephen Warren ram_base = -1 20705266103SStephen Warren raise Exception('Failed to find RAM bank start in `bdinfo`') 20805266103SStephen Warren 20905266103SStephen Warren return ram_base 210