From 274e9acff4bfed12e5d1fc5e80751c804cedc8e9 Mon Sep 17 00:00:00 2001 From: Caleb Connolly Date: Tue, 2 Jan 2024 11:55:30 +0000 Subject: [PATCH 1/7] pytest: update requirements.txt Update pygit2 and py to their latest versions. Even in the venv pygit2 still links against the system libgit2 library which is failing on latest Arch Linux. The py library also needs updating to fix a KeyNotFound exception during test initialisation. Signed-off-by: Caleb Connolly --- test/py/requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/py/requirements.txt b/test/py/requirements.txt index c3cc9163fb8..0f67c3c6194 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 From e894c10c040bc1bc65a7ea0168503fb1f8d18b09 Mon Sep 17 00:00:00 2001 From: Love Kumar Date: Fri, 19 Jan 2024 11:06:41 +0530 Subject: [PATCH 2/7] test/py: zynqmp_rpu: Add test for loading RPU apps Add testcases for loading RPU applications in split and lockstep mode including the negative one for AMD's ZynqMP SoC. Signed-off-by: Love Kumar --- test/py/tests/test_zynqmp_rpu.py | 208 +++++++++++++++++++++++++++++++ 1 file changed, 208 insertions(+) create mode 100644 test/py/tests/test_zynqmp_rpu.py diff --git a/test/py/tests/test_zynqmp_rpu.py b/test/py/tests/test_zynqmp_rpu.py new file mode 100644 index 00000000000..479a612b4ec --- /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() From d07f9e6661d380576c5e346d70ce9812935885e8 Mon Sep 17 00:00:00 2001 From: Love Kumar Date: Fri, 19 Jan 2024 11:08:34 +0530 Subject: [PATCH 3/7] test/py: gpio: Add gpio pins generic test Add gpio pins generic test for the set of gpio pin list to test various gpio related functionality, such as the input, set, clear, and toggle, it also tests the input and output functionality for shorted gpio pins. This test depends on boardenv* configuration to define gpio pins names. Signed-off-by: Love Kumar --- test/py/tests/test_gpio.py | 90 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/test/py/tests/test_gpio.py b/test/py/tests/test_gpio.py index 0af186f2360..3e16e636574 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 From a0d9432c4dee1f0aea52aec0901fd542cb3eeb20 Mon Sep 17 00:00:00 2001 From: Love Kumar Date: Fri, 19 Jan 2024 15:31:29 +0530 Subject: [PATCH 4/7] test/py: secure: Add secure tests for Zynq & ZynqMP Add test cases to verify the different type of secure boot images loaded at DDR location for AMD's ZynqMP SoC. It also adds tests authentication and decryption functionality using AES and RSA features for Zynq. Signed-off-by: Love Kumar --- test/py/tests/test_zynq_secure.py | 190 ++++++++++++++++++++++++++++ test/py/tests/test_zynqmp_secure.py | 104 +++++++++++++++ 2 files changed, 294 insertions(+) create mode 100644 test/py/tests/test_zynq_secure.py create mode 100644 test/py/tests/test_zynqmp_secure.py diff --git a/test/py/tests/test_zynq_secure.py b/test/py/tests/test_zynq_secure.py new file mode 100644 index 00000000000..0ee5aebc484 --- /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] ' + 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] ' + 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] ' + 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 ' + 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 ' + 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_secure.py b/test/py/tests/test_zynqmp_secure.py new file mode 100644 index 00000000000..570bd2439c1 --- /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 From 6c509cab6a0a5dee70ae324ebc983ba34b76ca13 Mon Sep 17 00:00:00 2001 From: Love Kumar Date: Fri, 19 Jan 2024 19:55:50 +0530 Subject: [PATCH 5/7] test/py: mmc: Add tests for MMC device Add the test cases for mmc commands to test its various functionality such as mmc list, dev, info, rescan, part, ls, load, and save. It also adds different file systems cases such as fat32, ext2 and ext4. Signed-off-by: Love Kumar --- test/py/tests/test_mmc.py | 671 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 671 insertions(+) create mode 100644 test/py/tests/test_mmc.py diff --git a/test/py/tests/test_mmc.py b/test/py/tests/test_mmc.py new file mode 100644 index 00000000000..a96c4e8fd89 --- /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()) From 1c5b6edad381d451462530421b487315d87ae308 Mon Sep 17 00:00:00 2001 From: Love Kumar Date: Fri, 19 Jan 2024 19:56:02 +0530 Subject: [PATCH 6/7] test/py: usb: Add tests for USB device Add the test cases for usb commands to test its various functionality such as start, stop, reset, info, tree, storage, dev, part, ls, load, and save. It also adds different file systems cases such as fat32, ext2 and ext4. Signed-off-by: Love Kumar --- test/py/tests/test_usb.py | 626 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 626 insertions(+) create mode 100644 test/py/tests/test_usb.py diff --git a/test/py/tests/test_usb.py b/test/py/tests/test_usb.py new file mode 100644 index 00000000000..fb3d20f0826 --- /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') From e24e5405d266f16b95fd7c1c3a2eec753c01f112 Mon Sep 17 00:00:00 2001 From: Love Kumar Date: Fri, 19 Jan 2024 20:07:13 +0530 Subject: [PATCH 7/7] test/py: scsi: Add test for scsi commands Add a following test cases for scsi commands: scsi_reset - To reset SCSI controller scsi_info - To show available SCSI devices scsi_scan - To (re-)scan SCSI bus scsi_device - To show or set surrent device scsi_part - To print partition table of selected SCSI device Signed-off-by: Love Kumar --- test/py/tests/test_scsi.py | 92 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 test/py/tests/test_scsi.py diff --git a/test/py/tests/test_scsi.py b/test/py/tests/test_scsi.py new file mode 100644 index 00000000000..be2e283e7d2 --- /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')