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