xref: /rk3399_rockchip-uboot/test/py/u_boot_utils.py (revision e8debf394fbba594fcfc267c61f8c6bbca395b06)
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