diff options
-rw-r--r-- | test/py/requirements.txt | 4 | ||||
-rw-r--r-- | test/py/tests/test_gpio.py | 90 | ||||
-rw-r--r-- | test/py/tests/test_mmc.py | 671 | ||||
-rw-r--r-- | test/py/tests/test_nand.py | 201 | ||||
-rw-r--r-- | test/py/tests/test_scsi.py | 92 | ||||
-rw-r--r-- | test/py/tests/test_spi.py | 626 | ||||
-rw-r--r-- | test/py/tests/test_usb.py | 626 | ||||
-rw-r--r-- | test/py/tests/test_zynq_secure.py | 190 | ||||
-rw-r--r-- | test/py/tests/test_zynqmp_rpu.py | 208 | ||||
-rw-r--r-- | test/py/tests/test_zynqmp_secure.py | 104 |
10 files changed, 2810 insertions, 2 deletions
diff --git a/test/py/requirements.txt b/test/py/requirements.txt index c3cc916..0f67c3c 100644 --- a/test/py/requirements.txt +++ b/test/py/requirements.txt @@ -11,10 +11,10 @@ more-itertools==7.2.0 packaging==23.2 pbr==5.4.3 pluggy==0.13.0 -py==1.10.0 +py==1.11.0 pycryptodomex==3.19.1 pyelftools==0.27 -pygit2==1.9.2 +pygit2==1.13.3 pyparsing==3.0.7 pytest==6.2.5 pytest-xdist==2.5.0 diff --git a/test/py/tests/test_gpio.py b/test/py/tests/test_gpio.py index 0af186f..3e16e63 100644 --- a/test/py/tests/test_gpio.py +++ b/test/py/tests/test_gpio.py @@ -85,6 +85,13 @@ env__gpio_dev_config = { 'gpio_ip_pin_clear':'66', 'gpio_clear_value': 'value is 0', 'gpio_set_value': 'value is 1', + # GPIO pin list to test gpio functionality for each pins, pin should be + # pin names (str) + 'gpio_pin_list': ['gpio@1000031', 'gpio@1000032', 'gpio@20000033'], + # GPIO input output list for shorted gpio pins to test gpio + # functionality for each of pairs, where the first element is + # configured as input and second as output + 'gpio_ip_op_list': [['gpio0', 'gpio1'], ['gpio2', 'gpio3']], } """ @@ -223,3 +230,86 @@ def test_gpio_input_generic(u_boot_console): response = u_boot_console.run_command(cmd) good_response = gpio_set_value assert good_response in response + +@pytest.mark.buildconfigspec('cmd_gpio') +def test_gpio_pins_generic(u_boot_console): + """Test various gpio related functionality, such as the input, set, clear, + and toggle for the set of gpio pin list. + + Specific set of gpio pins (by mentioning gpio pin name) configured as + input (mentioned as 'gpio_pin_list') to be tested for multiple gpio + commands. + """ + + f = u_boot_console.config.env.get('env__gpio_dev_config', False) + if not f: + pytest.skip('gpio not configured') + + gpio_pins = f.get('gpio_pin_list', None) + if not gpio_pins: + pytest.skip('gpio pin list are not configured') + + for gpin in gpio_pins: + # gpio input + u_boot_console.run_command(f'gpio input {gpin}') + expected_response = f'{gpin}: input:' + response = u_boot_console.run_command(f'gpio status -a {gpin}') + assert expected_response in response + + # gpio set + u_boot_console.run_command(f'gpio set {gpin}') + expected_response = f'{gpin}: output: 1' + response = u_boot_console.run_command(f'gpio status -a {gpin}') + assert expected_response in response + + # gpio clear + u_boot_console.run_command(f'gpio clear {gpin}') + expected_response = f'{gpin}: output: 0' + response = u_boot_console.run_command(f'gpio status -a {gpin}') + assert expected_response in response + + # gpio toggle + u_boot_console.run_command(f'gpio toggle {gpin}') + expected_response = f'{gpin}: output: 1' + response = u_boot_console.run_command(f'gpio status -a {gpin}') + assert expected_response in response + +@pytest.mark.buildconfigspec('cmd_gpio') +def test_gpio_pins_input_output_generic(u_boot_console): + """Test gpio related functionality such as input and output for the list of + shorted gpio pins provided as a pair of input and output pins. This test + will fail, if the gpio pins are not shorted properly. + + Specific set of shorted gpio pins (by mentioning gpio pin name) + configured as input and output (mentioned as 'gpio_ip_op_list') as a + pair to be tested for gpio input output case. + """ + + f = u_boot_console.config.env.get('env__gpio_dev_config', False) + if not f: + pytest.skip('gpio not configured') + + gpio_pins = f.get('gpio_ip_op_list', None) + if not gpio_pins: + pytest.skip('gpio pin list for input and output are not configured') + + for gpins in gpio_pins: + u_boot_console.run_command(f'gpio input {gpins[0]}') + expected_response = f'{gpins[0]}: input:' + response = u_boot_console.run_command(f'gpio status -a {gpins[0]}') + assert expected_response in response + + u_boot_console.run_command(f'gpio set {gpins[1]}') + expected_response = f'{gpins[1]}: output:' + response = u_boot_console.run_command(f'gpio status -a {gpins[1]}') + assert expected_response in response + + u_boot_console.run_command(f'gpio clear {gpins[1]}') + expected_response = f'{gpins[0]}: input: 0' + response = u_boot_console.run_command(f'gpio status -a {gpins[0]}') + assert expected_response in response + + u_boot_console.run_command(f'gpio set {gpins[1]}') + expected_response = f'{gpins[0]}: input: 1' + response = u_boot_console.run_command(f'gpio status -a {gpins[0]}') + assert expected_response in response diff --git a/test/py/tests/test_mmc.py b/test/py/tests/test_mmc.py new file mode 100644 index 0000000..a96c4e8 --- /dev/null +++ b/test/py/tests/test_mmc.py @@ -0,0 +1,671 @@ +# SPDX-License-Identifier: GPL-2.0 +# (C) Copyright 2023, Advanced Micro Devices, Inc. + +import pytest +import random +import re +import u_boot_utils + +""" +Note: This test doesn't rely on boardenv_* configuration values but it can +change the test behavior. To test MMC file system cases (fat32, ext2, ext4), +MMC device should be formatted and valid partitions should be created for +different file system, otherwise it may leads to failure. This test will be +skipped if the MMC device is not detected. + +For example: + +# Setup env__mmc_device_test_skip to not skipping the test. By default, its +# value is set to True. Set it to False to run all tests for MMC device. +env__mmc_device_test_skip = False +""" + +mmc_set_up = False +controllers = 0 +devices = {} + +def setup_mmc(u_boot_console): + if u_boot_console.config.env.get('env__mmc_device_test_skip', True): + pytest.skip('MMC device test is not enabled') + +@pytest.mark.buildconfigspec('cmd_mmc') +def test_mmc_list(u_boot_console): + setup_mmc(u_boot_console) + output = u_boot_console.run_command('mmc list') + if 'No MMC device available' in output: + pytest.skip('No SD/MMC/eMMC controller available') + + if 'Card did not respond to voltage select' in output: + pytest.skip('No SD/MMC card present') + + array = output.split() + global devices + global controllers + controllers = int(len(array) / 2) + for x in range(0, controllers): + y = x * 2 + devices[x] = {} + devices[x]['name'] = array[y] + + global mmc_set_up + mmc_set_up = True + +@pytest.mark.buildconfigspec('cmd_mmc') +def test_mmc_dev(u_boot_console): + if not mmc_set_up: + pytest.skip('No SD/MMC/eMMC controller available') + + fail = 0 + for x in range(0, controllers): + devices[x]['detected'] = 'yes' + output = u_boot_console.run_command('mmc dev %d' % x) + + # Some sort of switch here + if 'Card did not respond to voltage select' in output: + fail = 1 + devices[x]['detected'] = 'no' + + if 'no mmc device at slot' in output: + devices[x]['detected'] = 'no' + + if 'MMC: no card present' in output: + devices[x]['detected'] = 'no' + + if fail: + pytest.fail('Card not present') + +@pytest.mark.buildconfigspec('cmd_mmc') +def test_mmcinfo(u_boot_console): + if not mmc_set_up: + pytest.skip('No SD/MMC/eMMC controller available') + + for x in range(0, controllers): + if devices[x]['detected'] == 'yes': + u_boot_console.run_command('mmc dev %d' % x) + output = u_boot_console.run_command('mmcinfo') + if 'busy timeout' in output: + pytest.skip('No SD/MMC/eMMC device present') + + obj = re.search(r'Capacity: (\d+|\d+[\.]?\d)', output) + try: + capacity = float(obj.groups()[0]) + print(capacity) + devices[x]['capacity'] = capacity + print('Capacity of dev %d is: %g GiB' % (x, capacity)) + except ValueError: + pytest.fail('MMC capacity not recognized') + +@pytest.mark.buildconfigspec('cmd_mmc') +def test_mmc_info(u_boot_console): + if not mmc_set_up: + pytest.skip('No SD/MMC/eMMC controller available') + + for x in range(0, controllers): + if devices[x]['detected'] == 'yes': + u_boot_console.run_command('mmc dev %d' % x) + + output = u_boot_console.run_command('mmc info') + + obj = re.search(r'Capacity: (\d+|\d+[\.]?\d)', output) + try: + capacity = float(obj.groups()[0]) + print(capacity) + if devices[x]['capacity'] != capacity: + pytest.fail("MMC capacity doesn't match mmcinfo") + + except ValueError: + pytest.fail('MMC capacity not recognized') + +@pytest.mark.buildconfigspec('cmd_mmc') +def test_mmc_rescan(u_boot_console): + if not mmc_set_up: + pytest.skip('No SD/MMC/eMMC controller available') + + if not devices: + pytest.skip('No devices detected') + + for x in range(0, controllers): + if devices[x]['detected'] == 'yes': + u_boot_console.run_command('mmc dev %d' % x) + output = u_boot_console.run_command('mmc rescan') + if output: + pytest.fail('mmc rescan has something to check') + output = u_boot_console.run_command('echo $?') + assert output.endswith('0') + +@pytest.mark.buildconfigspec('cmd_mmc') +def test_mmc_part(u_boot_console): + if not mmc_set_up: + pytest.skip('No SD/MMC/eMMC controller available') + + if not devices: + pytest.skip('No devices detected') + + for x in range(0, controllers): + if devices[x]['detected'] == 'yes': + u_boot_console.run_command('mmc dev %d' % x) + output = u_boot_console.run_command('mmc part') + + lines = output.split('\n') + part_fat = [] + part_ext = [] + for line in lines: + obj = re.search( + r'(\d)\s+\d+\s+\d+\s+\w+\d+\w+-\d+\s+(\d+\w+)', line) + if obj: + part_id = int(obj.groups()[0]) + part_type = obj.groups()[1] + print('part_id:%d, part_type:%s' % (part_id, part_type)) + + if part_type in ['0c', '0b', '0e']: + print('Fat detected') + part_fat.append(part_id) + elif part_type == '83': + print('ext detected') + part_ext.append(part_id) + else: + pytest.fail('Unsupported Filesystem on device %d' % x) + devices[x]['ext4'] = part_ext + devices[x]['ext2'] = part_ext + devices[x]['fat'] = part_fat + + if not part_ext and not part_fat: + pytest.fail('No partition detected on device %d' % x) + +@pytest.mark.buildconfigspec('cmd_mmc') +@pytest.mark.buildconfigspec('cmd_fat') +def test_mmc_fatls_fatinfo(u_boot_console): + if not mmc_set_up: + pytest.skip('No SD/MMC/eMMC controller available') + + if not devices: + pytest.skip('No devices detected') + + part_detect = 0 + fs = 'fat' + for x in range(0, controllers): + if devices[x]['detected'] == 'yes': + u_boot_console.run_command('mmc dev %d' % x) + try: + partitions = devices[x][fs] + except: + print('No %s table on this device' % fs.upper()) + continue + + for part in partitions: + output = u_boot_console.run_command( + 'fatls mmc %d:%s' % (x, part)) + if 'Unrecognized filesystem type' in output: + partitions.remove(part) + pytest.fail('Unrecognized filesystem') + + if not re.search(r'\d file\(s\), \d dir\(s\)', output): + pytest.fail('%s read failed on device %d' % (fs.upper, x)) + output = u_boot_console.run_command( + 'fatinfo mmc %d:%s' % (x, part)) + string = 'Filesystem: %s' % fs.upper + if re.search(string, output): + pytest.fail('%s FS failed on device %d' % (fs.upper(), x)) + part_detect = 1 + + if not part_detect: + pytest.skip('No %s partition detected' % fs.upper()) + + +@pytest.mark.buildconfigspec('cmd_mmc') +@pytest.mark.buildconfigspec('cmd_fat') +@pytest.mark.buildconfigspec('cmd_memory') +def test_mmc_fatload_fatwrite(u_boot_console): + if not mmc_set_up: + pytest.skip('No SD/MMC/eMMC controller available') + + if not devices: + pytest.skip('No devices detected') + + part_detect = 0 + fs = 'fat' + for x in range(0, controllers): + if devices[x]['detected'] == 'yes': + u_boot_console.run_command('mmc dev %d' % x) + try: + partitions = devices[x][fs] + except: + print('No %s table on this device' % fs.upper()) + continue + + for part in partitions: + part_detect = 1 + addr = u_boot_utils.find_ram_base(u_boot_console) + devices[x]['addr_%d' % part] = addr + size = random.randint(4, 1 * 1024 * 1024) + devices[x]['size_%d' % part] = size + # count CRC32 + output = u_boot_console.run_command('crc32 %x %x' % (addr, size)) + m = re.search('==> (.+?)', output) + if not m: + pytest.fail('CRC32 failed') + expected_crc32 = m.group(1) + devices[x]['expected_crc32_%d' % part] = expected_crc32 + # do write + file = '%s_%d' % ('uboot_test', size) + devices[x]['file_%d' % part] = file + output = u_boot_console.run_command( + '%swrite mmc %d:%s %x %s %x' % (fs, x, part, addr, file, size) + ) + assert 'Unable to write' not in output + assert 'Error' not in output + assert 'overflow' not in output + expected_text = '%d bytes written' % size + assert expected_text in output + + alignment = int( + u_boot_console.config.buildconfig.get( + 'config_sys_cacheline_size', 128 + ) + ) + offset = random.randrange(alignment, 1024, alignment) + output = u_boot_console.run_command( + '%sload mmc %d:%s %x %s' % (fs, x, part, addr + offset, file) + ) + assert 'Invalid FAT entry' not in output + assert 'Unable to read file' not in output + assert 'Misaligned buffer address' not in output + expected_text = '%d bytes read' % size + assert expected_text in output + + output = u_boot_console.run_command( + 'crc32 %x $filesize' % (addr + offset) + ) + assert expected_crc32 in output + + if not part_detect: + pytest.skip('No %s partition detected' % fs.upper()) + +@pytest.mark.buildconfigspec('cmd_mmc') +@pytest.mark.buildconfigspec('cmd_ext4') +def test_mmc_ext4ls(u_boot_console): + if not mmc_set_up: + pytest.skip('No SD/MMC/eMMC controller available') + + if not devices: + pytest.skip('No devices detected') + + part_detect = 0 + fs = 'ext4' + for x in range(0, controllers): + if devices[x]['detected'] == 'yes': + try: + partitions = devices[x][fs] + except: + print('No %s table on this device' % fs.upper()) + continue + + u_boot_console.run_command('mmc dev %d' % x) + for part in partitions: + output = u_boot_console.run_command('%sls mmc %d:%s' % (fs, x, part)) + if 'Unrecognized filesystem type' in output: + partitions.remove(part) + pytest.fail('Unrecognized filesystem') + part_detect = 1 + + if not part_detect: + pytest.skip('No %s partition detected' % fs.upper()) + +@pytest.mark.buildconfigspec('cmd_mmc') +@pytest.mark.buildconfigspec('cmd_ext4') +@pytest.mark.buildconfigspec('ext4_write') +@pytest.mark.buildconfigspec('cmd_memory') +def test_mmc_ext4load_ext4write(u_boot_console): + if not mmc_set_up: + pytest.skip('No SD/MMC/eMMC controller available') + + if not devices: + pytest.skip('No devices detected') + + part_detect = 0 + fs = 'ext4' + for x in range(0, controllers): + if devices[x]['detected'] == 'yes': + u_boot_console.run_command('mmc dev %d' % x) + try: + partitions = devices[x][fs] + except: + print('No %s table on this device' % fs.upper()) + continue + + for part in partitions: + part_detect = 1 + addr = u_boot_utils.find_ram_base(u_boot_console) + devices[x]['addr_%d' % part] = addr + size = random.randint(4, 1 * 1024 * 1024) + devices[x]['size_%d' % part] = size + # count CRC32 + output = u_boot_console.run_command('crc32 %x %x' % (addr, size)) + m = re.search('==> (.+?)', output) + if not m: + pytest.fail('CRC32 failed') + expected_crc32 = m.group(1) + devices[x]['expected_crc32_%d' % part] = expected_crc32 + # do write + + file = '%s_%d' % ('uboot_test', size) + devices[x]['file_%d' % part] = file + output = u_boot_console.run_command( + '%swrite mmc %d:%s %x /%s %x' % (fs, x, part, addr, file, size) + ) + assert 'Unable to write' not in output + assert 'Error' not in output + assert 'overflow' not in output + expected_text = '%d bytes written' % size + assert expected_text in output + + offset = random.randrange(128, 1024, 128) + output = u_boot_console.run_command( + '%sload mmc %d:%s %x /%s' % (fs, x, part, addr + offset, file) + ) + expected_text = '%d bytes read' % size + assert expected_text in output + + output = u_boot_console.run_command( + 'crc32 %x $filesize' % (addr + offset) + ) + assert expected_crc32 in output + + if not part_detect: + pytest.skip('No %s partition detected' % fs.upper()) + +@pytest.mark.buildconfigspec('cmd_mmc') +@pytest.mark.buildconfigspec('cmd_ext2') +def test_mmc_ext2ls(u_boot_console): + if not mmc_set_up: + pytest.skip('No SD/MMC/eMMC controller available') + + if not devices: + pytest.skip('No devices detected') + + part_detect = 0 + fs = 'ext2' + for x in range(0, controllers): + if devices[x]['detected'] == 'yes': + u_boot_console.run_command('mmc dev %d' % x) + try: + partitions = devices[x][fs] + except: + print('No %s table on this device' % fs.upper()) + continue + + for part in partitions: + part_detect = 1 + output = u_boot_console.run_command('%sls mmc %d:%s' % (fs, x, part)) + if 'Unrecognized filesystem type' in output: + partitions.remove(part) + pytest.fail('Unrecognized filesystem') + part_detect = 1 + + if not part_detect: + pytest.skip('No %s partition detected' % fs.upper()) + +@pytest.mark.buildconfigspec('cmd_mmc') +@pytest.mark.buildconfigspec('cmd_ext2') +@pytest.mark.buildconfigspec('cmd_ext4') +@pytest.mark.buildconfigspec('ext4_write') +@pytest.mark.buildconfigspec('cmd_memory') +def test_mmc_ext2load(u_boot_console): + if not mmc_set_up: + pytest.skip('No SD/MMC/eMMC controller available') + + if not devices: + pytest.skip('No devices detected') + + part_detect = 0 + fs = 'ext2' + for x in range(0, controllers): + if devices[x]['detected'] == 'yes': + u_boot_console.run_command('mmc dev %d' % x) + try: + partitions = devices[x][fs] + except: + print('No %s table on this device' % fs.upper()) + continue + + for part in partitions: + part_detect = 1 + addr = devices[x]['addr_%d' % part] + size = devices[x]['size_%d' % part] + expected_crc32 = devices[x]['expected_crc32_%d' % part] + file = devices[x]['file_%d' % part] + + offset = random.randrange(128, 1024, 128) + output = u_boot_console.run_command( + '%sload mmc %d:%s %x /%s' % (fs, x, part, addr + offset, file) + ) + expected_text = '%d bytes read' % size + assert expected_text in output + + output = u_boot_console.run_command( + 'crc32 %x $filesize' % (addr + offset) + ) + assert expected_crc32 in output + + if not part_detect: + pytest.skip('No %s partition detected' % fs.upper()) + +@pytest.mark.buildconfigspec('cmd_mmc') +@pytest.mark.buildconfigspec('cmd_fs_generic') +def test_mmc_ls(u_boot_console): + if not mmc_set_up: + pytest.skip('No SD/MMC/eMMC controller available') + + if not devices: + pytest.skip('No devices detected') + + part_detect = 0 + for x in range(0, controllers): + if devices[x]['detected'] == 'yes': + u_boot_console.run_command('mmc dev %d' % x) + for fs in ['fat', 'ext4']: + try: + partitions = devices[x][fs] + except: + print('No %s table on this device' % fs.upper()) + continue + + for part in partitions: + part_detect = 1 + output = u_boot_console.run_command('ls mmc %d:%s' % (x, part)) + if re.search(r'No \w+ table on this device', output): + pytest.fail( + '%s: Partition table not found %d' % (fs.upper(), x) + ) + + if not part_detect: + pytest.skip('No partition detected') + +@pytest.mark.buildconfigspec('cmd_mmc') +@pytest.mark.buildconfigspec('cmd_fs_generic') +def test_mmc_load(u_boot_console): + if not mmc_set_up: + pytest.skip('No SD/MMC/eMMC controller available') + + if not devices: + pytest.skip('No devices detected') + + part_detect = 0 + for x in range(0, controllers): + if devices[x]['detected'] == 'yes': + u_boot_console.run_command('mmc dev %d' % x) + for fs in ['fat', 'ext4']: + try: + partitions = devices[x][fs] + except: + print('No %s table on this device' % fs.upper()) + continue + + for part in partitions: + part_detect = 1 + addr = devices[x]['addr_%d' % part] + size = devices[x]['size_%d' % part] + expected_crc32 = devices[x]['expected_crc32_%d' % part] + file = devices[x]['file_%d' % part] + + offset = random.randrange(128, 1024, 128) + output = u_boot_console.run_command( + 'load mmc %d:%s %x /%s' % (x, part, addr + offset, file) + ) + expected_text = '%d bytes read' % size + assert expected_text in output + + output = u_boot_console.run_command( + 'crc32 %x $filesize' % (addr + offset) + ) + assert expected_crc32 in output + + if not part_detect: + pytest.skip('No partition detected') + +@pytest.mark.buildconfigspec('cmd_mmc') +@pytest.mark.buildconfigspec('cmd_fs_generic') +def test_mmc_save(u_boot_console): + if not mmc_set_up: + pytest.skip('No SD/MMC/eMMC controller available') + + if not devices: + pytest.skip('No devices detected') + + part_detect = 0 + for x in range(0, controllers): + if devices[x]['detected'] == 'yes': + u_boot_console.run_command('mmc dev %d' % x) + for fs in ['fat', 'ext4']: + try: + partitions = devices[x][fs] + except: + print('No %s table on this device' % fs.upper()) + continue + + for part in partitions: + part_detect = 1 + addr = devices[x]['addr_%d' % part] + size = 0 + file = devices[x]['file_%d' % part] + + offset = random.randrange(128, 1024, 128) + output = u_boot_console.run_command( + 'save mmc %d:%s %x /%s %d' + % (x, part, addr + offset, file, size) + ) + expected_text = '%d bytes written' % size + assert expected_text in output + + if not part_detect: + pytest.skip('No partition detected') + +@pytest.mark.buildconfigspec('cmd_mmc') +@pytest.mark.buildconfigspec('cmd_fat') +@pytest.mark.buildconfigspec('cmd_memory') +def test_mmc_fat_read_write_files(u_boot_console): + test_mmc_list(u_boot_console) + test_mmc_dev(u_boot_console) + test_mmcinfo(u_boot_console) + test_mmc_part(u_boot_console) + if not mmc_set_up: + pytest.skip('No SD/MMC/eMMC controller available') + + if not devices: + pytest.skip('No devices detected') + + part_detect = 0 + fs = 'fat' + + # Number of files to be written/read in MMC card + num_files = 100 + + for x in range(0, controllers): + if devices[x]['detected'] == 'yes': + u_boot_console.run_command('mmc dev %d' % x) + try: + partitions = devices[x][fs] + except: + print('No %s table on this device' % fs.upper()) + continue + + for part in partitions: + part_detect = 1 + addr = u_boot_utils.find_ram_base(u_boot_console) + count_f = 0 + addr_l = [] + size_l = [] + file_l = [] + crc32_l = [] + offset_l = [] + addr_l.append(addr) + + while count_f < num_files: + size_l.append(random.randint(4, 1 * 1024 * 1024)) + + # CRC32 count + output = u_boot_console.run_command( + 'crc32 %x %x' % (addr_l[count_f], size_l[count_f]) + ) + m = re.search('==> (.+?)', output) + if not m: + pytest.fail('CRC32 failed') + crc32_l.append(m.group(1)) + + # Write operation + file_l.append('%s_%d_%d' % ('uboot_test', count_f, size_l[count_f])) + output = u_boot_console.run_command( + '%swrite mmc %d:%s %x %s %x' + % ( + fs, + x, + part, + addr_l[count_f], + file_l[count_f], + size_l[count_f], + ) + ) + assert 'Unable to write' not in output + assert 'Error' not in output + assert 'overflow' not in output + expected_text = '%d bytes written' % size_l[count_f] + assert expected_text in output + + addr_l.append(addr_l[count_f] + size_l[count_f] + 1048576) + count_f += 1 + + count_f = 0 + while count_f < num_files: + alignment = int( + u_boot_console.config.buildconfig.get( + 'config_sys_cacheline_size', 128 + ) + ) + offset_l.append(random.randrange(alignment, 1024, alignment)) + + # Read operation + output = u_boot_console.run_command( + '%sload mmc %d:%s %x %s' + % ( + fs, + x, + part, + addr_l[count_f] + offset_l[count_f], + file_l[count_f], + ) + ) + assert 'Invalid FAT entry' not in output + assert 'Unable to read file' not in output + assert 'Misaligned buffer address' not in output + expected_text = '%d bytes read' % size_l[count_f] + assert expected_text in output + + output = u_boot_console.run_command( + 'crc32 %x $filesize' % (addr_l[count_f] + offset_l[count_f]) + ) + assert crc32_l[count_f] in output + + count_f += 1 + + if not part_detect: + pytest.skip('No %s partition detected' % fs.upper()) diff --git a/test/py/tests/test_nand.py b/test/py/tests/test_nand.py new file mode 100644 index 0000000..f5fd5d0 --- /dev/null +++ b/test/py/tests/test_nand.py @@ -0,0 +1,201 @@ +# SPDX-License-Identifier: GPL-2.0 +# (C) Copyright 2023, Advanced Micro Devices, Inc. + +import pytest +import random +import re +import u_boot_utils + +""" +Note: This test relies on boardenv_* containing configuration values to define +the nand device total size and timeout available for testing. Without this, the +test will be automatically skipped. This test will be also skipped if the NAND +flash device is not detected. + +For example: + +# Setup env__nand_device_test to set the NAND flash total size and timeout. +env__nand_device_test = { + 'size': '8192 MB', + 'timeout': 100000, +} +""" + +# Find out nand memory parameters +def nand_pre_commands(u_boot_console): + f = u_boot_console.config.env.get('env__nand_device_test', None) + if not f: + pytest.skip('No env file to read for NAND device test') + + total_size = f.get('size', None) + timeout = f.get('timeout') + + if not total_size: + pytest.skip('NAND device size not recognized') + + output = u_boot_console.run_command('nand info') + if not 'Device 0: nand0' in output: + pytest.skip('No NAND device available') + + m = re.search('Page size (.+?) b', output) + if m: + try: + page_size = int(m.group(1)) + except ValueError: + pytest.fail('NAND page size not recognized') + + m = re.search('sector size (.+?) KiB', output) + if m: + try: + erase_size = int(m.group(1)) + sector_size = erase_size + except ValueError: + pytest.fail('NAND erase size not recognized') + + erase_size *= 1024 + + output = u_boot_console.run_command('nand bad') + if not 'bad blocks:' in output: + pytest.skip('No NAND device available') + + count = 0 + m = re.search('bad blocks:([\n\s\s\d\w]*)', output) + if m: + print(m.group(1)) + var = m.group(1).split() + count = len(var) + print('bad blocks count= ' + str(count)) + + m = re.search('(.+?) MB', total_size) + if m: + try: + total_size = int(m.group(1)) + total_size *= 1024 * 1024 + print('Total size is: ' + str(total_size) + ' B') + total_size -= count * sector_size * 1024 + print('New Total size is: ' + str(total_size) + ' B') + except ValueError: + pytest.fail('NAND size not recognized') + + return page_size, erase_size, total_size, sector_size, timeout + +@pytest.mark.buildconfigspec('cmd_nand') +@pytest.mark.buildconfigspec('cmd_bdi') +@pytest.mark.buildconfigspec('cmd_memory') +def test_nand_read_twice(u_boot_console): + """This test reads the whole NAND flash twice, random_size till full flash + size, random till page size. + """ + + page_size, erase_size, total_size, sector_size, timeout = nand_pre_commands( + u_boot_console) + expected_read = 'read: OK' + + for size in random.randint(4, page_size), random.randint(4, total_size), total_size: + addr = u_boot_utils.find_ram_base(u_boot_console) + + output = u_boot_console.run_command( + 'nand read %x 0 %x' % (addr + total_size, size) + ) + assert expected_read in output + + output = u_boot_console.run_command('crc32 %x %x' % (addr + total_size, size)) + m = re.search('==> (.+?)', output) + if not m: + pytest.fail('CRC32 failed') + expected_crc32 = m.group(1) + + output = u_boot_console.run_command( + 'nand read %x 0 %x' % (addr + total_size + 10, size) + ) + assert expected_read in output + + output = u_boot_console.run_command( + 'crc32 %x %x' % (addr + total_size + 10, size) + ) + assert expected_crc32 in output + +@pytest.mark.buildconfigspec('cmd_nand') +@pytest.mark.buildconfigspec('cmd_bdi') +@pytest.mark.buildconfigspec('cmd_memory') +def test_nand_write_twice(u_boot_console): + """This test does the random writes till page size, size and full size""" + + page_size, erase_size, total_size, sector_size, timeout = nand_pre_commands( + u_boot_console) + expected_write = 'written: OK' + expected_read = 'read: OK' + expected_erase = '100% complete.' + old_size = 0 + + for size in ( + random.randint(4, page_size), + random.randint(page_size, total_size), + total_size, + ): + offset = page_size + addr = u_boot_utils.find_ram_base(u_boot_console) + size = size - old_size + output = u_boot_console.run_command('crc32 %x %x' % (addr + total_size, size)) + m = re.search('==> (.+?)', output) + if not m: + pytest.fail('CRC32 failed') + + expected_crc32 = m.group(1) + + if old_size % page_size: + old_size = int(old_size / page_size + 1) + old_size *= page_size + + if old_size + size > total_size: + size = total_size - old_size + + eraseoffset = int(old_size / erase_size) + eraseoffset *= erase_size + + erasesize = int(size / erase_size + 1) + erasesize *= erase_size + + output = u_boot_console.run_command( + 'nand erase.spread %x %x' % (eraseoffset, erasesize) + ) + assert expected_erase in output + + output = u_boot_console.run_command( + 'nand write %x %x %x' % (addr + total_size, old_size, size) + ) + assert expected_write in output + output = u_boot_console.run_command( + 'nand read %x %x %x' % (addr + total_size + offset, old_size, size) + ) + assert expected_read in output + output = u_boot_console.run_command( + 'crc32 %x %x' % (addr + total_size + offset, size) + ) + assert expected_crc32 in output + old_size = size + +@pytest.mark.buildconfigspec('cmd_nand') +def test_nand_erase_block(u_boot_console): + page_size, erase_size, total_size, sector_size, timeout = nand_pre_commands( + u_boot_console) + + expected_erase = '100% complete.' + for start in range(0, total_size, erase_size): + output = u_boot_console.run_command( + 'nand erase.spread %x %x' % (start, erase_size) + ) + assert expected_erase in output + +@pytest.mark.buildconfigspec('cmd_nand') +def test_nand_erase_all(u_boot_console): + page_size, erase_size, total_size, sector_size, timeout = nand_pre_commands( + u_boot_console) + + expected_erase = '100% complete.' + start = 0 + with u_boot_console.temporary_timeout(timeout): + output = u_boot_console.run_command( + 'nand erase.spread 0 ' + str(hex(total_size)) + ) + assert expected_erase in output diff --git a/test/py/tests/test_scsi.py b/test/py/tests/test_scsi.py new file mode 100644 index 0000000..be2e283 --- /dev/null +++ b/test/py/tests/test_scsi.py @@ -0,0 +1,92 @@ +# SPDX-License-Identifier: GPL-2.0 +# (C) Copyright 2023, Advanced Micro Devices, Inc. + +import pytest + +""" +Note: This test relies on boardenv_* containing configuration values to define +the SCSI device number, type and capacity. This test will be automatically +skipped without this. + +For example: + +# Setup env__scsi_device_test to set the SCSI device number/slot, the type of +device, and the device capacity in MB. +env__scsi_device_test = { + 'dev_num': 0, + 'device_type': 'Hard Disk', + 'device_capacity': '476940.0 MB', +} +""" + +def scsi_setup(u_boot_console): + f = u_boot_console.config.env.get('env__scsi_device_test', None) + if not f: + pytest.skip('No SCSI device to test') + + dev_num = f.get('dev_num', None) + if not isinstance(dev_num, int): + pytest.skip('No device number specified in env file to read') + + dev_type = f.get('device_type') + if not dev_type: + pytest.skip('No device type specified in env file to read') + + dev_size = f.get('device_capacity') + if not dev_size: + pytest.skip('No device capacity specified in env file to read') + + return dev_num, dev_type, dev_size + +@pytest.mark.buildconfigspec('cmd_scsi') +def test_scsi_reset(u_boot_console): + dev_num, dev_type, dev_size = scsi_setup(u_boot_console) + output = u_boot_console.run_command('scsi reset') + assert f'Device {dev_num}:' in output + assert f'Type: {dev_type}' in output + assert f'Capacity: {dev_size}' in output + output = u_boot_console.run_command('echo $?') + assert output.endswith('0') + +@pytest.mark.buildconfigspec('cmd_scsi') +def test_scsi_info(u_boot_console): + dev_num, dev_type, dev_size = scsi_setup(u_boot_console) + output = u_boot_console.run_command('scsi info') + assert f'Device {dev_num}:' in output + assert f'Type: {dev_type}' in output + assert f'Capacity: {dev_size}' in output + output = u_boot_console.run_command('echo $?') + assert output.endswith('0') + +@pytest.mark.buildconfigspec('cmd_scsi') +def test_scsi_scan(u_boot_console): + dev_num, dev_type, dev_size = scsi_setup(u_boot_console) + output = u_boot_console.run_command('scsi scan') + assert f'Device {dev_num}:' in output + assert f'Type: {dev_type}' in output + assert f'Capacity: {dev_size}' in output + output = u_boot_console.run_command('echo $?') + assert output.endswith('0') + +@pytest.mark.buildconfigspec('cmd_scsi') +def test_scsi_dev(u_boot_console): + dev_num, dev_type, dev_size = scsi_setup(u_boot_console) + output = u_boot_console.run_command('scsi device') + assert 'no scsi devices available' not in output + assert f'device {dev_num}:' in output + assert f'Type: {dev_type}' in output + assert f'Capacity: {dev_size}' in output + output = u_boot_console.run_command('echo $?') + assert output.endswith('0') + output = u_boot_console.run_command('scsi device %d' % dev_num) + assert 'is now current device' in output + output = u_boot_console.run_command('echo $?') + assert output.endswith('0') + +@pytest.mark.buildconfigspec('cmd_scsi') +def test_scsi_part(u_boot_console): + test_scsi_dev(u_boot_console) + output = u_boot_console.run_command('scsi part') + assert 'Partition Map for SCSI device' in output + output = u_boot_console.run_command('echo $?') + assert output.endswith('0') diff --git a/test/py/tests/test_spi.py b/test/py/tests/test_spi.py new file mode 100644 index 0000000..a0b5c07 --- /dev/null +++ b/test/py/tests/test_spi.py @@ -0,0 +1,626 @@ +# SPDX-License-Identifier: GPL-2.0 +# (C) Copyright 2023, Advanced Micro Devices, Inc. + +import pytest +import random +import re +import u_boot_utils + +""" +Note: This test relies on boardenv_* containing configuration values to define +spi minimum and maximum frequnecies at which the flash part can operate on and +these tests run at 5 different spi frequnecy randomised values in the range. +It also defines the SPI bus number containing the SPI-flash chip, SPI +chip-select, SPI mode, SPI flash part name and timeout parameters. If minimum +and maximum frequency is not defined, it will run on freq 0 by default. + +Without the boardenv_* configuration, this test will be automatically skipped. + +It also relies on configuration values for supported flashes for qspi lock and +unlock cases. It will run qspi lock-unlock cases only for the supported flash +parts. + +Example: +env__spi_device_test = { + 'bus': 0, + 'chip_select': 0, + 'min_freq': 10000000, + 'max_freq': 100000000, + 'mode': 0, + 'part_name': 'n25q00a', + 'timeout': 100000, +} + +env__qspi_lock_unlock = { + 'supported_flash': 'mt25qu512a, n25q00a, n25q512ax3', +} +""" + +def setup_spi(u_boot_console): + f = u_boot_console.config.env.get('env__spi_device_test', None) + if not f: + pytest.skip('No env file to read for SPI family device test') + + bus = f.get('bus', 0) + cs = f.get('chip_select', 0) + mode = f.get('mode', 0) + part_name = f.get('part_name', None) + timeout = f.get('timeout', None) + + if not part_name: + pytest.skip('No env file to read SPI family flash part name') + + return bus, cs, mode, part_name, timeout + +# Find out minimum and maximum frequnecies that SPI device can operate +def spi_find_freq_range(u_boot_console): + f = u_boot_console.config.env.get('env__spi_device_test', None) + if not f: + pytest.skip('No env file to read for SPI family device test') + + min_f = f.get('min_freq', None) + max_f = f.get('max_freq', None) + + if not min_f: + min_f = 0 + if not max_f: + max_f = 0 + if max_f < min_f: + max_f = min_f + + if min_f == 0 and max_f == 0: + iterations = 1 + else: + iterations = 5 + + return min_f, max_f, iterations + +# Find out SPI family flash memory parameters +def spi_pre_commands(u_boot_console, freq): + bus, cs, mode, part_name, timeout = setup_spi(u_boot_console) + + output = u_boot_console.run_command(f'sf probe {bus}:{cs} {freq} {mode}') + if not 'SF: Detected' in output: + pytest.skip('No SPI device available') + + if not part_name in output: + pytest.fail('SPI flash part name not recognized') + + m = re.search('page size (.+?) Bytes', output) + if m: + try: + page_size = int(m.group(1)) + except ValueError: + pytest.fail('SPI page size not recognized') + + m = re.search('erase size (.+?) KiB', output) + if m: + try: + erase_size = int(m.group(1)) + except ValueError: + pytest.fail('SPI erase size not recognized') + + erase_size *= 1024 + + m = re.search('total (.+?) MiB', output) + if m: + try: + global total_size + total_size = int(m.group(1)) + except ValueError: + pytest.fail('SPI total size not recognized') + + total_size *= 1024 * 1024 + + m = re.search('Detected (.+?) with', output) + if m: + try: + flash_part = m.group(1) + assert flash_part == part_name + except: + pytest.fail('SPI flash part not recognized') + + return page_size, erase_size, total_size, flash_part, timeout + +# Read the whole SPI flash twice, random_size till full flash size, random +# till page size +def spi_read_twice(u_boot_console, page_size, total_size, timeout): + expected_read = 'Read: OK' + + for size in random.randint(4, page_size), random.randint(4, total_size), total_size: + addr = u_boot_utils.find_ram_base(u_boot_console) + size = size & ~3 + with u_boot_console.temporary_timeout(timeout): + output = u_boot_console.run_command( + 'sf read %x 0 %x' % (addr + total_size, size) + ) + assert expected_read in output + output = u_boot_console.run_command('crc32 %x %x' % (addr + total_size, size)) + m = re.search('==> (.+?)$', output) + if not m: + pytest.fail('CRC32 failed') + expected_crc32 = m.group(1) + with u_boot_console.temporary_timeout(timeout): + output = u_boot_console.run_command( + 'sf read %x 0 %x' % (addr + total_size + 10, size) + ) + assert expected_read in output + output = u_boot_console.run_command( + 'crc32 %x %x' % (addr + total_size + 10, size) + ) + assert expected_crc32 in output + +@pytest.mark.buildconfigspec('cmd_sf') +@pytest.mark.buildconfigspec('cmd_bdi') +@pytest.mark.buildconfigspec('cmd_memory') +def test_spi_read_twice(u_boot_console): + min_f, max_f, loop = spi_find_freq_range(u_boot_console) + i = 0 + while i < loop: + page_size, erase_size, total_size, flash_part, timeout = spi_pre_commands( + u_boot_console, random.randint(min_f, max_f)) + spi_read_twice(u_boot_console, page_size, total_size, timeout) + i = i + 1 + +# This test check crossing boundary for dual/parallel configurations +def spi_erase_block(u_boot_console, erase_size, total_size): + expected_erase = 'Erased: OK' + for start in range(0, total_size, erase_size): + output = u_boot_console.run_command('sf erase %x %x' % (start, erase_size)) + assert expected_erase in output + +@pytest.mark.buildconfigspec('cmd_sf') +def test_spi_erase_block(u_boot_console): + min_f, max_f, loop = spi_find_freq_range(u_boot_console) + i = 0 + while i < loop: + page_size, erase_size, total_size, flash_part, timeout = spi_pre_commands( + u_boot_console, random.randint(min_f, max_f)) + spi_erase_block(u_boot_console, erase_size, total_size) + i = i + 1 + +# Random write till page size, random till size and full size +def spi_write_twice(u_boot_console, page_size, erase_size, total_size, timeout): + addr = u_boot_utils.find_ram_base(u_boot_console) + expected_write = 'Written: OK' + expected_read = 'Read: OK' + expected_erase = 'Erased: OK' + + old_size = 0 + for size in ( + random.randint(4, page_size), + random.randint(page_size, total_size), + total_size, + ): + offset = random.randint(4, page_size) + offset = offset & ~3 + size = size & ~3 + size = size - old_size + output = u_boot_console.run_command('crc32 %x %x' % (addr + total_size, size)) + m = re.search('==> (.+?)$', output) + if not m: + pytest.fail('CRC32 failed') + + expected_crc32 = m.group(1) + if old_size % page_size: + old_size = int(old_size / page_size) + old_size *= page_size + + if size % erase_size: + erasesize = int(size / erase_size + 1) + erasesize *= erase_size + + eraseoffset = int(old_size / erase_size) + eraseoffset *= erase_size + + timeout = 100000000 + start = 0 + with u_boot_console.temporary_timeout(timeout): + output = u_boot_console.run_command( + 'sf erase %x %x' % (eraseoffset, erasesize) + ) + assert expected_erase in output + + with u_boot_console.temporary_timeout(timeout): + output = u_boot_console.run_command( + 'sf write %x %x %x' % (addr + total_size, old_size, size) + ) + assert expected_write in output + with u_boot_console.temporary_timeout(timeout): + output = u_boot_console.run_command( + 'sf read %x %x %x' % (addr + total_size + offset, old_size, size) + ) + assert expected_read in output + output = u_boot_console.run_command( + 'crc32 %x %x' % (addr + total_size + offset, size) + ) + assert expected_crc32 in output + old_size = size + +@pytest.mark.buildconfigspec('cmd_bdi') +@pytest.mark.buildconfigspec('cmd_sf') +@pytest.mark.buildconfigspec('cmd_memory') +def test_spi_write_twice(u_boot_console): + min_f, max_f, loop = spi_find_freq_range(u_boot_console) + i = 0 + while i < loop: + page_size, erase_size, total_size, flash_part, timeout = spi_pre_commands( + u_boot_console, random.randint(min_f, max_f)) + spi_write_twice(u_boot_console, page_size, erase_size, total_size, timeout) + i = i + 1 + +def spi_write_continues(u_boot_console, page_size, total_size, timeout): + spi_erase_block(u_boot_console) + expected_write = 'Written: OK' + expected_read = 'Read: OK' + addr = u_boot_utils.find_ram_base(u_boot_console) + + output = u_boot_console.run_command('crc32 %x %x' % (addr + 0x10000, total_size)) + m = re.search('==> (.+?)$', output) + if not m: + pytest.fail('CRC32 failed') + expected_crc32 = m.group(1) + + old_size = 0 + for size in ( + random.randint(4, page_size), + random.randint(page_size, total_size), + total_size, + ): + size = size & ~3 + size = size - old_size + with u_boot_console.temporary_timeout(timeout): + output = u_boot_console.run_command( + 'sf write %x %x %x' % (addr + 0x10000 + old_size, old_size, size) + ) + assert expected_write in output + old_size += size + + with u_boot_console.temporary_timeout(timeout): + output = u_boot_console.run_command( + 'sf read %x %x %x' % (addr + 0x10000 + total_size, 0, total_size) + ) + assert expected_read in output + + output = u_boot_console.run_command( + 'crc32 %x %x' % (addr + 0x10000 + total_size, total_size) + ) + assert expected_crc32 in output + +@pytest.mark.buildconfigspec('cmd_bdi') +@pytest.mark.buildconfigspec('cmd_sf') +@pytest.mark.buildconfigspec('cmd_memory') +def test_spi_write_continues(u_boot_console): + min_f, max_f, loop = spi_find_freq_range(u_boot_console) + i = 0 + while i < loop: + page_size, erase_size, total_size, flash_part, timeout = spi_pre_commands( + u_boot_console, random.randint(min_f, max_f)) + spi_write_continues(u_boot_console, page_size, total_size, timeout) + i = i + 1 + +def spi_erase_all(u_boot_console, total_size, timeout): + expected_erase = 'Erased: OK' + start = 0 + with u_boot_console.temporary_timeout(timeout): + output = u_boot_console.run_command('sf erase 0 ' + str(hex(total_size))) + assert expected_erase in output + +@pytest.mark.buildconfigspec('cmd_sf') +def test_spi_erase_all(u_boot_console): + min_f, max_f, loop = spi_find_freq_range(u_boot_console) + i = 0 + while i < loop: + page_size, erase_size, total_size, flash_part, timeout = spi_pre_commands( + u_boot_console, random.randint(min_f, max_f)) + spi_erase_all(u_boot_console, total_size, timeout) + i = i + 1 + +# Flash operations: erase/write/read +def flash_ops( + u_boot_console, ops, start, size, offset=0, exp_ret=0, exp_str='', not_exp_str='' +): + + f = u_boot_console.config.env.get('env__spi_device_test', None) + if not f: + timeout = 1000000 + + timeout = f.get('timeout', 1000000) + + if ops == 'erase': + with u_boot_console.temporary_timeout(timeout): + output = u_boot_console.run_command('sf erase %x %x' % (start, size)) + else: + with u_boot_console.temporary_timeout(timeout): + output = u_boot_console.run_command( + 'sf %s %x %x %x' % (ops, offset, start, size) + ) + + if exp_str: + assert exp_str in output + if not_exp_str: + assert not_exp_str not in output + + ret_code = u_boot_console.run_command('echo $?') + if exp_ret >= 0: + assert ret_code.endswith(str(exp_ret)) + + return output, ret_code + +# Unlock the flash before making it fail +def qspi_unlock_exit(u_boot_console, addr, size): + u_boot_console.run_command('sf protect unlock %x %x' % (addr, size)) + assert False, 'FAIL: Flash lock is unable to protect the data!' + +# QSPI lock-unlock operations +def qspi_lock_unlock(u_boot_console, lock_addr, lock_size): + addr = u_boot_utils.find_ram_base(u_boot_console) + expected_erase = 'Erased: OK' + expected_write = 'Written: OK' + expected_erase_errors = [ + 'Erase operation failed', + 'Attempted to modify a protected sector', + 'Erased: ERROR', + 'is protected and cannot be erased', + 'ERROR: flash area is locked', + ] + expected_write_errors = [ + 'ERROR: flash area is locked', + 'Program operation failed', + 'Attempted to modify a protected sector', + 'Written: ERROR', + ] + + # Find the protected/un-protected region + if lock_addr < (total_size // 2): + sect_num = (lock_addr + lock_size) // erase_size + x = 1 + while x < sect_num: + x *= 2 + prot_start = 0 + prot_size = x * erase_size + unprot_start = prot_start + prot_size + unprot_size = total_size - unprot_start + else: + sect_num = (total_size - lock_addr) // erase_size + x = 1 + while x < sect_num: + x *= 2 + prot_start = total_size - (x * erase_size) + prot_size = total_size - prot_start + unprot_start = 0 + unprot_size = prot_start + + # Check erase/write operation before locking + flash_ops(u_boot_console, 'erase', prot_start, prot_size, 0, 0, expected_erase) + flash_ops(u_boot_console, 'write', prot_start, prot_size, addr, 0, expected_write) + + # Locking the flash + u_boot_console.run_command('sf protect lock %x %x' % (lock_addr, lock_size)) + output = u_boot_console.run_command('echo $?') + assert output.endswith('0') + + # Check erase/write operation after locking + output, ret_code = flash_ops(u_boot_console, 'erase', prot_start, prot_size, 0, -1) + if not any(error in output for error in expected_erase_errors) or ret_code.endswith( + '0' + ): + qspi_unlock_exit(u_boot_console, lock_addr, lock_size) + + output, ret_code = flash_ops( + u_boot_console, 'write', prot_start, prot_size, addr, -1 + ) + if not any(error in output for error in expected_write_errors) or ret_code.endswith( + '0' + ): + qspi_unlock_exit(u_boot_console, lock_addr, lock_size) + + # Check locked sectors + sect_lock_start = random.randrange(prot_start, (prot_start + prot_size), erase_size) + if prot_size > erase_size: + sect_lock_size = random.randrange( + erase_size, (prot_start + prot_size - sect_lock_start), erase_size + ) + else: + sect_lock_size = erase_size + sect_write_size = random.randint(1, sect_lock_size) + + output, ret_code = flash_ops( + u_boot_console, 'erase', sect_lock_start, sect_lock_size, 0, -1 + ) + if not any(error in output for error in expected_erase_errors) or ret_code.endswith( + '0' + ): + qspi_unlock_exit(u_boot_console, lock_addr, lock_size) + + output, ret_code = flash_ops( + u_boot_console, 'write', sect_lock_start, sect_write_size, addr, -1 + ) + if not any(error in output for error in expected_write_errors) or ret_code.endswith( + '0' + ): + qspi_unlock_exit(u_boot_console, lock_addr, lock_size) + + # Check unlocked sectors + if unprot_size != 0: + sect_unlock_start = random.randrange( + unprot_start, (unprot_start + unprot_size), erase_size + ) + if unprot_size > erase_size: + sect_unlock_size = random.randrange( + erase_size, (unprot_start + unprot_size - sect_unlock_start), erase_size + ) + else: + sect_unlock_size = erase_size + sect_write_size = random.randint(1, sect_unlock_size) + + output, ret_code = flash_ops( + u_boot_console, 'erase', sect_unlock_start, sect_unlock_size, 0, -1 + ) + if expected_erase not in output or ret_code.endswith('1'): + qspi_unlock_exit(u_boot_console, lock_addr, lock_size) + + output, ret_code = flash_ops( + u_boot_console, 'write', sect_unlock_start, sect_write_size, addr, -1 + ) + if expected_write not in output or ret_code.endswith('1'): + qspi_unlock_exit(u_boot_console, lock_addr, lock_size) + + # Unlocking the flash + u_boot_console.run_command('sf protect unlock %x %x' % (lock_addr, lock_size)) + output = u_boot_console.run_command('echo $?') + assert output.endswith('0') + + # Check erase/write operation after un-locking + flash_ops(u_boot_console, 'erase', prot_start, prot_size, 0, 0, expected_erase) + flash_ops(u_boot_console, 'write', prot_start, prot_size, addr, 0, expected_write) + + # Check previous locked sectors + sect_lock_start = random.randrange(prot_start, (prot_start + prot_size), erase_size) + if prot_size > erase_size: + sect_lock_size = random.randrange( + erase_size, (prot_start + prot_size - sect_lock_start), erase_size + ) + else: + sect_lock_size = erase_size + sect_write_size = random.randint(1, sect_lock_size) + + flash_ops( + u_boot_console, 'erase', sect_lock_start, sect_lock_size, 0, 0, expected_erase + ) + flash_ops( + u_boot_console, + 'write', + sect_lock_start, + sect_write_size, + addr, + 0, + expected_write, + ) + +@pytest.mark.buildconfigspec('cmd_bdi') +@pytest.mark.buildconfigspec('cmd_sf') +@pytest.mark.buildconfigspec('cmd_memory') +def test_qspi_lock_unlock(u_boot_console): + min_f, max_f, loop = spi_find_freq_range(u_boot_console) + flashes = u_boot_console.config.env.get('env__qspi_lock_unlock', False) + if not flashes: + pytest.skip('No supported flash list for lock/unlock provided') + + i = 0 + while i < loop: + page_size, erase_size, total_size, flash_part, timeout = spi_pre_commands( + u_boot_console, random.randint(min_f, max_f)) + + flashes_list = flashes.get('supported_flash', None).split(',') + flashes_list = [x.strip() for x in flashes_list] + if flash_part not in flashes_list: + pytest.skip('Detected flash does not support lock/unlock') + + # For lower half of memory + lock_addr = random.randint(0, (total_size // 2) - 1) + lock_size = random.randint(1, ((total_size // 2) - lock_addr)) + qspi_lock_unlock(u_boot_console, lock_addr, lock_size) + + # For upper half of memory + lock_addr = random.randint((total_size // 2), total_size - 1) + lock_size = random.randint(1, (total_size - lock_addr)) + qspi_lock_unlock(u_boot_console, lock_addr, lock_size) + + # For entire flash + lock_addr = random.randint(0, total_size - 1) + lock_size = random.randint(1, (total_size - lock_addr)) + qspi_lock_unlock(u_boot_console, lock_addr, lock_size) + + i = i + 1 + +@pytest.mark.buildconfigspec('cmd_bdi') +@pytest.mark.buildconfigspec('cmd_sf') +@pytest.mark.buildconfigspec('cmd_memory') +def test_spi_negative(u_boot_console): + expected_erase = 'Erased: OK' + expected_write = 'Written: OK' + expected_read = 'Read: OK' + min_f, max_f, loop = spi_find_freq_range(u_boot_console) + page_size, erase_size, total_size, flash_part, timeout = spi_pre_commands( + u_boot_console, random.randint(min_f, max_f)) + addr = u_boot_utils.find_ram_base(u_boot_console) + i = 0 + while i < loop: + # Erase negative test + start = random.randint(0, total_size) + esize = erase_size + + # If erasesize is not multiple of flash's erase size + while esize % erase_size == 0: + esize = random.randint(0, total_size - start) + + error_msg = 'Erased: ERROR' + flash_ops( + u_boot_console, 'erase', start, esize, 0, 1, error_msg, expected_erase + ) + + # If eraseoffset exceeds beyond flash size + eoffset = random.randint(total_size, (total_size + int(0x1000000))) + error_msg = 'Offset exceeds device limit' + flash_ops( + u_boot_console, 'erase', eoffset, esize, 0, 1, error_msg, expected_erase + ) + + # If erasesize exceeds beyond flash size + esize = random.randint((total_size - start), (total_size + int(0x1000000))) + error_msg = 'ERROR: attempting erase past flash size' + flash_ops( + u_boot_console, 'erase', start, esize, 0, 1, error_msg, expected_erase + ) + + # If erase size is 0 + esize = 0 + error_msg = 'ERROR: Invalid size 0' + flash_ops( + u_boot_console, 'erase', start, esize, 0, 1, error_msg, expected_erase + ) + + # If erasesize is less than flash's page size + esize = random.randint(0, page_size) + start = random.randint(0, (total_size - page_size)) + error_msg = 'Erased: ERROR' + flash_ops( + u_boot_console, 'erase', start, esize, 0, 1, error_msg, expected_erase + ) + + # Write/Read negative test + # if Write/Read size exceeds beyond flash size + offset = random.randint(0, total_size) + size = random.randint((total_size - offset), (total_size + int(0x1000000))) + error_msg = 'Size exceeds partition or device limit' + flash_ops( + u_boot_console, 'write', offset, size, addr, 1, error_msg, expected_write + ) + flash_ops( + u_boot_console, 'read', offset, size, addr, 1, error_msg, expected_read + ) + + # if Write/Read offset exceeds beyond flash size + offset = random.randint(total_size, (total_size + int(0x1000000))) + size = random.randint(0, total_size) + error_msg = 'Offset exceeds device limit' + flash_ops( + u_boot_console, 'write', offset, size, addr, 1, error_msg, expected_write + ) + flash_ops( + u_boot_console, 'read', offset, size, addr, 1, error_msg, expected_read + ) + + # if Write/Read size is 0 + offset = random.randint(0, 2) + size = 0 + error_msg = 'ERROR: Invalid size 0' + flash_ops( + u_boot_console, 'write', offset, size, addr, 1, error_msg, expected_write + ) + flash_ops( + u_boot_console, 'read', offset, size, addr, 1, error_msg, expected_read + ) + + i = i + 1 diff --git a/test/py/tests/test_usb.py b/test/py/tests/test_usb.py new file mode 100644 index 0000000..fb3d20f --- /dev/null +++ b/test/py/tests/test_usb.py @@ -0,0 +1,626 @@ +# SPDX-License-Identifier: GPL-2.0 +# (C) Copyright 2023, Advanced Micro Devices, Inc. + +import pytest +import random +import re +import u_boot_utils + +""" +Note: This test doesn't rely on boardenv_* configuration values but it can +change the test behavior. To test USB file system cases (fat32, ext2, ext4), +USB device should be formatted and valid partitions should be created for +different file system, otherwise it may leads to failure. This test will be +skipped if the USB device is not detected. + +For example: + +# Setup env__usb_device_test_skip to not skipping the test. By default, its +# value is set to True. Set it to False to run all tests for USB device. +env__usb_device_test_skip = False +""" + +def setup_usb(u_boot_console): + if u_boot_console.config.env.get('env__usb_device_test_skip', True): + pytest.skip('USB device test is not enabled') + +@pytest.mark.buildconfigspec('cmd_usb') +def test_usb_start(u_boot_console): + setup_usb(u_boot_console) + output = u_boot_console.run_command('usb start') + + # if output is empty, usb start may already run as part of preboot command + # re-start the usb, in that case + if not output: + u_boot_console.run_command('usb stop') + output = u_boot_console.run_command('usb start') + + if 'No USB device found' in output: + pytest.skip('No USB controller available') + + if 'Card did not respond to voltage select' in output: + pytest.skip('No USB device present') + + controllers = 0 + storage_device = 0 + obj = re.search(r'\d USB Device\(s\) found', output) + controllers = int(obj.group()[0]) + + if not controllers: + pytest.skip('No USB device present') + + obj = re.search(r'\d Storage Device\(s\) found', output) + storage_device = int(obj.group()[0]) + + if not storage_device: + pytest.skip('No USB storage device present') + + assert 'USB init failed' not in output + assert 'starting USB...' in output + + if 'Starting the controller' in output: + assert 'USB XHCI' in output + + output = u_boot_console.run_command('echo $?') + assert output.endswith('0') + return controllers, storage_device + +@pytest.mark.buildconfigspec('cmd_usb') +def test_usb_stop(u_boot_console): + setup_usb(u_boot_console) + output = u_boot_console.run_command('usb stop') + assert 'stopping USB..' in output + + output = u_boot_console.run_command('echo $?') + assert output.endswith('0') + + output = u_boot_console.run_command('usb dev') + assert "USB is stopped. Please issue 'usb start' first." in output + +@pytest.mark.buildconfigspec('cmd_usb') +def test_usb_reset(u_boot_console): + setup_usb(u_boot_console) + output = u_boot_console.run_command('usb reset') + + if 'No USB device found' in output: + pytest.skip('No USB controller available') + + if 'Card did not respond to voltage select' in output: + pytest.skip('No USB device present') + + obj = re.search(r'\d USB Device\(s\) found', output) + usb_dev_num = int(obj.group()[0]) + + if not usb_dev_num: + pytest.skip('No USB device present') + + obj = re.search(r'\d Storage Device\(s\) found', output) + usb_stor_num = int(obj.group()[0]) + + if not usb_stor_num: + pytest.skip('No USB storage device present') + + assert 'BUG' not in output + assert 'USB init failed' not in output + assert 'resetting USB...' in output + + if 'Starting the controller' in output: + assert 'USB XHCI' in output + + output = u_boot_console.run_command('echo $?') + assert output.endswith('0') + +@pytest.mark.buildconfigspec('cmd_usb') +def test_usb_info(u_boot_console): + controllers, storage_device = test_usb_start(u_boot_console) + output = u_boot_console.run_command('usb info') + + num_controller = len(re.findall(': Hub,', output)) + num_mass_storage = len(re.findall(': Mass Storage,', output)) + + assert num_controller == controllers - 1 + assert num_mass_storage == storage_device + + output = u_boot_console.run_command('echo $?') + assert output.endswith('0') + + for i in range(0, storage_device + controllers - 1): + output = u_boot_console.run_command('usb info %d' % i) + num_controller = len(re.findall(': Hub,', output)) + num_mass_storage = len(re.findall(': Mass Storage,', output)) + assert num_controller + num_mass_storage == 1 + assert 'No device available' not in output + output = u_boot_console.run_command('echo $?') + assert output.endswith('0') + +@pytest.mark.buildconfigspec('cmd_usb') +def test_usb_tree(u_boot_console): + controllers, storage_device = test_usb_start(u_boot_console) + output = u_boot_console.run_command('usb tree') + + num_controller = len(re.findall('Hub', output)) + num_mass_storage = len(re.findall('Mass Storage', output)) + + assert num_controller == controllers - 1 + assert num_mass_storage == storage_device + + output = u_boot_console.run_command('echo $?') + assert output.endswith('0') + +@pytest.mark.buildconfigspec('cmd_usb') +@pytest.mark.buildconfigspec('usb_storage') +def test_usb_storage(u_boot_console): + controllers, storage_device = test_usb_start(u_boot_console) + output = u_boot_console.run_command('usb storage') + + obj = re.findall(r'Capacity: (\d+|\d+[\.]?\d)', output) + devices = {} + + for key in range(int(storage_device)): + devices[key] = {} + + for x in range(int(storage_device)): + try: + capacity = float(obj[x].split()[0]) + devices[x]['capacity'] = capacity + print('USB storage device %d capacity is: %g MB' % (x, capacity)) + except ValueError: + pytest.fail('USB storage device capacity not recognized') + + output = u_boot_console.run_command('echo $?') + assert output.endswith('0') + +@pytest.mark.buildconfigspec('cmd_usb') +def test_usb_dev(u_boot_console): + controllers, storage_device = test_usb_start(u_boot_console) + output = u_boot_console.run_command('usb dev') + + assert 'no usb devices available' not in output + + output = u_boot_console.run_command('echo $?') + assert output.endswith('0') + + devices = {} + + for key in range(int(storage_device)): + devices[key] = {} + + fail = 0 + for x in range(0, storage_device): + devices[x]['detected'] = 'yes' + output = u_boot_console.run_command('usb dev %d' % x) + + if 'Card did not respond to voltage select' in output: + fail = 1 + devices[x]['detected'] = 'no' + + if 'No USB device found' in output: + devices[x]['detected'] = 'no' + + if 'unknown device' in output: + devices[x]['detected'] = 'no' + + assert 'is now current device' in output + output = u_boot_console.run_command('echo $?') + assert output.endswith('0') + + if fail: + pytest.fail('USB device not present') + + return devices, controllers, storage_device + +@pytest.mark.buildconfigspec('cmd_usb') +def test_usb_part(u_boot_console): + devices, controllers, storage_device = test_usb_dev(u_boot_console) + if not devices: + pytest.skip('No devices detected') + + u_boot_console.run_command('usb part') + + output = u_boot_console.run_command('echo $?') + assert output.endswith('0') + + for i in range(0, storage_device): + if devices[i]['detected'] == 'yes': + u_boot_console.run_command('usb dev %d' % i) + output = u_boot_console.run_command('usb part') + + lines = output.split('\n') + part_fat = [] + part_ext = [] + for line in lines: + obj = re.search(r'(\d)\s+\d+\s+\d+\s+\w+\d+\w+-\d+\s+(\d+\w+)', line) + if obj: + part_id = int(obj.groups()[0]) + part_type = obj.groups()[1] + print('part_id:%d, part_type:%s' % (part_id, part_type)) + + if part_type == '0c' or part_type == '0b' or part_type == '0e': + print('Fat detected') + part_fat.append(part_id) + elif part_type == '83': + print('ext detected') + part_ext.append(part_id) + else: + pytest.fail('Unsupported Filesystem on device %d' % i) + devices[i]['ext4'] = part_ext + devices[i]['ext2'] = part_ext + devices[i]['fat'] = part_fat + + if not part_ext and not part_fat: + pytest.fail('No partition detected on device %d' % i) + + return devices, controllers, storage_device + +@pytest.mark.buildconfigspec('cmd_usb') +@pytest.mark.buildconfigspec('cmd_fat') +def test_usb_fatls_fatinfo(u_boot_console): + devices, controllers, storage_device = test_usb_part(u_boot_console) + if not devices: + pytest.skip('No devices detected') + + part_detect = 0 + fs = 'fat' + for x in range(0, int(storage_device)): + if devices[x]['detected'] == 'yes': + u_boot_console.run_command('usb dev %d' % x) + try: + partitions = devices[x][fs] + except: + print('No %s table on this device' % fs.upper()) + continue + + for part in partitions: + output = u_boot_console.run_command('fatls usb %d:%s' % (x, part)) + if 'Unrecognized filesystem type' in output: + partitions.remove(part) + pytest.fail('Unrecognized filesystem') + + if not re.search(r'\d file\(s\), \d dir\(s\)', output): + pytest.fail('%s read failed on device %d' % (fs.upper, x)) + + output = u_boot_console.run_command('fatinfo usb %d:%s' % (x, part)) + string = 'Filesystem: %s' % fs.upper + if re.search(string, output): + pytest.fail('%s FS failed on device %d' % (fs.upper(), x)) + part_detect = 1 + + if not part_detect: + pytest.skip('No %s partition detected' % fs.upper()) + +@pytest.mark.buildconfigspec('cmd_usb') +@pytest.mark.buildconfigspec('cmd_fat') +@pytest.mark.buildconfigspec('cmd_memory') +def test_usb_fatload_fatwrite(u_boot_console): + devices, controllers, storage_device = test_usb_part(u_boot_console) + if not devices: + pytest.skip('No devices detected') + + part_detect = 0 + fs = 'fat' + for x in range(0, int(storage_device)): + if devices[x]['detected'] == 'yes': + u_boot_console.run_command('usb dev %d' % x) + try: + partitions = devices[x][fs] + except: + print('No %s table on this device' % fs.upper()) + continue + + for part in partitions: + part_detect = 1 + addr = u_boot_utils.find_ram_base(u_boot_console) + size = random.randint(4, 1 * 1024 * 1024) + output = u_boot_console.run_command('crc32 %x %x' % (addr, size)) + m = re.search('==> (.+?)', output) + if not m: + pytest.fail('CRC32 failed') + expected_crc32 = m.group(1) + + file = '%s_%d' % ('uboot_test', size) + output = u_boot_console.run_command( + '%swrite usb %d:%s %x %s %x' % (fs, x, part, addr, file, size) + ) + assert 'Unable to write' not in output + assert 'Error' not in output + assert 'overflow' not in output + expected_text = '%d bytes written' % size + assert expected_text in output + + alignment = int( + u_boot_console.config.buildconfig.get( + 'config_sys_cacheline_size', 128 + ) + ) + offset = random.randrange(alignment, 1024, alignment) + output = u_boot_console.run_command( + '%sload usb %d:%s %x %s' % (fs, x, part, addr + offset, file) + ) + assert 'Invalid FAT entry' not in output + assert 'Unable to read file' not in output + assert 'Misaligned buffer address' not in output + expected_text = '%d bytes read' % size + assert expected_text in output + + output = u_boot_console.run_command( + 'crc32 %x $filesize' % (addr + offset) + ) + assert expected_crc32 in output + + if not part_detect: + pytest.skip('No %s partition detected' % fs.upper()) + + return file, size + +@pytest.mark.buildconfigspec('cmd_usb') +@pytest.mark.buildconfigspec('cmd_ext4') +def test_usb_ext4ls(u_boot_console): + devices, controllers, storage_device = test_usb_part(u_boot_console) + if not devices: + pytest.skip('No devices detected') + + part_detect = 0 + fs = 'ext4' + for x in range(0, int(storage_device)): + if devices[x]['detected'] == 'yes': + try: + partitions = devices[x][fs] + except: + print('No %s table on this device' % fs.upper()) + continue + + u_boot_console.run_command('usb dev %d' % x) + for part in partitions: + output = u_boot_console.run_command('%sls usb %d:%s' % (fs, x, part)) + if 'Unrecognized filesystem type' in output: + partitions.remove(part) + pytest.fail('Unrecognized filesystem') + part_detect = 1 + + if not part_detect: + pytest.skip('No %s partition detected' % fs.upper()) + +@pytest.mark.buildconfigspec('cmd_usb') +@pytest.mark.buildconfigspec('cmd_ext4') +@pytest.mark.buildconfigspec('ext4_write') +@pytest.mark.buildconfigspec('cmd_memory') +def test_usb_ext4load_ext4write(u_boot_console): + devices, controllers, storage_device = test_usb_part(u_boot_console) + if not devices: + pytest.skip('No devices detected') + + part_detect = 0 + fs = 'ext4' + for x in range(0, int(storage_device)): + if devices[x]['detected'] == 'yes': + u_boot_console.run_command('usb dev %d' % x) + try: + partitions = devices[x][fs] + except: + print('No %s table on this device' % fs.upper()) + continue + + for part in partitions: + part_detect = 1 + addr = u_boot_utils.find_ram_base(u_boot_console) + size = random.randint(4, 1 * 1024 * 1024) + output = u_boot_console.run_command('crc32 %x %x' % (addr, size)) + m = re.search('==> (.+?)', output) + if not m: + pytest.fail('CRC32 failed') + expected_crc32 = m.group(1) + file = '%s_%d' % ('uboot_test', size) + + output = u_boot_console.run_command( + '%swrite usb %d:%s %x /%s %x' % (fs, x, part, addr, file, size) + ) + assert 'Unable to write' not in output + assert 'Error' not in output + assert 'overflow' not in output + expected_text = '%d bytes written' % size + assert expected_text in output + + offset = random.randrange(128, 1024, 128) + output = u_boot_console.run_command( + '%sload usb %d:%s %x /%s' % (fs, x, part, addr + offset, file) + ) + expected_text = '%d bytes read' % size + assert expected_text in output + + output = u_boot_console.run_command( + 'crc32 %x $filesize' % (addr + offset) + ) + assert expected_crc32 in output + + if not part_detect: + pytest.skip('No %s partition detected' % fs.upper()) + + return file, size + +@pytest.mark.buildconfigspec('cmd_usb') +@pytest.mark.buildconfigspec('cmd_ext2') +def test_usb_ext2ls(u_boot_console): + devices, controllers, storage_device = test_usb_part(u_boot_console) + if not devices: + pytest.skip('No devices detected') + + part_detect = 0 + fs = 'ext2' + for x in range(0, int(storage_device)): + if devices[x]['detected'] == 'yes': + u_boot_console.run_command('usb dev %d' % x) + try: + partitions = devices[x][fs] + except: + print('No %s table on this device' % fs.upper()) + continue + + for part in partitions: + part_detect = 1 + output = u_boot_console.run_command('%sls usb %d:%s' % (fs, x, part)) + if 'Unrecognized filesystem type' in output: + partitions.remove(part) + pytest.fail('Unrecognized filesystem') + part_detect = 1 + + if not part_detect: + pytest.skip('No %s partition detected' % fs.upper()) + +@pytest.mark.buildconfigspec('cmd_usb') +@pytest.mark.buildconfigspec('cmd_ext2') +@pytest.mark.buildconfigspec('cmd_ext4') +@pytest.mark.buildconfigspec('ext4_write') +@pytest.mark.buildconfigspec('cmd_memory') +def test_usb_ext2load(u_boot_console): + devices, controllers, storage_device = test_usb_part(u_boot_console) + file, size = test_usb_ext4load_ext4write(u_boot_console) + + if not devices: + pytest.skip('No devices detected') + + part_detect = 0 + fs = 'ext2' + for x in range(0, int(storage_device)): + if devices[x]['detected'] == 'yes': + u_boot_console.run_command('usb dev %d' % x) + try: + partitions = devices[x][fs] + except: + print('No %s table on this device' % fs.upper()) + continue + + for part in partitions: + part_detect = 1 + addr = u_boot_utils.find_ram_base(u_boot_console) + output = u_boot_console.run_command('crc32 %x %x' % (addr, size)) + m = re.search('==> (.+?)', output) + if not m: + pytest.fail('CRC32 failed') + expected_crc32 = m.group(1) + + offset = random.randrange(128, 1024, 128) + output = u_boot_console.run_command( + '%sload usb %d:%s %x /%s' % (fs, x, part, addr + offset, file) + ) + expected_text = '%d bytes read' % size + assert expected_text in output + + output = u_boot_console.run_command( + 'crc32 %x $filesize' % (addr + offset) + ) + assert expected_crc32 in output + + if not part_detect: + pytest.skip('No %s partition detected' % fs.upper()) + +@pytest.mark.buildconfigspec('cmd_usb') +@pytest.mark.buildconfigspec('cmd_fs_generic') +def test_usb_ls(u_boot_console): + devices, controllers, storage_device = test_usb_part(u_boot_console) + if not devices: + pytest.skip('No devices detected') + + part_detect = 0 + for x in range(0, int(storage_device)): + if devices[x]['detected'] == 'yes': + u_boot_console.run_command('usb dev %d' % x) + for fs in ['fat', 'ext4']: + try: + partitions = devices[x][fs] + except: + print('No %s table on this device' % fs.upper()) + continue + + for part in partitions: + part_detect = 1 + output = u_boot_console.run_command('ls usb %d:%s' % (x, part)) + if re.search(r'No \w+ table on this device', output): + pytest.fail( + '%s: Partition table not found %d' % (fs.upper(), x) + ) + + if not part_detect: + pytest.skip('No partition detected') + +@pytest.mark.buildconfigspec('cmd_usb') +@pytest.mark.buildconfigspec('cmd_fs_generic') +def test_usb_load(u_boot_console): + devices, controllers, storage_device = test_usb_part(u_boot_console) + if not devices: + pytest.skip('No devices detected') + + part_detect = 0 + for x in range(0, int(storage_device)): + if devices[x]['detected'] == 'yes': + u_boot_console.run_command('usb dev %d' % x) + for fs in ['fat', 'ext4']: + try: + partitions = devices[x][fs] + except: + print('No %s table on this device' % fs.upper()) + continue + + for part in partitions: + part_detect = 1 + addr = u_boot_utils.find_ram_base(u_boot_console) + + if fs == 'fat': + file, size = test_usb_fatload_fatwrite(u_boot_console) + elif fs == 'ext4': + file, size = test_usb_ext4load_ext4write(u_boot_console) + + output = u_boot_console.run_command('crc32 %x %x' % (addr, size)) + m = re.search('==> (.+?)', output) + if not m: + pytest.fail('CRC32 failed') + expected_crc32 = m.group(1) + + offset = random.randrange(128, 1024, 128) + output = u_boot_console.run_command( + 'load usb %d:%s %x /%s' % (x, part, addr + offset, file) + ) + expected_text = '%d bytes read' % size + assert expected_text in output + + output = u_boot_console.run_command( + 'crc32 %x $filesize' % (addr + offset) + ) + assert expected_crc32 in output + + if not part_detect: + pytest.skip('No partition detected') + +@pytest.mark.buildconfigspec('cmd_usb') +@pytest.mark.buildconfigspec('cmd_fs_generic') +def test_usb_save(u_boot_console): + devices, controllers, storage_device = test_usb_part(u_boot_console) + if not devices: + pytest.skip('No devices detected') + + part_detect = 0 + for x in range(0, int(storage_device)): + if devices[x]['detected'] == 'yes': + u_boot_console.run_command('usb dev %d' % x) + for fs in ['fat', 'ext4']: + try: + partitions = devices[x][fs] + except: + print('No %s table on this device' % fs.upper()) + continue + + for part in partitions: + part_detect = 1 + addr = u_boot_utils.find_ram_base(u_boot_console) + size = random.randint(4, 1 * 1024 * 1024) + file = '%s_%d' % ('uboot_test', size) + + offset = random.randrange(128, 1024, 128) + output = u_boot_console.run_command( + 'save usb %d:%s %x /%s %x' + % (x, part, addr + offset, file, size) + ) + expected_text = '%d bytes written' % size + assert expected_text in output + + if not part_detect: + pytest.skip('No partition detected') diff --git a/test/py/tests/test_zynq_secure.py b/test/py/tests/test_zynq_secure.py new file mode 100644 index 0000000..0ee5aeb --- /dev/null +++ b/test/py/tests/test_zynq_secure.py @@ -0,0 +1,190 @@ +# SPDX-License-Identifier: GPL-2.0 +# (C) Copyright 2023, Advanced Micro Devices, Inc. + +import pytest +import re +import u_boot_utils +import test_net + +""" +This test verifies different type of secure boot images to authentication and +decryption using AES and RSA features for AMD's Zynq SoC. + +Note: This test relies on boardenv_* containing configuration values to define +the network available and files to be used for testing. Without this, this test +will be automatically skipped. It also relies on dhcp or setup_static net test +to support tftp to load files from a TFTP server. + +For example: + +# Details regarding the files that may be read from a TFTP server and addresses +# and size for aes and rsa cases respectively. This variable may be omitted or +# set to None if zynqmp secure testing is not possible or desired. +env__zynq_aes_readable_file = { + 'fn': 'zynq_aes_image.bin', + 'fnbit': 'zynq_aes_bit.bin', + 'fnpbit': 'zynq_aes_par_bit.bin', + 'srcaddr': 0x1000000, + 'dstaddr': 0x2000000, + 'dstlen': 0x1000000, +} + +env__zynq_rsa_readable_file = { + 'fn': 'zynq_rsa_image.bin', + 'fninvalid': 'zynq_rsa_image_invalid.bin', + 'srcaddr': 0x1000000, +} +""" + +def zynq_secure_pre_commands(u_boot_console): + output = u_boot_console.run_command('print modeboot') + if not 'modeboot=' in output: + pytest.skip('bootmode cannnot be determined') + m = re.search('modeboot=(.+?)boot', output) + if not m: + pytest.skip('bootmode cannnot be determined') + bootmode = m.group(1) + if bootmode == 'jtag': + pytest.skip('skipping due to jtag bootmode') + +@pytest.mark.buildconfigspec('cmd_zynq_aes') +def test_zynq_aes_image(u_boot_console): + f = u_boot_console.config.env.get('env__zynq_aes_readable_file', None) + if not f: + pytest.skip('No TFTP readable file for zynq secure aes case to read') + + dstaddr = f.get('dstaddr', None) + if not dstaddr: + pytest.skip('No dstaddr specified in env file to read') + + dstsize = f.get('dstlen', None) + if not dstsize: + pytest.skip('No dstlen specified in env file to read') + + zynq_secure_pre_commands(u_boot_console) + test_net.test_net_dhcp(u_boot_console) + if not test_net.net_set_up: + test_net.test_net_setup_static(u_boot_console) + + srcaddr = f.get('srcaddr', None) + if not srcaddr: + addr = u_boot_utils.find_ram_base(u_boot_console) + + expected_tftp = 'Bytes transferred = ' + fn = f['fn'] + output = u_boot_console.run_command('tftpboot %x %s' % (srcaddr, fn)) + assert expected_tftp in output + + expected_op = 'zynq aes [operation type] <srcaddr>' + output = u_boot_console.run_command( + 'zynq aes %x $filesize %x %x' % (srcaddr, dstaddr, dstsize) + ) + assert expected_op not in output + output = u_boot_console.run_command('echo $?') + assert output.endswith('0') + +@pytest.mark.buildconfigspec('cmd_zynq_aes') +def test_zynq_aes_bitstream(u_boot_console): + f = u_boot_console.config.env.get('env__zynq_aes_readable_file', None) + if not f: + pytest.skip('No TFTP readable file for zynq secure aes case to read') + + zynq_secure_pre_commands(u_boot_console) + test_net.test_net_dhcp(u_boot_console) + if not test_net.net_set_up: + test_net.test_net_setup_static(u_boot_console) + + srcaddr = f.get('srcaddr', None) + if not srcaddr: + addr = u_boot_utils.find_ram_base(u_boot_console) + + expected_tftp = 'Bytes transferred = ' + fn = f['fnbit'] + output = u_boot_console.run_command('tftpboot %x %s' % (srcaddr, fn)) + assert expected_tftp in output + + expected_op = 'zynq aes [operation type] <srcaddr>' + output = u_boot_console.run_command( + 'zynq aes load %x $filesize' % (srcaddr) + ) + assert expected_op not in output + output = u_boot_console.run_command('echo $?') + assert output.endswith('0') + +@pytest.mark.buildconfigspec('cmd_zynq_aes') +def test_zynq_aes_partial_bitstream(u_boot_console): + f = u_boot_console.config.env.get('env__zynq_aes_readable_file', None) + if not f: + pytest.skip('No TFTP readable file for zynq secure aes case to read') + + zynq_secure_pre_commands(u_boot_console) + test_net.test_net_dhcp(u_boot_console) + if not test_net.net_set_up: + test_net.test_net_setup_static(u_boot_console) + + srcaddr = f.get('srcaddr', None) + if not srcaddr: + addr = u_boot_utils.find_ram_base(u_boot_console) + + expected_tftp = 'Bytes transferred = ' + fn = f['fnpbit'] + output = u_boot_console.run_command('tftpboot %x %s' % (srcaddr, fn)) + assert expected_tftp in output + + expected_op = 'zynq aes [operation type] <srcaddr>' + output = u_boot_console.run_command('zynq aes loadp %x $filesize' % (srcaddr)) + assert expected_op not in output + output = u_boot_console.run_command('echo $?') + assert output.endswith('0') + +@pytest.mark.buildconfigspec('cmd_zynq_rsa') +def test_zynq_rsa_image(u_boot_console): + f = u_boot_console.config.env.get('env__zynq_rsa_readable_file', None) + if not f: + pytest.skip('No TFTP readable file for zynq secure rsa case to read') + + zynq_secure_pre_commands(u_boot_console) + test_net.test_net_dhcp(u_boot_console) + if not test_net.net_set_up: + test_net.test_net_setup_static(u_boot_console) + + srcaddr = f.get('srcaddr', None) + if not srcaddr: + addr = u_boot_utils.find_ram_base(u_boot_console) + + expected_tftp = 'Bytes transferred = ' + fn = f['fn'] + output = u_boot_console.run_command('tftpboot %x %s' % (srcaddr, fn)) + assert expected_tftp in output + + expected_op = 'zynq rsa <baseaddr>' + output = u_boot_console.run_command('zynq rsa %x ' % (srcaddr)) + assert expected_op not in output + output = u_boot_console.run_command('echo $?') + assert output.endswith('0') + +@pytest.mark.buildconfigspec('cmd_zynq_rsa') +def test_zynq_rsa_image_invalid(u_boot_console): + f = u_boot_console.config.env.get('env__zynq_rsa_readable_file', None) + if not f: + pytest.skip('No TFTP readable file for zynq secure rsa case to read') + + zynq_secure_pre_commands(u_boot_console) + test_net.test_net_dhcp(u_boot_console) + if not test_net.net_set_up: + test_net.test_net_setup_static(u_boot_console) + + srcaddr = f.get('srcaddr', None) + if not srcaddr: + addr = u_boot_utils.find_ram_base(u_boot_console) + + expected_tftp = 'Bytes transferred = ' + fninvalid = f['fninvalid'] + output = u_boot_console.run_command('tftpboot %x %s' % (srcaddr, fninvalid)) + assert expected_tftp in output + + expected_op = 'zynq rsa <baseaddr>' + output = u_boot_console.run_command('zynq rsa %x ' % (srcaddr)) + assert expected_op in output + output = u_boot_console.run_command('echo $?') + assert not output.endswith('0') diff --git a/test/py/tests/test_zynqmp_rpu.py b/test/py/tests/test_zynqmp_rpu.py new file mode 100644 index 0000000..479a612 --- /dev/null +++ b/test/py/tests/test_zynqmp_rpu.py @@ -0,0 +1,208 @@ +# SPDX-License-Identifier: GPL-2.0 +# (C) Copyright 2023, Advanced Micro Devices, Inc. + +import pytest +import random +import string +import test_net + +""" +Note: This test relies on boardenv_* containing configuration values to define +RPU applications information for AMD's ZynqMP SoC which contains, application +names, processors, address where it is built, expected output and the tftp load +addresses. This test will be automatically skipped without this. + +It also relies on dhcp or setup_static net test to support tftp to load +application on DDR. All the environment parameters are stored sequentially. +The length of all parameters values should be same. For example, if 2 app_names +are defined in a list as a value of parameter 'app_name' then the other +parameters value also should have a list with 2 items. +It will run RPU cases for all the applications defined in boardenv_* +configuration file. + +Example: +env__zynqmp_rpu_apps = { + 'app_name': ['hello_world_r5_0_ddr.elf', 'hello_world_r5_1_ddr.elf'], + 'proc': ['rpu0', 'rpu1'], + 'cpu_num': [4, 5], + 'addr': [0xA00000, 0xB00000], + 'output': ['Successfully ran Hello World application on DDR from RPU0', + 'Successfully ran Hello World application on DDR from RPU1'], + 'tftp_addr': [0x100000, 0x200000], +} +""" + +# Get rpu apps params from env +def get_rpu_apps_env(u_boot_console): + rpu_apps = u_boot_console.config.env.get('env__zynqmp_rpu_apps', False) + if not rpu_apps: + pytest.skip('ZynqMP RPU application info not defined!') + + apps = rpu_apps.get('app_name', None) + if not apps: + pytest.skip('No RPU application found!') + + procs = rpu_apps.get('proc', None) + if not procs: + pytest.skip('No RPU application processor provided!') + + cpu_nums = rpu_apps.get('cpu_num', None) + if not cpu_nums: + pytest.skip('No CPU number for respective processor provided!') + + addrs = rpu_apps.get('addr', None) + if not addrs: + pytest.skip('No RPU application build address found!') + + outputs = rpu_apps.get('output', None) + if not outputs: + pytest.skip('Expected output not found!') + + tftp_addrs = rpu_apps.get('tftp_addr', None) + if not tftp_addrs: + pytest.skip('TFTP address to load application not found!') + + return apps, procs, cpu_nums, addrs, outputs, tftp_addrs + +# Check return code +def ret_code(u_boot_console): + return u_boot_console.run_command('echo $?') + +# Initialize tcm +def tcminit(u_boot_console, rpu_mode): + output = u_boot_console.run_command('zynqmp tcminit %s' % rpu_mode) + assert 'Initializing TCM overwrites TCM content' in output + return ret_code(u_boot_console) + +# Load application in DDR +def load_app_ddr(u_boot_console, tftp_addr, app): + output = u_boot_console.run_command('tftpboot %x %s' % (tftp_addr, app)) + assert 'TIMEOUT' not in output + assert 'Bytes transferred = ' in output + + # Load elf + u_boot_console.run_command('bootelf -p %x' % tftp_addr) + assert ret_code(u_boot_console).endswith('0') + +# Disable cpus +def disable_cpus(u_boot_console, cpu_nums): + for num in cpu_nums: + u_boot_console.run_command(f'cpu {num} disable') + +# Load apps on RPU cores +def rpu_apps_load(u_boot_console, rpu_mode): + apps, procs, cpu_nums, addrs, outputs, tftp_addrs = get_rpu_apps_env( + u_boot_console) + test_net.test_net_dhcp(u_boot_console) + if not test_net.net_set_up: + test_net.test_net_setup_static(u_boot_console) + + try: + assert tcminit(u_boot_console, rpu_mode).endswith('0') + + for i in range(len(apps)): + if rpu_mode == 'lockstep' and procs[i] != 'rpu0': + continue + + load_app_ddr(u_boot_console, tftp_addrs[i], apps[i]) + rel_addr = int(addrs[i] + 0x3C) + + # Release cpu at app load address + cpu_num = cpu_nums[i] + cmd = 'cpu %d release %x %s' % (cpu_num, rel_addr, rpu_mode) + output = u_boot_console.run_command(cmd) + exp_op = f'Using TCM jump trampoline for address {hex(rel_addr)}' + assert exp_op in output + assert f'R5 {rpu_mode} mode' in output + u_boot_console.wait_for(outputs[i]) + assert ret_code(u_boot_console).endswith('0') + finally: + disable_cpus(u_boot_console, cpu_nums) + +@pytest.mark.buildconfigspec('cmd_zynqmp') +def test_zynqmp_rpu_app_load_split(u_boot_console): + rpu_apps_load(u_boot_console, 'split') + +@pytest.mark.buildconfigspec('cmd_zynqmp') +def test_zynqmp_rpu_app_load_lockstep(u_boot_console): + rpu_apps_load(u_boot_console, 'lockstep') + +@pytest.mark.buildconfigspec('cmd_zynqmp') +def test_zynqmp_rpu_app_load_negative(u_boot_console): + apps, procs, cpu_nums, addrs, outputs, tftp_addrs = get_rpu_apps_env( + u_boot_console) + + # Invalid commands + u_boot_console.run_command('zynqmp tcminit mode') + assert ret_code(u_boot_console).endswith('1') + + rand_str = ''.join(random.choices(string.ascii_lowercase, k=4)) + u_boot_console.run_command('zynqmp tcminit %s' % rand_str) + assert ret_code(u_boot_console).endswith('1') + + rand_num = random.randint(2, 100) + u_boot_console.run_command('zynqmp tcminit %d' % rand_num) + assert ret_code(u_boot_console).endswith('1') + + test_net.test_net_dhcp(u_boot_console) + if not test_net.net_set_up: + test_net.test_net_setup_static(u_boot_console) + + try: + rpu_mode = 'split' + assert tcminit(u_boot_console, rpu_mode).endswith('0') + + for i in range(len(apps)): + load_app_ddr(u_boot_console, tftp_addrs[i], apps[i]) + + # Run in split mode at different load address + rel_addr = int(addrs[i]) + random.randint(200, 1000) + cpu_num = cpu_nums[i] + cmd = 'cpu %d release %x %s' % (cpu_num, rel_addr, rpu_mode) + output = u_boot_console.run_command(cmd) + exp_op = f'Using TCM jump trampoline for address {hex(rel_addr)}' + assert exp_op in output + assert f'R5 {rpu_mode} mode' in output + assert not outputs[i] in output + + # Invalid rpu mode + rand_str = ''.join(random.choices(string.ascii_lowercase, k=4)) + cmd = 'cpu %d release %x %s' % (cpu_num, rel_addr, rand_str) + output = u_boot_console.run_command(cmd) + assert exp_op in output + assert f'Unsupported mode' in output + assert not ret_code(u_boot_console).endswith('0') + + # Switch to lockstep mode, without disabling CPUs + rpu_mode = 'lockstep' + u_boot_console.run_command('zynqmp tcminit %s' % rpu_mode) + assert not ret_code(u_boot_console).endswith('0') + + # Disable cpus + disable_cpus(u_boot_console, cpu_nums) + + # Switch to lockstep mode, after disabling CPUs + output = u_boot_console.run_command('zynqmp tcminit %s' % rpu_mode) + assert 'Initializing TCM overwrites TCM content' in output + assert ret_code(u_boot_console).endswith('0') + + # Run lockstep mode for RPU1 + for i in range(len(apps)): + if procs[i] == 'rpu0': + continue + + load_app_ddr(u_boot_console, tftp_addrs[i], apps[i]) + rel_addr = int(addrs[i] + 0x3C) + cpu_num = cpu_nums[i] + cmd = 'cpu %d release %x %s' % (cpu_num, rel_addr, rpu_mode) + output = u_boot_console.run_command(cmd) + exp_op = f'Using TCM jump trampoline for address {hex(rel_addr)}' + assert exp_op in output + assert f'R5 {rpu_mode} mode' in output + assert u_boot_console.p.expect([outputs[i]]) + finally: + disable_cpus(u_boot_console, cpu_nums) + # This forces the console object to be shutdown, so any subsequent test + # will reset the board back into U-Boot. + u_boot_console.drain_console() + u_boot_console.cleanup_spawn() diff --git a/test/py/tests/test_zynqmp_secure.py b/test/py/tests/test_zynqmp_secure.py new file mode 100644 index 0000000..570bd24 --- /dev/null +++ b/test/py/tests/test_zynqmp_secure.py @@ -0,0 +1,104 @@ +# SPDX-License-Identifier: GPL-2.0 +# (C) Copyright 2023, Advanced Micro Devices, Inc. + +import pytest +import re +import u_boot_utils +import test_net + +""" +This test verifies different type of secure boot images loaded at the DDR for +AMD's ZynqMP SoC. + +Note: This test relies on boardenv_* containing configuration values to define +the files to be used for testing. Without this, this test will be automatically +skipped. It also relies on dhcp or setup_static net test to support tftp to +load files from a TFTP server. + +For example: + +# Details regarding the files that may be read from a TFTP server. This +# variable may be omitted or set to None if zynqmp secure testing is not +# possible or desired. +env__zynqmp_secure_readable_file = { + 'fn': 'auth_bhdr_ppk1.bin', + 'enckupfn': 'auth_bhdr_enc_kup_load.bin', + 'addr': 0x1000000, + 'keyaddr': 0x100000, + 'keyfn': 'aes.txt', +} +""" + +@pytest.mark.buildconfigspec('cmd_zynqmp') +def test_zynqmp_secure_boot_image(u_boot_console): + """This test verifies secure boot image at the DDR address for + authentication only case. + """ + + f = u_boot_console.config.env.get('env__zynqmp_secure_readable_file', None) + if not f: + pytest.skip('No TFTP readable file for zynqmp secure cases to read') + + test_net.test_net_dhcp(u_boot_console) + if not test_net.net_set_up: + test_net.test_net_setup_static(u_boot_console) + + addr = f.get('addr', None) + if not addr: + addr = u_boot_utils.find_ram_base(u_boot_console) + + expected_tftp = 'Bytes transferred = ' + fn = f['fn'] + output = u_boot_console.run_command('tftpboot %x %s' % (addr, fn)) + assert expected_tftp in output + + output = u_boot_console.run_command('zynqmp secure %x $filesize' % (addr)) + assert 'Verified image at' in output + ver_addr = re.search(r'Verified image at 0x(.+)', output).group(1) + output = u_boot_console.run_command('echo $?') + assert output.endswith('0') + output = u_boot_console.run_command('print zynqmp_verified_img_addr') + assert f'zynqmp_verified_img_addr={ver_addr}' in output + assert 'Error' not in output + + +@pytest.mark.buildconfigspec('cmd_zynqmp') +def test_zynqmp_secure_boot_img_kup(u_boot_console): + """This test verifies secure boot image at the DDR address for encryption + with kup key case. + """ + + f = u_boot_console.config.env.get('env__zynqmp_secure_readable_file', None) + if not f: + pytest.skip('No TFTP readable file for zynqmp secure cases to read') + + test_net.test_net_dhcp(u_boot_console) + if not test_net.net_set_up: + test_net.test_net_setup_static(u_boot_console) + + keyaddr = f.get('keyaddr', None) + if not keyaddr: + addr = u_boot_utils.find_ram_base(u_boot_console) + expected_tftp = 'Bytes transferred = ' + keyfn = f['keyfn'] + output = u_boot_console.run_command('tftpboot %x %s' % (keyaddr, keyfn)) + assert expected_tftp in output + + addr = f.get('addr', None) + if not addr: + addr = u_boot_utils.find_ram_base(u_boot_console) + expected_tftp = 'Bytes transferred = ' + fn = f['enckupfn'] + output = u_boot_console.run_command('tftpboot %x %s' % (addr, fn)) + assert expected_tftp in output + + output = u_boot_console.run_command( + 'zynqmp secure %x $filesize %x' % (addr, keyaddr) + ) + assert 'Verified image at' in output + ver_addr = re.search(r'Verified image at 0x(.+)', output).group(1) + output = u_boot_console.run_command('echo $?') + assert output.endswith('0') + output = u_boot_console.run_command('print zynqmp_verified_img_addr') + assert f'zynqmp_verified_img_addr={ver_addr}' in output + assert 'Error' not in output |