aboutsummaryrefslogtreecommitdiffstats
path: root/roms/u-boot/test/py/tests
diff options
context:
space:
mode:
authorAngelos Mouzakitis <a.mouzakitis@virtualopensystems.com>2023-10-10 14:33:42 +0000
committerAngelos Mouzakitis <a.mouzakitis@virtualopensystems.com>2023-10-10 14:33:42 +0000
commitaf1a266670d040d2f4083ff309d732d648afba2a (patch)
tree2fc46203448ddcc6f81546d379abfaeb323575e9 /roms/u-boot/test/py/tests
parente02cda008591317b1625707ff8e115a4841aa889 (diff)
Add submodule dependency filesHEADmaster
Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
Diffstat (limited to 'roms/u-boot/test/py/tests')
-rw-r--r--roms/u-boot/test/py/tests/test_000_version.py19
-rw-r--r--roms/u-boot/test/py/tests/test_android/test_ab.py75
-rw-r--r--roms/u-boot/test/py/tests/test_android/test_abootimg.py159
-rw-r--r--roms/u-boot/test/py/tests/test_android/test_avb.py137
-rw-r--r--roms/u-boot/test/py/tests/test_bind.py179
-rw-r--r--roms/u-boot/test/py/tests/test_button.py37
-rw-r--r--roms/u-boot/test/py/tests/test_dfu.py320
-rw-r--r--roms/u-boot/test/py/tests/test_dm.py35
-rw-r--r--roms/u-boot/test/py/tests/test_efi_capsule/capsule_defs.py5
-rw-r--r--roms/u-boot/test/py/tests/test_efi_capsule/conftest.py74
-rw-r--r--roms/u-boot/test/py/tests/test_efi_capsule/test_capsule_firmware.py249
-rw-r--r--roms/u-boot/test/py/tests/test_efi_capsule/uboot_bin_env.its36
-rw-r--r--roms/u-boot/test/py/tests/test_efi_fit.py458
-rw-r--r--roms/u-boot/test/py/tests/test_efi_loader.py204
-rw-r--r--roms/u-boot/test/py/tests/test_efi_secboot/conftest.py239
-rw-r--r--roms/u-boot/test/py/tests/test_efi_secboot/defs.py14
-rw-r--r--roms/u-boot/test/py/tests/test_efi_secboot/openssl.cnf48
-rw-r--r--roms/u-boot/test/py/tests/test_efi_secboot/test_authvar.py281
-rw-r--r--roms/u-boot/test/py/tests/test_efi_secboot/test_signed.py259
-rw-r--r--roms/u-boot/test/py/tests/test_efi_secboot/test_signed_intca.py135
-rw-r--r--roms/u-boot/test/py/tests/test_efi_secboot/test_unsigned.py117
-rw-r--r--roms/u-boot/test/py/tests/test_efi_selftest.py214
-rw-r--r--roms/u-boot/test/py/tests/test_env.py517
-rw-r--r--roms/u-boot/test/py/tests/test_extension.py53
-rwxr-xr-xroms/u-boot/test/py/tests/test_fit.py460
-rw-r--r--roms/u-boot/test/py/tests/test_fit_ecdsa.py111
-rw-r--r--roms/u-boot/test/py/tests/test_fpga.py565
-rw-r--r--roms/u-boot/test/py/tests/test_fs/conftest.py662
-rw-r--r--roms/u-boot/test/py/tests/test_fs/fstest_defs.py16
-rw-r--r--roms/u-boot/test/py/tests/test_fs/fstest_helpers.py13
-rw-r--r--roms/u-boot/test/py/tests/test_fs/test_basic.py292
-rw-r--r--roms/u-boot/test/py/tests/test_fs/test_ext.py319
-rw-r--r--roms/u-boot/test/py/tests/test_fs/test_fs_cmd.py13
-rw-r--r--roms/u-boot/test/py/tests/test_fs/test_mkdir.py121
-rw-r--r--roms/u-boot/test/py/tests/test_fs/test_squashfs/sqfs_common.py76
-rw-r--r--roms/u-boot/test/py/tests/test_fs/test_squashfs/test_sqfs_load.py46
-rw-r--r--roms/u-boot/test/py/tests/test_fs/test_squashfs/test_sqfs_ls.py36
-rw-r--r--roms/u-boot/test/py/tests/test_fs/test_symlink.py130
-rw-r--r--roms/u-boot/test/py/tests/test_fs/test_unlink.py118
-rw-r--r--roms/u-boot/test/py/tests/test_gpio.py37
-rw-r--r--roms/u-boot/test/py/tests/test_gpt.py178
-rw-r--r--roms/u-boot/test/py/tests/test_handoff.py15
-rw-r--r--roms/u-boot/test/py/tests/test_help.py8
-rw-r--r--roms/u-boot/test/py/tests/test_hush_if_test.py192
-rw-r--r--roms/u-boot/test/py/tests/test_log.py48
-rw-r--r--roms/u-boot/test/py/tests/test_lsblk.py13
-rw-r--r--roms/u-boot/test/py/tests/test_md.py36
-rw-r--r--roms/u-boot/test/py/tests/test_mmc_rd.py286
-rw-r--r--roms/u-boot/test/py/tests/test_mmc_wr.py105
-rw-r--r--roms/u-boot/test/py/tests/test_net.py208
-rw-r--r--roms/u-boot/test/py/tests/test_ofplatdata.py23
-rw-r--r--roms/u-boot/test/py/tests/test_part.py14
-rw-r--r--roms/u-boot/test/py/tests/test_pinmux.py84
-rw-r--r--roms/u-boot/test/py/tests/test_pstore.py77
-rw-r--r--roms/u-boot/test/py/tests/test_pstore_data_console.hexbin0 -> 4096 bytes
-rw-r--r--roms/u-boot/test/py/tests/test_pstore_data_panic1.hexbin0 -> 4096 bytes
-rw-r--r--roms/u-boot/test/py/tests/test_pstore_data_panic2.hexbin0 -> 4096 bytes
-rw-r--r--roms/u-boot/test/py/tests/test_qfw.py26
-rw-r--r--roms/u-boot/test/py/tests/test_sandbox_exit.py45
-rw-r--r--roms/u-boot/test/py/tests/test_scp03.py27
-rw-r--r--roms/u-boot/test/py/tests/test_sf.py217
-rw-r--r--roms/u-boot/test/py/tests/test_shell_basics.py45
-rw-r--r--roms/u-boot/test/py/tests/test_sleep.py43
-rw-r--r--roms/u-boot/test/py/tests/test_spl.py34
-rw-r--r--roms/u-boot/test/py/tests/test_stackprotector.py14
-rw-r--r--roms/u-boot/test/py/tests/test_tpm2.py233
-rw-r--r--roms/u-boot/test/py/tests/test_ums.py236
-rw-r--r--roms/u-boot/test/py/tests/test_unknown_cmd.py13
-rw-r--r--roms/u-boot/test/py/tests/test_ut.py43
-rw-r--r--roms/u-boot/test/py/tests/test_vboot.py401
-rw-r--r--roms/u-boot/test/py/tests/vboot/sandbox-kernel.dts7
-rw-r--r--roms/u-boot/test/py/tests/vboot/sandbox-u-boot.dts10
-rw-r--r--roms/u-boot/test/py/tests/vboot/sign-configs-sha1-pss.its46
-rw-r--r--roms/u-boot/test/py/tests/vboot/sign-configs-sha1.its45
-rw-r--r--roms/u-boot/test/py/tests/vboot/sign-configs-sha256-pss-prod.its46
-rw-r--r--roms/u-boot/test/py/tests/vboot/sign-configs-sha256-pss.its46
-rw-r--r--roms/u-boot/test/py/tests/vboot/sign-configs-sha256.its45
-rw-r--r--roms/u-boot/test/py/tests/vboot/sign-images-sha1-pss.its44
-rw-r--r--roms/u-boot/test/py/tests/vboot/sign-images-sha1.its42
-rw-r--r--roms/u-boot/test/py/tests/vboot/sign-images-sha256-pss.its44
-rw-r--r--roms/u-boot/test/py/tests/vboot/sign-images-sha256.its42
-rw-r--r--roms/u-boot/test/py/tests/vboot_evil.py485
-rw-r--r--roms/u-boot/test/py/tests/vboot_forge.py423
83 files changed, 10868 insertions, 0 deletions
diff --git a/roms/u-boot/test/py/tests/test_000_version.py b/roms/u-boot/test/py/tests/test_000_version.py
new file mode 100644
index 000000000..bd089ab54
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_000_version.py
@@ -0,0 +1,19 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2015 Stephen Warren
+# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
+
+# pytest runs tests the order of their module path, which is related to the
+# filename containing the test. This file is named such that it is sorted
+# first, simply as a very basic sanity check of the functionality of the U-Boot
+# command prompt.
+
+def test_version(u_boot_console):
+ """Test that the "version" command prints the U-Boot version."""
+
+ # "version" prints the U-Boot sign-on message. This is usually considered
+ # an error, so that any unexpected reboot causes an error. Here, this
+ # error detection is disabled since the sign-on message is expected.
+ with u_boot_console.disable_check('main_signon'):
+ response = u_boot_console.run_command('version')
+ # Ensure "version" printed what we expected.
+ u_boot_console.validate_version_string_in_text(response)
diff --git a/roms/u-boot/test/py/tests/test_android/test_ab.py b/roms/u-boot/test/py/tests/test_android/test_ab.py
new file mode 100644
index 000000000..c79cb07fd
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_android/test_ab.py
@@ -0,0 +1,75 @@
+# SPDX-License-Identifier: GPL-2.0
+# (C) Copyright 2018 Texas Instruments, <www.ti.com>
+
+# Test A/B update commands.
+
+import os
+import pytest
+import u_boot_utils
+
+class ABTestDiskImage(object):
+ """Disk Image used by the A/B tests."""
+
+ def __init__(self, u_boot_console):
+ """Initialize a new ABTestDiskImage object.
+
+ Args:
+ u_boot_console: A U-Boot console.
+
+ Returns:
+ Nothing.
+ """
+
+ filename = 'test_ab_disk_image.bin'
+
+ persistent = u_boot_console.config.persistent_data_dir + '/' + filename
+ self.path = u_boot_console.config.result_dir + '/' + filename
+
+ with u_boot_utils.persistent_file_helper(u_boot_console.log, persistent):
+ if os.path.exists(persistent):
+ u_boot_console.log.action('Disk image file ' + persistent +
+ ' already exists')
+ else:
+ u_boot_console.log.action('Generating ' + persistent)
+ fd = os.open(persistent, os.O_RDWR | os.O_CREAT)
+ os.ftruncate(fd, 524288)
+ os.close(fd)
+ cmd = ('sgdisk', persistent)
+ u_boot_utils.run_and_log(u_boot_console, cmd)
+
+ cmd = ('sgdisk', '--new=1:64:512', '--change-name=1:misc',
+ persistent)
+ u_boot_utils.run_and_log(u_boot_console, cmd)
+ cmd = ('sgdisk', '--load-backup=' + persistent)
+ u_boot_utils.run_and_log(u_boot_console, cmd)
+
+ cmd = ('cp', persistent, self.path)
+ u_boot_utils.run_and_log(u_boot_console, cmd)
+
+di = None
+@pytest.fixture(scope='function')
+def ab_disk_image(u_boot_console):
+ global di
+ if not di:
+ di = ABTestDiskImage(u_boot_console)
+ return di
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('android_ab')
+@pytest.mark.buildconfigspec('cmd_ab_select')
+@pytest.mark.requiredtool('sgdisk')
+def test_ab(ab_disk_image, u_boot_console):
+ """Test the 'ab_select' command."""
+
+ u_boot_console.run_command('host bind 0 ' + ab_disk_image.path)
+
+ output = u_boot_console.run_command('ab_select slot_name host 0#misc')
+ assert 're-initializing A/B metadata' in output
+ assert 'Attempting slot a, tries remaining 7' in output
+ output = u_boot_console.run_command('printenv slot_name')
+ assert 'slot_name=a' in output
+
+ output = u_boot_console.run_command('ab_select slot_name host 0:1')
+ assert 'Attempting slot b, tries remaining 7' in output
+ output = u_boot_console.run_command('printenv slot_name')
+ assert 'slot_name=b' in output
diff --git a/roms/u-boot/test/py/tests/test_android/test_abootimg.py b/roms/u-boot/test/py/tests/test_android/test_abootimg.py
new file mode 100644
index 000000000..43a7099c4
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_android/test_abootimg.py
@@ -0,0 +1,159 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2020
+# Author: Sam Protsenko <joe.skb7@gmail.com>
+
+# Test U-Boot's "abootimg" commands.
+
+import os
+import pytest
+import u_boot_utils
+
+"""
+These tests rely on disk image (boot.img), which is automatically created by
+the test from the stored hex dump. This is done to avoid the dependency on the
+most recent mkbootimg tool from AOSP/master. Here is the list of commands which
+was used to generate the boot.img and obtain compressed hex dump from it:
+
+ $ echo '/dts-v1/; / { model = "x1"; compatible = "y1,z1"; };' > test1.dts
+ $ echo '/dts-v1/; / { model = "x2"; compatible = "y2,z2"; };' > test2.dts
+ $ dtc test1.dts > dt1.dtb
+ $ dtc test2.dts > dt2.dtb
+ $ cat dt1.dtb dt2.dtb > dtb.img
+ $ echo 'kernel payload' > kernel
+ $ echo 'ramdisk payload' > ramdisk.img
+ $ mkbootimg --kernel ./kernel --ramdisk ./ramdisk.img \
+ --cmdline "cmdline test" --dtb ./dtb.img \
+ --os_version R --os_patch_level 2019-06-05 \
+ --header_version 2 --output boot.img
+ $ gzip -9 boot.img
+ $ xxd -p boot.img.gz > boot.img.gz.hex
+
+Now one can obtain original boot.img from this hex dump like this:
+
+ $ xxd -r -p boot.img.gz.hex boot.img.gz
+ $ gunzip -9 boot.img.gz
+"""
+
+# boot.img.gz hex dump
+img_hex = """1f8b08084844af5d0203626f6f742e696d670073f47309f2f77451e46700
+820606010106301084501f04181819041838181898803c3346060c909c9b
+92939997aa50925a5cc2300a461c3078b2e1793c4b876fd92db97939fb6c
+b7762ffff07d345446c1281805e8a0868d81e117a45e111c0d8dc101b253
+8bf25273140a122b73f21353b8460364148c8251300a46c1281801a02831
+3725b3387bb401300a46c1281805a360148c207081f7df5b20550bc41640
+9c03c41a0c90f17fe85400986d82452b6c3680198a192a0ce17c3610ae34
+d4a9820881a70f3873f35352731892f3730b124b32937252a96bb9119ae5
+463a5546f82c1f05a360148c8251300a462e000085bf67f200200000"""
+# Expected response for "abootimg dtb_dump" command
+dtb_dump_resp="""## DTB area contents (concat format):
+ - DTB #0:
+ (DTB)size = 125
+ (DTB)model = x1
+ (DTB)compatible = y1,z1
+ - DTB #1:
+ (DTB)size = 125
+ (DTB)model = x2
+ (DTB)compatible = y2,z2"""
+# Address in RAM where to load the boot image ('abootimg' looks in $loadaddr)
+loadaddr = 0x1000
+# Expected DTB #1 offset from the boot image start address
+dtb1_offset = 0x187d
+# DTB #1 start address in RAM
+dtb1_addr = loadaddr + dtb1_offset
+
+class AbootimgTestDiskImage(object):
+ """Disk image used by abootimg tests."""
+
+ def __init__(self, u_boot_console):
+ """Initialize a new AbootimgDiskImage object.
+
+ Args:
+ u_boot_console: A U-Boot console.
+
+ Returns:
+ Nothing.
+ """
+
+ gz_hex = u_boot_console.config.persistent_data_dir + '/boot.img.gz.hex'
+ gz = u_boot_console.config.persistent_data_dir + '/boot.img.gz'
+
+ filename = 'boot.img'
+ persistent = u_boot_console.config.persistent_data_dir + '/' + filename
+ self.path = u_boot_console.config.result_dir + '/' + filename
+
+ with u_boot_utils.persistent_file_helper(u_boot_console.log, persistent):
+ if os.path.exists(persistent):
+ u_boot_console.log.action('Disk image file ' + persistent +
+ ' already exists')
+ else:
+ u_boot_console.log.action('Generating ' + persistent)
+
+ f = open(gz_hex, "w")
+ f.write(img_hex)
+ f.close()
+
+ cmd = ('xxd', '-r', '-p', gz_hex, gz)
+ u_boot_utils.run_and_log(u_boot_console, cmd)
+
+ cmd = ('gunzip', '-9', gz)
+ u_boot_utils.run_and_log(u_boot_console, cmd)
+
+ cmd = ('cp', persistent, self.path)
+ u_boot_utils.run_and_log(u_boot_console, cmd)
+
+gtdi = None
+@pytest.fixture(scope='function')
+def abootimg_disk_image(u_boot_console):
+ """pytest fixture to provide a AbootimgTestDiskImage object to tests.
+ This is function-scoped because it uses u_boot_console, which is also
+ function-scoped. However, we don't need to actually do any function-scope
+ work, so this simply returns the same object over and over each time."""
+
+ global gtdi
+ if not gtdi:
+ gtdi = AbootimgTestDiskImage(u_boot_console)
+ return gtdi
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('android_boot_image')
+@pytest.mark.buildconfigspec('cmd_abootimg')
+@pytest.mark.buildconfigspec('cmd_fdt')
+@pytest.mark.requiredtool('xxd')
+@pytest.mark.requiredtool('gunzip')
+def test_abootimg(abootimg_disk_image, u_boot_console):
+ """Test the 'abootimg' command."""
+
+ u_boot_console.log.action('Loading disk image to RAM...')
+ u_boot_console.run_command('setenv loadaddr 0x%x' % (loadaddr))
+ u_boot_console.run_command('host load hostfs - 0x%x %s' % (loadaddr,
+ abootimg_disk_image.path))
+
+ u_boot_console.log.action('Testing \'abootimg get ver\'...')
+ response = u_boot_console.run_command('abootimg get ver')
+ assert response == "2"
+ u_boot_console.run_command('abootimg get ver v')
+ response = u_boot_console.run_command('env print v')
+ assert response == 'v=2'
+
+ u_boot_console.log.action('Testing \'abootimg get recovery_dtbo\'...')
+ response = u_boot_console.run_command('abootimg get recovery_dtbo a')
+ assert response == 'Error: recovery_dtbo_size is 0'
+
+ u_boot_console.log.action('Testing \'abootimg dump dtb\'...')
+ response = u_boot_console.run_command('abootimg dump dtb').replace('\r', '')
+ assert response == dtb_dump_resp
+
+ u_boot_console.log.action('Testing \'abootimg get dtb_load_addr\'...')
+ u_boot_console.run_command('abootimg get dtb_load_addr a')
+ response = u_boot_console.run_command('env print a')
+ assert response == 'a=11f00000'
+
+ u_boot_console.log.action('Testing \'abootimg get dtb --index\'...')
+ u_boot_console.run_command('abootimg get dtb --index=1 dtb1_start')
+ response = u_boot_console.run_command('env print dtb1_start')
+ correct_str = "dtb1_start=%x" % (dtb1_addr)
+ assert response == correct_str
+ u_boot_console.run_command('fdt addr $dtb1_start')
+ u_boot_console.run_command('fdt get value v / model')
+ response = u_boot_console.run_command('env print v')
+ assert response == 'v=x2'
diff --git a/roms/u-boot/test/py/tests/test_android/test_avb.py b/roms/u-boot/test/py/tests/test_android/test_avb.py
new file mode 100644
index 000000000..a04a7ff26
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_android/test_avb.py
@@ -0,0 +1,137 @@
+# Copyright (c) 2018, Linaro Limited
+#
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Android Verified Boot 2.0 Test
+
+"""
+This tests Android Verified Boot 2.0 support in U-boot:
+
+For additional details about how to build proper vbmeta partition
+check doc/android/avb2.rst
+
+For configuration verification:
+- Corrupt boot partition and check for failure
+- Corrupt vbmeta partition and check for failure
+"""
+
+import pytest
+import u_boot_utils as util
+
+# defauld mmc id
+mmc_dev = 1
+temp_addr = 0x90000000
+temp_addr2 = 0x90002000
+
+@pytest.mark.buildconfigspec('cmd_avb')
+@pytest.mark.buildconfigspec('cmd_mmc')
+def test_avb_verify(u_boot_console):
+ """Run AVB 2.0 boot verification chain with avb subset of commands
+ """
+
+ success_str = "Verification passed successfully"
+
+ response = u_boot_console.run_command('avb init %s' %str(mmc_dev))
+ assert response == ''
+ response = u_boot_console.run_command('avb verify')
+ assert response.find(success_str)
+
+
+@pytest.mark.buildconfigspec('cmd_avb')
+@pytest.mark.buildconfigspec('cmd_mmc')
+def test_avb_mmc_uuid(u_boot_console):
+ """Check if 'avb get_uuid' works, compare results with
+ 'part list mmc 1' output
+ """
+
+ response = u_boot_console.run_command('avb init %s' % str(mmc_dev))
+ assert response == ''
+
+ response = u_boot_console.run_command('mmc rescan; mmc dev %s' %
+ str(mmc_dev))
+ assert response.find('is current device')
+
+ part_lines = u_boot_console.run_command('mmc part').splitlines()
+ part_list = {}
+ cur_partname = ''
+
+ for line in part_lines:
+ if '"' in line:
+ start_pt = line.find('"')
+ end_pt = line.find('"', start_pt + 1)
+ cur_partname = line[start_pt + 1: end_pt]
+
+ if 'guid:' in line:
+ guid_to_check = line.split('guid:\t')
+ part_list[cur_partname] = guid_to_check[1]
+
+ # lets check all guids with avb get_guid
+ for part, guid in part_list.iteritems():
+ avb_guid_resp = u_boot_console.run_command('avb get_uuid %s' % part)
+ assert guid == avb_guid_resp.split('UUID: ')[1]
+
+
+@pytest.mark.buildconfigspec('cmd_avb')
+def test_avb_read_rb(u_boot_console):
+ """Test reading rollback indexes
+ """
+
+ response = u_boot_console.run_command('avb init %s' % str(mmc_dev))
+ assert response == ''
+
+ response = u_boot_console.run_command('avb read_rb 1')
+ assert response == 'Rollback index: 0'
+
+
+@pytest.mark.buildconfigspec('cmd_avb')
+def test_avb_is_unlocked(u_boot_console):
+ """Test if device is in the unlocked state
+ """
+
+ response = u_boot_console.run_command('avb init %s' % str(mmc_dev))
+ assert response == ''
+
+ response = u_boot_console.run_command('avb is_unlocked')
+ assert response == 'Unlocked = 1'
+
+
+@pytest.mark.buildconfigspec('cmd_avb')
+@pytest.mark.buildconfigspec('cmd_mmc')
+def test_avb_mmc_read(u_boot_console):
+ """Test mmc read operation
+ """
+
+ response = u_boot_console.run_command('mmc rescan; mmc dev %s 0' %
+ str(mmc_dev))
+ assert response.find('is current device')
+
+ response = u_boot_console.run_command('mmc read 0x%x 0x100 0x1' % temp_addr)
+ assert response.find('read: OK')
+
+ response = u_boot_console.run_command('avb init %s' % str(mmc_dev))
+ assert response == ''
+
+ response = u_boot_console.run_command('avb read_part xloader 0 100 0x%x' %
+ temp_addr2)
+ assert response.find('Read 512 bytes')
+
+ # Now lets compare two buffers
+ response = u_boot_console.run_command('cmp 0x%x 0x%x 40' %
+ (temp_addr, temp_addr2))
+ assert response.find('64 word')
+
+
+@pytest.mark.buildconfigspec('cmd_avb')
+@pytest.mark.buildconfigspec('optee_ta_avb')
+def test_avb_persistent_values(u_boot_console):
+ """Test reading/writing persistent storage to avb
+ """
+
+ response = u_boot_console.run_command('avb init %s' % str(mmc_dev))
+ assert response == ''
+
+ response = u_boot_console.run_command('avb write_pvalue test value_value')
+ assert response == 'Wrote 12 bytes'
+
+ response = u_boot_console.run_command('avb read_pvalue test 12')
+ assert response == 'Read 12 bytes, value = value_value'
diff --git a/roms/u-boot/test/py/tests/test_bind.py b/roms/u-boot/test/py/tests/test_bind.py
new file mode 100644
index 000000000..6703325c0
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_bind.py
@@ -0,0 +1,179 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
+
+import os.path
+import pytest
+import re
+
+def in_tree(response, name, uclass, drv, depth, last_child):
+ lines = [x.strip() for x in response.splitlines()]
+ leaf = ''
+ if depth != 0:
+ leaf = ' ' + ' ' * (depth - 1) ;
+ if not last_child:
+ leaf = leaf + r'\|'
+ else:
+ leaf = leaf + '`'
+
+ leaf = leaf + '-- ' + name
+ line = (r' *{:10.10} *[0-9]* \[ [ +] \] {:20.20} [` |]{}$'
+ .format(uclass, drv, leaf))
+ prog = re.compile(line)
+ for l in lines:
+ if prog.match(l):
+ return True
+ return False
+
+
+@pytest.mark.buildconfigspec('cmd_bind')
+def test_bind_unbind_with_node(u_boot_console):
+
+ tree = u_boot_console.run_command('dm tree')
+ assert in_tree(tree, 'bind-test', 'simple_bus', 'simple_bus', 0, True)
+ assert in_tree(tree, 'bind-test-child1', 'phy', 'phy_sandbox', 1, False)
+ assert in_tree(tree, 'bind-test-child2', 'simple_bus', 'simple_bus', 1, True)
+
+ #Unbind child #1. No error expected and all devices should be there except for bind-test-child1
+ response = u_boot_console.run_command('unbind /bind-test/bind-test-child1')
+ assert response == ''
+ tree = u_boot_console.run_command('dm tree')
+ assert in_tree(tree, 'bind-test', 'simple_bus', 'simple_bus', 0, True)
+ assert 'bind-test-child1' not in tree
+ assert in_tree(tree, 'bind-test-child2', 'simple_bus', 'simple_bus', 1, True)
+
+ #bind child #1. No error expected and all devices should be there
+ response = u_boot_console.run_command('bind /bind-test/bind-test-child1 phy_sandbox')
+ assert response == ''
+ tree = u_boot_console.run_command('dm tree')
+ assert in_tree(tree, 'bind-test', 'simple_bus', 'simple_bus', 0, True)
+ assert in_tree(tree, 'bind-test-child1', 'phy', 'phy_sandbox', 1, True)
+ assert in_tree(tree, 'bind-test-child2', 'simple_bus', 'simple_bus', 1, False)
+
+ #Unbind child #2. No error expected and all devices should be there except for bind-test-child2
+ response = u_boot_console.run_command('unbind /bind-test/bind-test-child2')
+ assert response == ''
+ tree = u_boot_console.run_command('dm tree')
+ assert in_tree(tree, 'bind-test', 'simple_bus', 'simple_bus', 0, True)
+ assert in_tree(tree, 'bind-test-child1', 'phy', 'phy_sandbox', 1, True)
+ assert 'bind-test-child2' not in tree
+
+
+ #Bind child #2. No error expected and all devices should be there
+ response = u_boot_console.run_command('bind /bind-test/bind-test-child2 simple_bus')
+ assert response == ''
+ tree = u_boot_console.run_command('dm tree')
+ assert in_tree(tree, 'bind-test', 'simple_bus', 'simple_bus', 0, True)
+ assert in_tree(tree, 'bind-test-child1', 'phy', 'phy_sandbox', 1, False)
+ assert in_tree(tree, 'bind-test-child2', 'simple_bus', 'simple_bus', 1, True)
+
+ #Unbind parent. No error expected. All devices should be removed and unbound
+ response = u_boot_console.run_command('unbind /bind-test')
+ assert response == ''
+ tree = u_boot_console.run_command('dm tree')
+ assert 'bind-test' not in tree
+ assert 'bind-test-child1' not in tree
+ assert 'bind-test-child2' not in tree
+
+ #try binding invalid node with valid driver
+ response = u_boot_console.run_command('bind /not-a-valid-node simple_bus')
+ assert response != ''
+ tree = u_boot_console.run_command('dm tree')
+ assert 'not-a-valid-node' not in tree
+
+ #try binding valid node with invalid driver
+ response = u_boot_console.run_command('bind /bind-test not_a_driver')
+ assert response != ''
+ tree = u_boot_console.run_command('dm tree')
+ assert 'bind-test' not in tree
+
+ #bind /bind-test. Device should come up as well as its children
+ response = u_boot_console.run_command('bind /bind-test simple_bus')
+ assert response == ''
+ tree = u_boot_console.run_command('dm tree')
+ assert in_tree(tree, 'bind-test', 'simple_bus', 'simple_bus', 0, True)
+ assert in_tree(tree, 'bind-test-child1', 'phy', 'phy_sandbox', 1, False)
+ assert in_tree(tree, 'bind-test-child2', 'simple_bus', 'simple_bus', 1, True)
+
+ response = u_boot_console.run_command('unbind /bind-test')
+ assert response == ''
+
+def get_next_line(tree, name):
+ treelines = [x.strip() for x in tree.splitlines() if x.strip()]
+ child_line = ''
+ for idx, line in enumerate(treelines):
+ if ('-- ' + name) in line:
+ try:
+ child_line = treelines[idx+1]
+ except:
+ pass
+ break
+ return child_line
+
+@pytest.mark.buildconfigspec('cmd_bind')
+def test_bind_unbind_with_uclass(u_boot_console):
+ #bind /bind-test
+ response = u_boot_console.run_command('bind /bind-test simple_bus')
+ assert response == ''
+
+ #make sure bind-test-child2 is there and get its uclass/index pair
+ tree = u_boot_console.run_command('dm tree')
+ child2_line = [x.strip() for x in tree.splitlines() if '-- bind-test-child2' in x]
+ assert len(child2_line) == 1
+
+ child2_uclass = child2_line[0].split()[0]
+ child2_index = int(child2_line[0].split()[1])
+
+ #bind simple_bus as a child of bind-test-child2
+ response = u_boot_console.run_command('bind {} {} simple_bus'.format(child2_uclass, child2_index, 'simple_bus'))
+
+ #check that the child is there and its uclass/index pair is right
+ tree = u_boot_console.run_command('dm tree')
+
+ child_of_child2_line = get_next_line(tree, 'bind-test-child2')
+ assert child_of_child2_line
+ child_of_child2_index = int(child_of_child2_line.split()[1])
+ assert in_tree(tree, 'simple_bus', 'simple_bus', 'simple_bus', 2, True)
+ assert child_of_child2_index == child2_index + 1
+
+ #unbind the child and check it has been removed
+ response = u_boot_console.run_command('unbind simple_bus {}'.format(child_of_child2_index))
+ assert response == ''
+ tree = u_boot_console.run_command('dm tree')
+ assert in_tree(tree, 'bind-test-child2', 'simple_bus', 'simple_bus', 1, True)
+ assert not in_tree(tree, 'simple_bus', 'simple_bus', 'simple_bus', 2, True)
+ child_of_child2_line = get_next_line(tree, 'bind-test-child2')
+ assert child_of_child2_line == ''
+
+ #bind simple_bus as a child of bind-test-child2
+ response = u_boot_console.run_command('bind {} {} simple_bus'.format(child2_uclass, child2_index, 'simple_bus'))
+
+ #check that the child is there and its uclass/index pair is right
+ tree = u_boot_console.run_command('dm tree')
+ treelines = [x.strip() for x in tree.splitlines() if x.strip()]
+
+ child_of_child2_line = get_next_line(tree, 'bind-test-child2')
+ assert child_of_child2_line
+ child_of_child2_index = int(child_of_child2_line.split()[1])
+ assert in_tree(tree, 'simple_bus', 'simple_bus', 'simple_bus', 2, True)
+ assert child_of_child2_index == child2_index + 1
+
+ #unbind the child and check it has been removed
+ response = u_boot_console.run_command('unbind {} {} simple_bus'.format(child2_uclass, child2_index, 'simple_bus'))
+ assert response == ''
+
+ tree = u_boot_console.run_command('dm tree')
+ assert in_tree(tree, 'bind-test-child2', 'simple_bus', 'simple_bus', 1, True)
+
+ child_of_child2_line = get_next_line(tree, 'bind-test-child2')
+ assert child_of_child2_line == ''
+
+ #unbind the child again and check it doesn't change the tree
+ tree_old = u_boot_console.run_command('dm tree')
+ response = u_boot_console.run_command('unbind {} {} simple_bus'.format(child2_uclass, child2_index, 'simple_bus'))
+ tree_new = u_boot_console.run_command('dm tree')
+
+ assert response == ''
+ assert tree_old == tree_new
+
+ response = u_boot_console.run_command('unbind /bind-test')
+ assert response == ''
diff --git a/roms/u-boot/test/py/tests/test_button.py b/roms/u-boot/test/py/tests/test_button.py
new file mode 100644
index 000000000..3b7f148c8
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_button.py
@@ -0,0 +1,37 @@
+# SPDX-License-Identifier: GPL-2.0+
+
+import pytest
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('cmd_button')
+def test_button_list(u_boot_console):
+ """Test listing buttons"""
+
+ response = u_boot_console.run_command('button list; echo rc:$?')
+ assert('button1' in response)
+ assert('button2' in response)
+ assert('rc:0' in response)
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('cmd_button')
+@pytest.mark.buildconfigspec('cmd_gpio')
+def test_button_return_code(u_boot_console):
+ """Test correct reporting of the button status
+
+ The sandbox gpio driver reports the last output value as input value.
+ We can use this in our test to emulate different input statuses.
+ """
+
+ u_boot_console.run_command('gpio set a3; gpio input a3');
+ response = u_boot_console.run_command('button button1; echo rc:$?')
+ assert('on' in response)
+ assert('rc:0' in response)
+
+ u_boot_console.run_command('gpio clear a3; gpio input a3');
+ response = u_boot_console.run_command('button button1; echo rc:$?')
+ assert('off' in response)
+ assert('rc:1' in response)
+
+ response = u_boot_console.run_command('button nonexistent-button; echo rc:$?')
+ assert('not found' in response)
+ assert('rc:1' in response)
diff --git a/roms/u-boot/test/py/tests/test_dfu.py b/roms/u-boot/test/py/tests/test_dfu.py
new file mode 100644
index 000000000..5d87eb349
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_dfu.py
@@ -0,0 +1,320 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
+
+# Test U-Boot's "dfu" command. The test starts DFU in U-Boot, waits for USB
+# device enumeration on the host, executes dfu-util multiple times to test
+# various transfer sizes, many of which trigger USB driver edge cases, and
+# finally aborts the "dfu" command in U-Boot.
+
+import os
+import os.path
+import pytest
+import u_boot_utils
+
+"""
+Note: This test relies on:
+
+a) boardenv_* to contain configuration values to define which USB ports are
+available for testing. Without this, this test will be automatically skipped.
+For example:
+
+env__usb_dev_ports = (
+ {
+ 'fixture_id': 'micro_b',
+ 'tgt_usb_ctlr': '0',
+ 'host_usb_dev_node': '/dev/usbdev-p2371-2180',
+ # This parameter is optional /if/ you only have a single board
+ # attached to your host at a time.
+ 'host_usb_port_path': '3-13',
+ },
+)
+
+# Optional entries (required only when 'alt_id_test_file' and
+# 'alt_id_dummy_file' are specified).
+test_file_name = '/dfu_test.bin'
+dummy_file_name = '/dfu_dummy.bin'
+# Above files are used to generate proper 'alt_info' entry
+'alt_info': '/%s ext4 0 2;/%s ext4 0 2' % (test_file_name, dummy_file_name),
+
+env__dfu_configs = (
+ # eMMC, partition 1
+ {
+ 'fixture_id': 'emmc',
+ 'alt_info': '/dfu_test.bin ext4 0 1;/dfu_dummy.bin ext4 0 1',
+ 'cmd_params': 'mmc 0',
+ # This value is optional.
+ # If present, it specified the set of transfer sizes tested.
+ # If missing, a default list of sizes will be used, which covers
+ # various useful corner cases.
+ # Manually specifying test sizes is useful if you wish to test 4 DFU
+ # configurations, but don't want to test every single transfer size
+ # on each, to avoid bloating the overall time taken by testing.
+ 'test_sizes': (63, 64, 65),
+ # This value is optional.
+ # The name of the environment variable that the the dfu command reads
+ # alt info from. If unspecified, this defaults to dfu_alt_info, which is
+ # valid for most systems. Some systems use a different variable name.
+ # One example is the Odroid XU3, which automatically generates
+ # $dfu_alt_info, each time the dfu command is run, by concatenating
+ # $dfu_alt_boot and $dfu_alt_system.
+ 'alt_info_env_name': 'dfu_alt_system',
+ # This value is optional.
+ # For boards which require the 'test file' alt setting number other than
+ # default (0) it is possible to specify exact file name to be used as
+ # this parameter.
+ 'alt_id_test_file': test_file_name,
+ # This value is optional.
+ # For boards which require the 'dummy file' alt setting number other
+ # than default (1) it is possible to specify exact file name to be used
+ # as this parameter.
+ 'alt_id_dummy_file': dummy_file_name,
+ },
+)
+
+b) udev rules to set permissions on devices nodes, so that sudo is not
+required. For example:
+
+ACTION=="add", SUBSYSTEM=="block", SUBSYSTEMS=="usb", KERNELS=="3-13", MODE:="666"
+
+(You may wish to change the group ID instead of setting the permissions wide
+open. All that matters is that the user ID running the test can access the
+device.)
+
+c) An optional udev rule to give you a persistent value to use in
+host_usb_dev_node. For example:
+
+IMPORT{builtin}="path_id"
+ENV{ID_PATH}=="?*", ENV{.ID_PORT}=="", SYMLINK+="bus/usb/by-path/$env{ID_PATH}"
+ENV{ID_PATH}=="?*", ENV{.ID_PORT}=="?*", SYMLINK+="bus/usb/by-path/$env{ID_PATH}-port$env{.ID_PORT}"
+"""
+
+# The set of file sizes to test. These values trigger various edge-cases such
+# as one less than, equal to, and one greater than typical USB max packet
+# sizes, and similar boundary conditions.
+test_sizes_default = (
+ 64 - 1,
+ 64,
+ 64 + 1,
+ 128 - 1,
+ 128,
+ 128 + 1,
+ 960 - 1,
+ 960,
+ 960 + 1,
+ 4096 - 1,
+ 4096,
+ 4096 + 1,
+ 1024 * 1024 - 1,
+ 1024 * 1024,
+ 8 * 1024 * 1024,
+)
+
+first_usb_dev_port = None
+
+@pytest.mark.buildconfigspec('cmd_dfu')
+@pytest.mark.requiredtool('dfu-util')
+def test_dfu(u_boot_console, env__usb_dev_port, env__dfu_config):
+ """Test the "dfu" command; the host system must be able to enumerate a USB
+ device when "dfu" is running, various DFU transfers are tested, and the
+ USB device must disappear when "dfu" is aborted.
+
+ Args:
+ u_boot_console: A U-Boot console connection.
+ env__usb_dev_port: The single USB device-mode port specification on
+ which to run the test. See the file-level comment above for
+ details of the format.
+ env__dfu_config: The single DFU (memory region) configuration on which
+ to run the test. See the file-level comment above for details
+ of the format.
+
+ Returns:
+ Nothing.
+ """
+
+ def start_dfu():
+ """Start U-Boot's dfu shell command.
+
+ This also waits for the host-side USB enumeration process to complete.
+
+ Args:
+ None.
+
+ Returns:
+ Nothing.
+ """
+
+ u_boot_utils.wait_until_file_open_fails(
+ env__usb_dev_port['host_usb_dev_node'], True)
+ fh = u_boot_utils.attempt_to_open_file(
+ env__usb_dev_port['host_usb_dev_node'])
+ if fh:
+ fh.close()
+ raise Exception('USB device present before dfu command invoked')
+
+ u_boot_console.log.action(
+ 'Starting long-running U-Boot dfu shell command')
+
+ dfu_alt_info_env = env__dfu_config.get('alt_info_env_name', \
+ 'dfu_alt_info')
+
+ cmd = 'setenv "%s" "%s"' % (dfu_alt_info_env,
+ env__dfu_config['alt_info'])
+ u_boot_console.run_command(cmd)
+
+ cmd = 'dfu 0 ' + env__dfu_config['cmd_params']
+ u_boot_console.run_command(cmd, wait_for_prompt=False)
+ u_boot_console.log.action('Waiting for DFU USB device to appear')
+ fh = u_boot_utils.wait_until_open_succeeds(
+ env__usb_dev_port['host_usb_dev_node'])
+ fh.close()
+
+ def stop_dfu(ignore_errors):
+ """Stop U-Boot's dfu shell command from executing.
+
+ This also waits for the host-side USB de-enumeration process to
+ complete.
+
+ Args:
+ ignore_errors: Ignore any errors. This is useful if an error has
+ already been detected, and the code is performing best-effort
+ cleanup. In this case, we do not want to mask the original
+ error by "honoring" any new errors.
+
+ Returns:
+ Nothing.
+ """
+
+ try:
+ u_boot_console.log.action(
+ 'Stopping long-running U-Boot dfu shell command')
+ u_boot_console.ctrlc()
+ u_boot_console.log.action(
+ 'Waiting for DFU USB device to disappear')
+ u_boot_utils.wait_until_file_open_fails(
+ env__usb_dev_port['host_usb_dev_node'], ignore_errors)
+ except:
+ if not ignore_errors:
+ raise
+
+ def run_dfu_util(alt_setting, fn, up_dn_load_arg):
+ """Invoke dfu-util on the host.
+
+ Args:
+ alt_setting: The DFU "alternate setting" identifier to interact
+ with.
+ fn: The host-side file name to transfer.
+ up_dn_load_arg: '-U' or '-D' depending on whether a DFU upload or
+ download operation should be performed.
+
+ Returns:
+ Nothing.
+ """
+
+ cmd = ['dfu-util', '-a', alt_setting, up_dn_load_arg, fn]
+ if 'host_usb_port_path' in env__usb_dev_port:
+ cmd += ['-p', env__usb_dev_port['host_usb_port_path']]
+ u_boot_utils.run_and_log(u_boot_console, cmd)
+ u_boot_console.wait_for('Ctrl+C to exit ...')
+
+ def dfu_write(alt_setting, fn):
+ """Write a file to the target board using DFU.
+
+ Args:
+ alt_setting: The DFU "alternate setting" identifier to interact
+ with.
+ fn: The host-side file name to transfer.
+
+ Returns:
+ Nothing.
+ """
+
+ run_dfu_util(alt_setting, fn, '-D')
+
+ def dfu_read(alt_setting, fn):
+ """Read a file from the target board using DFU.
+
+ Args:
+ alt_setting: The DFU "alternate setting" identifier to interact
+ with.
+ fn: The host-side file name to transfer.
+
+ Returns:
+ Nothing.
+ """
+
+ # dfu-util fails reads/uploads if the host file already exists
+ if os.path.exists(fn):
+ os.remove(fn)
+ run_dfu_util(alt_setting, fn, '-U')
+
+ def dfu_write_read_check(size):
+ """Test DFU transfers of a specific size of data
+
+ This function first writes data to the board then reads it back and
+ compares the written and read back data. Measures are taken to avoid
+ certain types of false positives.
+
+ Args:
+ size: The data size to test.
+
+ Returns:
+ Nothing.
+ """
+
+ test_f = u_boot_utils.PersistentRandomFile(u_boot_console,
+ 'dfu_%d.bin' % size, size)
+ readback_fn = u_boot_console.config.result_dir + '/dfu_readback.bin'
+
+ u_boot_console.log.action('Writing test data to DFU primary ' +
+ 'altsetting')
+ dfu_write(alt_setting_test_file, test_f.abs_fn)
+
+ u_boot_console.log.action('Writing dummy data to DFU secondary ' +
+ 'altsetting to clear DFU buffers')
+ dfu_write(alt_setting_dummy_file, dummy_f.abs_fn)
+
+ u_boot_console.log.action('Reading DFU primary altsetting for ' +
+ 'comparison')
+ dfu_read(alt_setting_test_file, readback_fn)
+
+ u_boot_console.log.action('Comparing written and read data')
+ written_hash = test_f.content_hash
+ read_back_hash = u_boot_utils.md5sum_file(readback_fn, size)
+ assert(written_hash == read_back_hash)
+
+ # This test may be executed against multiple USB ports. The test takes a
+ # long time, so we don't want to do the whole thing each time. Instead,
+ # execute the full test on the first USB port, and perform a very limited
+ # test on other ports. In the limited case, we solely validate that the
+ # host PC can enumerate the U-Boot USB device.
+ global first_usb_dev_port
+ if not first_usb_dev_port:
+ first_usb_dev_port = env__usb_dev_port
+ if env__usb_dev_port == first_usb_dev_port:
+ sizes = env__dfu_config.get('test_sizes', test_sizes_default)
+ else:
+ sizes = []
+
+ dummy_f = u_boot_utils.PersistentRandomFile(u_boot_console,
+ 'dfu_dummy.bin', 1024)
+
+ alt_setting_test_file = env__dfu_config.get('alt_id_test_file', '0')
+ alt_setting_dummy_file = env__dfu_config.get('alt_id_dummy_file', '1')
+
+ ignore_cleanup_errors = True
+ try:
+ start_dfu()
+
+ u_boot_console.log.action(
+ 'Overwriting DFU primary altsetting with dummy data')
+ dfu_write(alt_setting_test_file, dummy_f.abs_fn)
+
+ for size in sizes:
+ with u_boot_console.log.section('Data size %d' % size):
+ dfu_write_read_check(size)
+ # Make the status of each sub-test obvious. If the test didn't
+ # pass, an exception was thrown so this code isn't executed.
+ u_boot_console.log.status_pass('OK')
+ ignore_cleanup_errors = False
+ finally:
+ stop_dfu(ignore_cleanup_errors)
diff --git a/roms/u-boot/test/py/tests/test_dm.py b/roms/u-boot/test/py/tests/test_dm.py
new file mode 100644
index 000000000..97203b536
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_dm.py
@@ -0,0 +1,35 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2020 Sean Anderson
+
+import pytest
+
+@pytest.mark.buildconfigspec('cmd_dm')
+def test_dm_compat(u_boot_console):
+ """Test that each driver in `dm tree` is also listed in `dm compat`."""
+ response = u_boot_console.run_command('dm tree')
+ driver_index = response.find('Driver')
+ assert driver_index != -1
+ drivers = (line[driver_index:].split()[0]
+ for line in response[:-1].split('\n')[2:])
+
+ response = u_boot_console.run_command('dm compat')
+ for driver in drivers:
+ assert driver in response
+
+@pytest.mark.buildconfigspec('cmd_dm')
+def test_dm_drivers(u_boot_console):
+ """Test that each driver in `dm compat` is also listed in `dm drivers`."""
+ response = u_boot_console.run_command('dm compat')
+ drivers = (line[:20].rstrip() for line in response[:-1].split('\n')[2:])
+ response = u_boot_console.run_command('dm drivers')
+ for driver in drivers:
+ assert driver in response
+
+@pytest.mark.buildconfigspec('cmd_dm')
+def test_dm_static(u_boot_console):
+ """Test that each driver in `dm static` is also listed in `dm drivers`."""
+ response = u_boot_console.run_command('dm static')
+ drivers = (line[:25].rstrip() for line in response[:-1].split('\n')[2:])
+ response = u_boot_console.run_command('dm drivers')
+ for driver in drivers:
+ assert driver in response
diff --git a/roms/u-boot/test/py/tests/test_efi_capsule/capsule_defs.py b/roms/u-boot/test/py/tests/test_efi_capsule/capsule_defs.py
new file mode 100644
index 000000000..4fd6353c2
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_efi_capsule/capsule_defs.py
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0+
+
+# Directories
+CAPSULE_DATA_DIR = '/EFI/CapsuleTestData'
+CAPSULE_INSTALL_DIR = '/EFI/UpdateCapsule'
diff --git a/roms/u-boot/test/py/tests/test_efi_capsule/conftest.py b/roms/u-boot/test/py/tests/test_efi_capsule/conftest.py
new file mode 100644
index 000000000..6ad5608cd
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_efi_capsule/conftest.py
@@ -0,0 +1,74 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2020, Linaro Limited
+# Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
+
+import os
+import os.path
+import re
+from subprocess import call, check_call, check_output, CalledProcessError
+import pytest
+from capsule_defs import *
+
+#
+# Fixture for UEFI secure boot test
+#
+
+
+@pytest.fixture(scope='session')
+def efi_capsule_data(request, u_boot_config):
+ """Set up a file system to be used in UEFI capsule test.
+
+ Args:
+ request: Pytest request object.
+ u_boot_config: U-boot configuration.
+
+ Return:
+ A path to disk image to be used for testing
+ """
+ global CAPSULE_DATA_DIR, CAPSULE_INSTALL_DIR
+
+ mnt_point = u_boot_config.persistent_data_dir + '/test_efi_capsule'
+ data_dir = mnt_point + CAPSULE_DATA_DIR
+ install_dir = mnt_point + CAPSULE_INSTALL_DIR
+ image_path = u_boot_config.persistent_data_dir + '/test_efi_capsule.img'
+
+ try:
+ # Create a target device
+ check_call('dd if=/dev/zero of=./spi.bin bs=1MiB count=16', shell=True)
+
+ check_call('rm -rf %s' % mnt_point, shell=True)
+ check_call('mkdir -p %s' % data_dir, shell=True)
+ check_call('mkdir -p %s' % install_dir, shell=True)
+
+ # Create capsule files
+ # two regions: one for u-boot.bin and the other for u-boot.env
+ check_call('cd %s; echo -n u-boot:Old > u-boot.bin.old; echo -n u-boot:New > u-boot.bin.new; echo -n u-boot-env:Old -> u-boot.env.old; echo -n u-boot-env:New > u-boot.env.new' % data_dir,
+ shell=True)
+ check_call('sed -e \"s?BINFILE1?u-boot.bin.new?\" -e \"s?BINFILE2?u-boot.env.new?\" %s/test/py/tests/test_efi_capsule/uboot_bin_env.its > %s/uboot_bin_env.its' %
+ (u_boot_config.source_dir, data_dir),
+ shell=True)
+ check_call('cd %s; %s/tools/mkimage -f uboot_bin_env.its uboot_bin_env.itb' %
+ (data_dir, u_boot_config.build_dir),
+ shell=True)
+ check_call('cd %s; %s/tools/mkeficapsule --fit uboot_bin_env.itb --index 1 Test01' %
+ (data_dir, u_boot_config.build_dir),
+ shell=True)
+ check_call('cd %s; %s/tools/mkeficapsule --raw u-boot.bin.new --index 1 Test02' %
+ (data_dir, u_boot_config.build_dir),
+ shell=True)
+
+ # Create a disk image with EFI system partition
+ check_call('virt-make-fs --partition=gpt --size=+1M --type=vfat %s %s' %
+ (mnt_point, image_path), shell=True)
+ check_call('sgdisk %s -A 1:set:0 -t 1:C12A7328-F81F-11D2-BA4B-00A0C93EC93B' %
+ image_path, shell=True)
+
+ except CalledProcessError as exception:
+ pytest.skip('Setup failed: %s' % exception.cmd)
+ return
+ else:
+ yield image_path
+ finally:
+ call('rm -rf %s' % mnt_point, shell=True)
+ call('rm -f %s' % image_path, shell=True)
+ call('rm -f ./spi.bin', shell=True)
diff --git a/roms/u-boot/test/py/tests/test_efi_capsule/test_capsule_firmware.py b/roms/u-boot/test/py/tests/test_efi_capsule/test_capsule_firmware.py
new file mode 100644
index 000000000..4697ca6f1
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_efi_capsule/test_capsule_firmware.py
@@ -0,0 +1,249 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2020, Linaro Limited
+# Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
+#
+# U-Boot UEFI: Firmware Update Test
+
+"""
+This test verifies capsule-on-disk firmware update
+"""
+
+from subprocess import check_call, check_output, CalledProcessError
+import pytest
+from capsule_defs import *
+
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('efi_capsule_firmware_fit')
+@pytest.mark.buildconfigspec('efi_capsule_firmware_raw')
+@pytest.mark.buildconfigspec('efi_capsule_on_disk')
+@pytest.mark.buildconfigspec('dfu')
+@pytest.mark.buildconfigspec('dfu_sf')
+@pytest.mark.buildconfigspec('cmd_efidebug')
+@pytest.mark.buildconfigspec('cmd_fat')
+@pytest.mark.buildconfigspec('cmd_memory')
+@pytest.mark.buildconfigspec('cmd_nvedit_efi')
+@pytest.mark.buildconfigspec('cmd_sf')
+@pytest.mark.slow
+class TestEfiCapsuleFirmwareFit(object):
+ def test_efi_capsule_fw1(
+ self, u_boot_config, u_boot_console, efi_capsule_data):
+ """
+ Test Case 1 - Update U-Boot and U-Boot environment on SPI Flash
+ but with OsIndications unset
+ No update should happen
+ 0x100000-0x150000: U-Boot binary (but dummy)
+ 0x150000-0x200000: U-Boot environment (but dummy)
+ """
+ disk_img = efi_capsule_data
+ with u_boot_console.log.section('Test Case 1-a, before reboot'):
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % disk_img,
+ 'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi -s ""',
+ 'efidebug boot order 1',
+ 'env set -e OsIndications',
+ 'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
+ 'env save'])
+
+ # initialize contents
+ output = u_boot_console.run_command_list([
+ 'sf probe 0:0',
+ 'fatload host 0:1 4000000 %s/u-boot.bin.old' % CAPSULE_DATA_DIR,
+ 'sf write 4000000 100000 10',
+ 'sf read 5000000 100000 10',
+ 'md.b 5000000 10'])
+ assert 'Old' in ''.join(output)
+ output = u_boot_console.run_command_list([
+ 'sf probe 0:0',
+ 'fatload host 0:1 4000000 %s/u-boot.env.old' % CAPSULE_DATA_DIR,
+ 'sf write 4000000 150000 10',
+ 'sf read 5000000 150000 10',
+ 'md.b 5000000 10'])
+ assert 'Old' in ''.join(output)
+
+ # place a capsule file
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 %s/Test01' % CAPSULE_DATA_DIR,
+ 'fatwrite host 0:1 4000000 %s/Test01 $filesize' % CAPSULE_INSTALL_DIR,
+ 'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
+ assert 'Test01' in ''.join(output)
+
+ # reboot
+ u_boot_console.restart_uboot()
+
+ capsule_early = u_boot_config.buildconfig.get(
+ 'config_efi_capsule_on_disk_early')
+ with u_boot_console.log.section('Test Case 1-b, after reboot'):
+ if not capsule_early:
+ # make sure that dfu_alt_info exists even persistent variables
+ # are not available.
+ output = u_boot_console.run_command_list([
+ 'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
+ 'host bind 0 %s' % disk_img,
+ 'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
+ assert 'Test01' in ''.join(output)
+
+ # need to run uefi command to initiate capsule handling
+ output = u_boot_console.run_command(
+ 'env print -e -all Capsule0000')
+
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % disk_img,
+ 'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
+ assert 'Test01' in ''.join(output)
+
+ output = u_boot_console.run_command_list([
+ 'sf probe 0:0',
+ 'sf read 4000000 100000 10',
+ 'md.b 4000000 10'])
+ assert 'u-boot:Old' in ''.join(output)
+
+ output = u_boot_console.run_command_list([
+ 'sf read 4000000 150000 10',
+ 'md.b 4000000 10'])
+ assert 'u-boot-env:Old' in ''.join(output)
+
+ def test_efi_capsule_fw2(
+ self, u_boot_config, u_boot_console, efi_capsule_data):
+ """
+ Test Case 2 - Update U-Boot and U-Boot environment on SPI Flash
+ 0x100000-0x150000: U-Boot binary (but dummy)
+ 0x150000-0x200000: U-Boot environment (but dummy)
+ """
+ disk_img = efi_capsule_data
+ with u_boot_console.log.section('Test Case 2-a, before reboot'):
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % disk_img,
+ 'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi -s ""',
+ 'efidebug boot order 1',
+ 'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
+ 'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
+ 'env save'])
+
+ # initialize contents
+ output = u_boot_console.run_command_list([
+ 'sf probe 0:0',
+ 'fatload host 0:1 4000000 %s/u-boot.bin.old' % CAPSULE_DATA_DIR,
+ 'sf write 4000000 100000 10',
+ 'sf read 5000000 100000 10',
+ 'md.b 5000000 10'])
+ assert 'Old' in ''.join(output)
+ output = u_boot_console.run_command_list([
+ 'sf probe 0:0',
+ 'fatload host 0:1 4000000 %s/u-boot.env.old' % CAPSULE_DATA_DIR,
+ 'sf write 4000000 150000 10',
+ 'sf read 5000000 150000 10',
+ 'md.b 5000000 10'])
+ assert 'Old' in ''.join(output)
+
+ # place a capsule file
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 %s/Test01' % CAPSULE_DATA_DIR,
+ 'fatwrite host 0:1 4000000 %s/Test01 $filesize' % CAPSULE_INSTALL_DIR,
+ 'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
+ assert 'Test01' in ''.join(output)
+
+ # reboot
+ u_boot_console.restart_uboot()
+
+ capsule_early = u_boot_config.buildconfig.get(
+ 'config_efi_capsule_on_disk_early')
+ with u_boot_console.log.section('Test Case 2-b, after reboot'):
+ if not capsule_early:
+ # make sure that dfu_alt_info exists even persistent variables
+ # are not available.
+ output = u_boot_console.run_command_list([
+ 'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
+ 'host bind 0 %s' % disk_img,
+ 'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
+ assert 'Test01' in ''.join(output)
+
+ # need to run uefi command to initiate capsule handling
+ output = u_boot_console.run_command(
+ 'env print -e -all Capsule0000')
+
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % disk_img,
+ 'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
+ assert 'Test01' not in ''.join(output)
+
+ output = u_boot_console.run_command_list([
+ 'sf probe 0:0',
+ 'sf read 4000000 100000 10',
+ 'md.b 4000000 10'])
+ assert 'u-boot:New' in ''.join(output)
+
+ output = u_boot_console.run_command_list([
+ 'sf read 4000000 150000 10',
+ 'md.b 4000000 10'])
+ assert 'u-boot-env:New' in ''.join(output)
+
+ def test_efi_capsule_fw3(
+ self, u_boot_config, u_boot_console, efi_capsule_data):
+ """
+ Test Case 3 - Update U-Boot on SPI Flash, raw image format
+ 0x100000-0x150000: U-Boot binary (but dummy)
+ """
+ disk_img = efi_capsule_data
+ with u_boot_console.log.section('Test Case 3-a, before reboot'):
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % disk_img,
+ 'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi -s ""',
+ 'efidebug boot order 1',
+ 'env set -e -nv -bs -rt OsIndications =0x0000000000000004',
+ 'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
+ 'env save'])
+
+ # initialize content
+ output = u_boot_console.run_command_list([
+ 'sf probe 0:0',
+ 'fatload host 0:1 4000000 %s/u-boot.bin.old' % CAPSULE_DATA_DIR,
+ 'sf write 4000000 100000 10',
+ 'sf read 5000000 100000 10',
+ 'md.b 5000000 10'])
+ assert 'Old' in ''.join(output)
+
+ # place a capsule file
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 %s/Test02' % CAPSULE_DATA_DIR,
+ 'fatwrite host 0:1 4000000 %s/Test02 $filesize' % CAPSULE_INSTALL_DIR,
+ 'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
+ assert 'Test02' in ''.join(output)
+
+ # reboot
+ u_boot_console.restart_uboot()
+
+ capsule_early = u_boot_config.buildconfig.get(
+ 'config_efi_capsule_on_disk_early')
+ with u_boot_console.log.section('Test Case 3-b, after reboot'):
+ if not capsule_early:
+ # make sure that dfu_alt_info exists even persistent variables
+ # are not available.
+ output = u_boot_console.run_command_list([
+ 'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
+ 'host bind 0 %s' % disk_img,
+ 'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
+ assert 'Test02' in ''.join(output)
+
+ # need to run uefi command to initiate capsule handling
+ output = u_boot_console.run_command(
+ 'env print -e -all Capsule0000')
+
+ output = u_boot_console.run_command_list(['efidebug capsule esrt'])
+
+ # ensure that EFI_FIRMWARE_IMAGE_TYPE_UBOOT_FIT_GUID is in the ESRT.
+ assert 'AE13FF2D-9AD4-4E25-9AC8-6D80B3B22147' in ''.join(output)
+
+ # ensure that EFI_FIRMWARE_IMAGE_TYPE_UBOOT_RAW_GUID is in the ESRT.
+ assert 'E2BB9C06-70E9-4B14-97A3-5A7913176E3F' in ''.join(output)
+
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % disk_img,
+ 'fatls host 0:1 %s' % CAPSULE_INSTALL_DIR])
+ assert 'Test02' not in ''.join(output)
+
+ output = u_boot_console.run_command_list([
+ 'sf probe 0:0',
+ 'sf read 4000000 100000 10',
+ 'md.b 4000000 10'])
+ assert 'u-boot:New' in ''.join(output)
diff --git a/roms/u-boot/test/py/tests/test_efi_capsule/uboot_bin_env.its b/roms/u-boot/test/py/tests/test_efi_capsule/uboot_bin_env.its
new file mode 100644
index 000000000..fc6590748
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_efi_capsule/uboot_bin_env.its
@@ -0,0 +1,36 @@
+/*
+ * Automatic software update for U-Boot
+ * Make sure the flashing addresses ('load' prop) is correct for your board!
+ */
+
+/dts-v1/;
+
+/ {
+ description = "Automatic U-Boot environment update";
+ #address-cells = <2>;
+
+ images {
+ u-boot-bin {
+ description = "U-Boot binary on SPI Flash";
+ data = /incbin/("BINFILE1");
+ compression = "none";
+ type = "firmware";
+ arch = "sandbox";
+ load = <0>;
+ hash-1 {
+ algo = "sha1";
+ };
+ };
+ u-boot-env {
+ description = "U-Boot environment on SPI Flash";
+ data = /incbin/("BINFILE2");
+ compression = "none";
+ type = "firmware";
+ arch = "sandbox";
+ load = <0>;
+ hash-1 {
+ algo = "sha1";
+ };
+ };
+ };
+};
diff --git a/roms/u-boot/test/py/tests/test_efi_fit.py b/roms/u-boot/test/py/tests/test_efi_fit.py
new file mode 100644
index 000000000..068a35a55
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_efi_fit.py
@@ -0,0 +1,458 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2019, Cristian Ciocaltea <cristian.ciocaltea@gmail.com>
+#
+# Work based on:
+# - test_net.py
+# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
+# - test_fit.py
+# Copyright (c) 2013, Google Inc.
+#
+# Test launching UEFI binaries from FIT images.
+
+"""
+Note: This test relies on boardenv_* containing configuration values to define
+which network environment is available for testing. Without this, the parts
+that rely on network will be automatically skipped.
+
+For example:
+
+# Boolean indicating whether the Ethernet device is attached to USB, and hence
+# USB enumeration needs to be performed prior to network tests.
+# This variable may be omitted if its value is False.
+env__net_uses_usb = False
+
+# Boolean indicating whether the Ethernet device is attached to PCI, and hence
+# PCI enumeration needs to be performed prior to network tests.
+# This variable may be omitted if its value is False.
+env__net_uses_pci = True
+
+# True if a DHCP server is attached to the network, and should be tested.
+# If DHCP testing is not possible or desired, this variable may be omitted or
+# set to False.
+env__net_dhcp_server = True
+
+# A list of environment variables that should be set in order to configure a
+# static IP. If solely relying on DHCP, this variable may be omitted or set to
+# an empty list.
+env__net_static_env_vars = [
+ ('ipaddr', '10.0.0.100'),
+ ('netmask', '255.255.255.0'),
+ ('serverip', '10.0.0.1'),
+]
+
+# Details regarding a file that may be read from a TFTP server. This variable
+# may be omitted or set to None if TFTP testing is not possible or desired.
+# Additionally, when the 'size' is not available, the file will be generated
+# automatically in the TFTP root directory, as specified by the 'dn' field.
+env__efi_fit_tftp_file = {
+ 'fn': 'test-efi-fit.img', # File path relative to TFTP root
+ 'size': 3831, # File size
+ 'crc32': '9fa3f79c', # Checksum using CRC-32 algorithm, optional
+ 'addr': 0x40400000, # Loading address, integer, optional
+ 'dn': 'tftp/root/dir', # TFTP root directory path, optional
+}
+"""
+
+import os.path
+import pytest
+import u_boot_utils as util
+
+# Define the parametrized ITS data to be used for FIT images generation.
+ITS_DATA = '''
+/dts-v1/;
+
+/ {
+ description = "EFI image with FDT blob";
+ #address-cells = <1>;
+
+ images {
+ efi {
+ description = "Test EFI";
+ data = /incbin/("%(efi-bin)s");
+ type = "%(kernel-type)s";
+ arch = "%(sys-arch)s";
+ os = "efi";
+ compression = "%(efi-comp)s";
+ load = <0x0>;
+ entry = <0x0>;
+ };
+ fdt {
+ description = "Test FDT";
+ data = /incbin/("%(fdt-bin)s");
+ type = "flat_dt";
+ arch = "%(sys-arch)s";
+ compression = "%(fdt-comp)s";
+ };
+ };
+
+ configurations {
+ default = "config-efi-fdt";
+ config-efi-fdt {
+ description = "EFI FIT w/ FDT";
+ kernel = "efi";
+ fdt = "fdt";
+ };
+ config-efi-nofdt {
+ description = "EFI FIT w/o FDT";
+ kernel = "efi";
+ };
+ };
+};
+'''
+
+# Define the parametrized FDT data to be used for DTB images generation.
+FDT_DATA = '''
+/dts-v1/;
+
+/ {
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ model = "%(sys-arch)s %(fdt_type)s EFI FIT Boot Test";
+ compatible = "%(sys-arch)s";
+
+ reset@0 {
+ compatible = "%(sys-arch)s,reset";
+ reg = <0 4>;
+ };
+};
+'''
+
+@pytest.mark.buildconfigspec('bootm_efi')
+@pytest.mark.buildconfigspec('cmd_bootefi_hello_compile')
+@pytest.mark.buildconfigspec('fit')
+@pytest.mark.notbuildconfigspec('generate_acpi_table')
+@pytest.mark.requiredtool('dtc')
+def test_efi_fit_launch(u_boot_console):
+ """Test handling of UEFI binaries inside FIT images.
+
+ The tests are trying to launch U-Boot's helloworld.efi embedded into
+ FIT images, in uncompressed or gzip compressed format.
+
+ Additionally, a sample FDT blob is created and embedded into the above
+ mentioned FIT images, in uncompressed or gzip compressed format.
+
+ For more details, see launch_efi().
+
+ The following test cases are currently defined and enabled:
+ - Launch uncompressed FIT EFI & internal FDT
+ - Launch uncompressed FIT EFI & FIT FDT
+ - Launch compressed FIT EFI & internal FDT
+ - Launch compressed FIT EFI & FIT FDT
+ """
+
+ def net_pre_commands():
+ """Execute any commands required to enable network hardware.
+
+ These commands are provided by the boardenv_* file; see the comment
+ at the beginning of this file.
+ """
+
+ init_usb = cons.config.env.get('env__net_uses_usb', False)
+ if init_usb:
+ cons.run_command('usb start')
+
+ init_pci = cons.config.env.get('env__net_uses_pci', False)
+ if init_pci:
+ cons.run_command('pci enum')
+
+ def net_dhcp():
+ """Execute the dhcp command.
+
+ The boardenv_* file may be used to enable/disable DHCP; see the
+ comment at the beginning of this file.
+ """
+
+ has_dhcp = cons.config.buildconfig.get('config_cmd_dhcp', 'n') == 'y'
+ if not has_dhcp:
+ cons.log.warning('CONFIG_CMD_DHCP != y: Skipping DHCP network setup')
+ return False
+
+ test_dhcp = cons.config.env.get('env__net_dhcp_server', False)
+ if not test_dhcp:
+ cons.log.info('No DHCP server available')
+ return False
+
+ cons.run_command('setenv autoload no')
+ output = cons.run_command('dhcp')
+ assert 'DHCP client bound to address ' in output
+ return True
+
+ def net_setup_static():
+ """Set up a static IP configuration.
+
+ The configuration is provided by the boardenv_* file; see the comment at
+ the beginning of this file.
+ """
+
+ has_dhcp = cons.config.buildconfig.get('config_cmd_dhcp', 'n') == 'y'
+ if not has_dhcp:
+ cons.log.warning('CONFIG_NET != y: Skipping static network setup')
+ return False
+
+ env_vars = cons.config.env.get('env__net_static_env_vars', None)
+ if not env_vars:
+ cons.log.info('No static network configuration is defined')
+ return False
+
+ for (var, val) in env_vars:
+ cons.run_command('setenv %s %s' % (var, val))
+ return True
+
+ def make_fpath(file_name):
+ """Compute the path of a given (temporary) file.
+
+ Args:
+ file_name: The name of a file within U-Boot build dir.
+ Return:
+ The computed file path.
+ """
+
+ return os.path.join(cons.config.build_dir, file_name)
+
+ def make_efi(fname, comp):
+ """Create an UEFI binary.
+
+ This simply copies lib/efi_loader/helloworld.efi into U-Boot
+ build dir and, optionally, compresses the file using gzip.
+
+ Args:
+ fname: The target file name within U-Boot build dir.
+ comp: Flag to enable gzip compression.
+ Return:
+ The path of the created file.
+ """
+
+ bin_path = make_fpath(fname)
+ util.run_and_log(cons,
+ ['cp', make_fpath('lib/efi_loader/helloworld.efi'),
+ bin_path])
+ if comp:
+ util.run_and_log(cons, ['gzip', '-f', bin_path])
+ bin_path += '.gz'
+ return bin_path
+
+ def make_dtb(fdt_type, comp):
+ """Create a sample DTB file.
+
+ Creates a DTS file and compiles it to a DTB.
+
+ Args:
+ fdt_type: The type of the FDT, i.e. internal, user.
+ comp: Flag to enable gzip compression.
+ Return:
+ The path of the created file.
+ """
+
+ # Generate resources referenced by FDT.
+ fdt_params = {
+ 'sys-arch': sys_arch,
+ 'fdt_type': fdt_type,
+ }
+
+ # Generate a test FDT file.
+ dts = make_fpath('test-efi-fit-%s.dts' % fdt_type)
+ with open(dts, 'w') as file:
+ file.write(FDT_DATA % fdt_params)
+
+ # Build the test FDT.
+ dtb = make_fpath('test-efi-fit-%s.dtb' % fdt_type)
+ util.run_and_log(cons, ['dtc', '-I', 'dts', '-O', 'dtb', '-o', dtb, dts])
+ if comp:
+ util.run_and_log(cons, ['gzip', '-f', dtb])
+ dtb += '.gz'
+ return dtb
+
+ def make_fit(comp):
+ """Create a sample FIT image.
+
+ Runs 'mkimage' to create a FIT image within U-Boot build dir.
+ Args:
+ comp: Enable gzip compression for the EFI binary and FDT blob.
+ Return:
+ The path of the created file.
+ """
+
+ # Generate resources referenced by ITS.
+ its_params = {
+ 'sys-arch': sys_arch,
+ 'efi-bin': os.path.basename(make_efi('test-efi-fit-helloworld.efi', comp)),
+ 'kernel-type': 'kernel' if comp else 'kernel_noload',
+ 'efi-comp': 'gzip' if comp else 'none',
+ 'fdt-bin': os.path.basename(make_dtb('user', comp)),
+ 'fdt-comp': 'gzip' if comp else 'none',
+ }
+
+ # Generate a test ITS file.
+ its_path = make_fpath('test-efi-fit-helloworld.its')
+ with open(its_path, 'w') as file:
+ file.write(ITS_DATA % its_params)
+
+ # Build the test ITS.
+ fit_path = make_fpath('test-efi-fit-helloworld.fit')
+ util.run_and_log(
+ cons, [make_fpath('tools/mkimage'), '-f', its_path, fit_path])
+ return fit_path
+
+ def load_fit_from_host(fit):
+ """Load the FIT image using the 'host load' command and return its address.
+
+ Args:
+ fit: Dictionary describing the FIT image to load, see env__efi_fit_test_file
+ in the comment at the beginning of this file.
+ Return:
+ The address where the file has been loaded.
+ """
+
+ addr = fit.get('addr', None)
+ if not addr:
+ addr = util.find_ram_base(cons)
+
+ output = cons.run_command(
+ 'host load hostfs - %x %s/%s' % (addr, fit['dn'], fit['fn']))
+ expected_text = ' bytes read'
+ size = fit.get('size', None)
+ if size:
+ expected_text = '%d' % size + expected_text
+ assert expected_text in output
+
+ return addr
+
+ def load_fit_from_tftp(fit):
+ """Load the FIT image using the tftpboot command and return its address.
+
+ The file is downloaded from the TFTP server, its size and optionally its
+ CRC32 are validated.
+
+ Args:
+ fit: Dictionary describing the FIT image to load, see env__efi_fit_tftp_file
+ in the comment at the beginning of this file.
+ Return:
+ The address where the file has been loaded.
+ """
+
+ addr = fit.get('addr', None)
+ if not addr:
+ addr = util.find_ram_base(cons)
+
+ file_name = fit['fn']
+ output = cons.run_command('tftpboot %x %s' % (addr, file_name))
+ expected_text = 'Bytes transferred = '
+ size = fit.get('size', None)
+ if size:
+ expected_text += '%d' % size
+ assert expected_text in output
+
+ expected_crc = fit.get('crc32', None)
+ if not expected_crc:
+ return addr
+
+ if cons.config.buildconfig.get('config_cmd_crc32', 'n') != 'y':
+ return addr
+
+ output = cons.run_command('crc32 $fileaddr $filesize')
+ assert expected_crc in output
+
+ return addr
+
+ def launch_efi(enable_fdt, enable_comp):
+ """Launch U-Boot's helloworld.efi binary from a FIT image.
+
+ An external image file can be downloaded from TFTP, when related
+ details are provided by the boardenv_* file; see the comment at the
+ beginning of this file.
+
+ If the size of the TFTP file is not provided within env__efi_fit_tftp_file,
+ the test image is generated automatically and placed in the TFTP root
+ directory specified via the 'dn' field.
+
+ When running the tests on Sandbox, the image file is loaded directly
+ from the host filesystem.
+
+ Once the load address is available on U-Boot console, the 'bootm'
+ command is executed for either 'config-efi-fdt' or 'config-efi-nofdt'
+ FIT configuration, depending on the value of the 'enable_fdt' function
+ argument.
+
+ Eventually the 'Hello, world' message is expected in the U-Boot console.
+
+ Args:
+ enable_fdt: Flag to enable using the FDT blob inside FIT image.
+ enable_comp: Flag to enable GZIP compression on EFI and FDT
+ generated content.
+ """
+
+ with cons.log.section('FDT=%s;COMP=%s' % (enable_fdt, enable_comp)):
+ if is_sandbox:
+ fit = {
+ 'dn': cons.config.build_dir,
+ }
+ else:
+ # Init networking.
+ net_pre_commands()
+ net_set_up = net_dhcp()
+ net_set_up = net_setup_static() or net_set_up
+ if not net_set_up:
+ pytest.skip('Network not initialized')
+
+ fit = cons.config.env.get('env__efi_fit_tftp_file', None)
+ if not fit:
+ pytest.skip('No env__efi_fit_tftp_file binary specified in environment')
+
+ size = fit.get('size', None)
+ if not size:
+ if not fit.get('dn', None):
+ pytest.skip('Neither "size", nor "dn" info provided in env__efi_fit_tftp_file')
+
+ # Create test FIT image.
+ fit_path = make_fit(enable_comp)
+ fit['fn'] = os.path.basename(fit_path)
+ fit['size'] = os.path.getsize(fit_path)
+
+ # Copy image to TFTP root directory.
+ if fit['dn'] != cons.config.build_dir:
+ util.run_and_log(cons, ['mv', '-f', fit_path, '%s/' % fit['dn']])
+
+ # Load FIT image.
+ addr = load_fit_from_host(fit) if is_sandbox else load_fit_from_tftp(fit)
+
+ # Select boot configuration.
+ fit_config = 'config-efi-fdt' if enable_fdt else 'config-efi-nofdt'
+
+ # Try booting.
+ output = cons.run_command('bootm %x#%s' % (addr, fit_config))
+ if enable_fdt:
+ assert 'Booting using the fdt blob' in output
+ assert 'Hello, world' in output
+ assert '## Application failed' not in output
+ cons.restart_uboot()
+
+ cons = u_boot_console
+ # Array slice removes leading/trailing quotes.
+ sys_arch = cons.config.buildconfig.get('config_sys_arch', '"sandbox"')[1:-1]
+ is_sandbox = sys_arch == 'sandbox'
+
+ try:
+ if is_sandbox:
+ # Use our own device tree file, will be restored afterwards.
+ control_dtb = make_dtb('internal', False)
+ old_dtb = cons.config.dtb
+ cons.config.dtb = control_dtb
+
+ # Run tests
+ # - fdt OFF, gzip OFF
+ launch_efi(False, False)
+ # - fdt ON, gzip OFF
+ launch_efi(True, False)
+
+ if is_sandbox:
+ # - fdt OFF, gzip ON
+ launch_efi(False, True)
+ # - fdt ON, gzip ON
+ launch_efi(True, True)
+
+ finally:
+ if is_sandbox:
+ # Go back to the original U-Boot with the correct dtb.
+ cons.config.dtb = old_dtb
+ cons.restart_uboot()
diff --git a/roms/u-boot/test/py/tests/test_efi_loader.py b/roms/u-boot/test/py/tests/test_efi_loader.py
new file mode 100644
index 000000000..fc8d6b865
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_efi_loader.py
@@ -0,0 +1,204 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
+# Copyright (c) 2016, Alexander Graf <agraf@suse.de>
+#
+# based on test_net.py.
+
+# Test efi loader implementation
+
+import pytest
+import u_boot_utils
+
+"""
+Note: This test relies on boardenv_* containing configuration values to define
+which network environment is available for testing. Without this, the parts
+that rely on network will be automatically skipped.
+
+For example:
+
+# Boolean indicating whether the Ethernet device is attached to USB, and hence
+# USB enumeration needs to be performed prior to network tests.
+# This variable may be omitted if its value is False.
+env__net_uses_usb = False
+
+# Boolean indicating whether the Ethernet device is attached to PCI, and hence
+# PCI enumeration needs to be performed prior to network tests.
+# This variable may be omitted if its value is False.
+env__net_uses_pci = True
+
+# True if a DHCP server is attached to the network, and should be tested.
+# If DHCP testing is not possible or desired, this variable may be omitted or
+# set to False.
+env__net_dhcp_server = True
+
+# A list of environment variables that should be set in order to configure a
+# static IP. If solely relying on DHCP, this variable may be omitted or set to
+# an empty list.
+env__net_static_env_vars = [
+ ('ipaddr', '10.0.0.100'),
+ ('netmask', '255.255.255.0'),
+ ('serverip', '10.0.0.1'),
+]
+
+# Details regarding a file that may be read from a TFTP server. This variable
+# may be omitted or set to None if TFTP testing is not possible or desired.
+env__efi_loader_helloworld_file = {
+ 'fn': 'lib/efi_loader/helloworld.efi', # file name
+ 'size': 5058624, # file length in bytes
+ 'crc32': 'c2244b26', # CRC32 check sum
+ 'addr': 0x40400000, # load address
+}
+"""
+
+net_set_up = False
+
+def test_efi_pre_commands(u_boot_console):
+ """Execute any commands required to enable network hardware.
+
+ These commands are provided by the boardenv_* file; see the comment at the
+ beginning of this file.
+ """
+
+ init_usb = u_boot_console.config.env.get('env__net_uses_usb', False)
+ if init_usb:
+ u_boot_console.run_command('usb start')
+
+ init_pci = u_boot_console.config.env.get('env__net_uses_pci', False)
+ if init_pci:
+ u_boot_console.run_command('pci enum')
+
+@pytest.mark.buildconfigspec('cmd_dhcp')
+def test_efi_setup_dhcp(u_boot_console):
+ """Set up the network using DHCP.
+
+ The boardenv_* file may be used to enable/disable this test; see the
+ comment at the beginning of this file.
+ """
+
+ test_dhcp = u_boot_console.config.env.get('env__net_dhcp_server', False)
+ if not test_dhcp:
+ env_vars = u_boot_console.config.env.get('env__net_static_env_vars', None)
+ if not env_vars:
+ pytest.skip('No DHCP server available')
+ return None
+
+ u_boot_console.run_command('setenv autoload no')
+ output = u_boot_console.run_command('dhcp')
+ assert 'DHCP client bound to address ' in output
+
+ global net_set_up
+ net_set_up = True
+
+@pytest.mark.buildconfigspec('net')
+def test_efi_setup_static(u_boot_console):
+ """Set up the network using a static IP configuration.
+
+ The configuration is provided by the boardenv_* file; see the comment at
+ the beginning of this file.
+ """
+
+ env_vars = u_boot_console.config.env.get('env__net_static_env_vars', None)
+ if not env_vars:
+ test_dhcp = u_boot_console.config.env.get('env__net_dhcp_server', False)
+ if not test_dhcp:
+ pytest.skip('No static network configuration is defined')
+ return None
+
+ for (var, val) in env_vars:
+ u_boot_console.run_command('setenv %s %s' % (var, val))
+
+ global net_set_up
+ net_set_up = True
+
+def fetch_tftp_file(u_boot_console, env_conf):
+ """Grab an env described file via TFTP and return its address
+
+ A file as described by an env config <env_conf> is downloaded from the TFTP
+ server. The address to that file is returned.
+ """
+ if not net_set_up:
+ pytest.skip('Network not initialized')
+
+ f = u_boot_console.config.env.get(env_conf, None)
+ if not f:
+ pytest.skip('No %s binary specified in environment' % env_conf)
+
+ addr = f.get('addr', None)
+ if not addr:
+ addr = u_boot_utils.find_ram_base(u_boot_console)
+
+ fn = f['fn']
+ output = u_boot_console.run_command('tftpboot %x %s' % (addr, fn))
+ expected_text = 'Bytes transferred = '
+ sz = f.get('size', None)
+ if sz:
+ expected_text += '%d' % sz
+ assert expected_text in output
+
+ expected_crc = f.get('crc32', None)
+ if not expected_crc:
+ return addr
+
+ if u_boot_console.config.buildconfig.get('config_cmd_crc32', 'n') != 'y':
+ return addr
+
+ output = u_boot_console.run_command('crc32 %x $filesize' % addr)
+ assert expected_crc in output
+
+ return addr
+
+@pytest.mark.buildconfigspec('of_control')
+@pytest.mark.buildconfigspec('cmd_bootefi_hello_compile')
+def test_efi_helloworld_net(u_boot_console):
+ """Run the helloworld.efi binary via TFTP.
+
+ The helloworld.efi file is downloaded from the TFTP server and is executed
+ using the fallback device tree at $fdtcontroladdr.
+ """
+
+ addr = fetch_tftp_file(u_boot_console, 'env__efi_loader_helloworld_file')
+
+ output = u_boot_console.run_command('bootefi %x' % addr)
+ expected_text = 'Hello, world'
+ assert expected_text in output
+ expected_text = '## Application failed'
+ assert expected_text not in output
+
+@pytest.mark.buildconfigspec('cmd_bootefi_hello')
+def test_efi_helloworld_builtin(u_boot_console):
+ """Run the builtin helloworld.efi binary.
+
+ The helloworld.efi file is included in U-Boot, execute it using the
+ special "bootefi hello" command.
+ """
+
+ output = u_boot_console.run_command('bootefi hello')
+ expected_text = 'Hello, world'
+ assert expected_text in output
+
+@pytest.mark.buildconfigspec('of_control')
+@pytest.mark.buildconfigspec('cmd_bootefi')
+def test_efi_grub_net(u_boot_console):
+ """Run the grub.efi binary via TFTP.
+
+ The grub.efi file is downloaded from the TFTP server and gets
+ executed.
+ """
+
+ addr = fetch_tftp_file(u_boot_console, 'env__efi_loader_grub_file')
+
+ u_boot_console.run_command('bootefi %x' % addr, wait_for_prompt=False)
+
+ # Verify that we have an SMBIOS table
+ check_smbios = u_boot_console.config.env.get('env__efi_loader_check_smbios', False)
+ if check_smbios:
+ u_boot_console.wait_for('grub>')
+ output = u_boot_console.run_command('lsefisystab', wait_for_prompt=False, wait_for_echo=False)
+ u_boot_console.wait_for('SMBIOS')
+
+ # Then exit cleanly
+ u_boot_console.wait_for('grub>')
+ u_boot_console.run_command('exit', wait_for_prompt=False, wait_for_echo=False)
+ u_boot_console.wait_for(u_boot_console.prompt)
+ # And give us our U-Boot prompt back
+ u_boot_console.run_command('')
diff --git a/roms/u-boot/test/py/tests/test_efi_secboot/conftest.py b/roms/u-boot/test/py/tests/test_efi_secboot/conftest.py
new file mode 100644
index 000000000..69a498ca0
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_efi_secboot/conftest.py
@@ -0,0 +1,239 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2019, Linaro Limited
+# Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
+
+import os
+import os.path
+from subprocess import call, check_call, check_output, CalledProcessError
+import pytest
+from defs import *
+
+
+#
+# Fixture for UEFI secure boot test
+#
+
+
+@pytest.fixture(scope='session')
+def efi_boot_env(request, u_boot_config):
+ """Set up a file system to be used in UEFI secure boot test.
+
+ Args:
+ request: Pytest request object.
+ u_boot_config: U-boot configuration.
+
+ Return:
+ A path to disk image to be used for testing
+ """
+ image_path = u_boot_config.persistent_data_dir
+ image_path = image_path + '/test_efi_secboot.img'
+
+ try:
+ mnt_point = u_boot_config.build_dir + '/mnt_efisecure'
+ check_call('rm -rf {}'.format(mnt_point), shell=True)
+ check_call('mkdir -p {}'.format(mnt_point), shell=True)
+
+ # suffix
+ # *.key: RSA private key in PEM
+ # *.crt: X509 certificate (self-signed) in PEM
+ # *.esl: signature list
+ # *.hash: message digest of image as signature list
+ # *.auth: signed signature list in signature database format
+ # *.efi: UEFI image
+ # *.efi.signed: signed UEFI image
+
+ # Create signature database
+ # PK
+ check_call('cd %s; openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_PK/ -keyout PK.key -out PK.crt -nodes -days 365'
+ % mnt_point, shell=True)
+ check_call('cd %s; %scert-to-efi-sig-list -g %s PK.crt PK.esl; %ssign-efi-sig-list -t "2020-04-01" -c PK.crt -k PK.key PK PK.esl PK.auth'
+ % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
+ shell=True)
+ # PK_null for deletion
+ check_call('cd %s; touch PK_null.esl; %ssign-efi-sig-list -t "2020-04-02" -c PK.crt -k PK.key PK PK_null.esl PK_null.auth'
+ % (mnt_point, EFITOOLS_PATH), shell=True)
+ # KEK
+ check_call('cd %s; openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_KEK/ -keyout KEK.key -out KEK.crt -nodes -days 365'
+ % mnt_point, shell=True)
+ check_call('cd %s; %scert-to-efi-sig-list -g %s KEK.crt KEK.esl; %ssign-efi-sig-list -t "2020-04-03" -c PK.crt -k PK.key KEK KEK.esl KEK.auth'
+ % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
+ shell=True)
+ # db
+ check_call('cd %s; openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_db/ -keyout db.key -out db.crt -nodes -days 365'
+ % mnt_point, shell=True)
+ check_call('cd %s; %scert-to-efi-sig-list -g %s db.crt db.esl; %ssign-efi-sig-list -t "2020-04-04" -c KEK.crt -k KEK.key db db.esl db.auth'
+ % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
+ shell=True)
+ # db1
+ check_call('cd %s; openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_db1/ -keyout db1.key -out db1.crt -nodes -days 365'
+ % mnt_point, shell=True)
+ check_call('cd %s; %scert-to-efi-sig-list -g %s db1.crt db1.esl; %ssign-efi-sig-list -t "2020-04-05" -c KEK.crt -k KEK.key db db1.esl db1.auth'
+ % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
+ shell=True)
+ # dbx (TEST_dbx certificate)
+ check_call('cd %s; openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_dbx/ -keyout dbx.key -out dbx.crt -nodes -days 365'
+ % mnt_point, shell=True)
+ check_call('cd %s; %scert-to-efi-sig-list -g %s dbx.crt dbx.esl; %ssign-efi-sig-list -t "2020-04-05" -c KEK.crt -k KEK.key dbx dbx.esl dbx.auth'
+ % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
+ shell=True)
+ # dbx_hash (digest of TEST_db certificate)
+ check_call('cd %s; %scert-to-efi-hash-list -g %s -t 0 -s 256 db.crt dbx_hash.crl; %ssign-efi-sig-list -t "2020-04-05" -c KEK.crt -k KEK.key dbx dbx_hash.crl dbx_hash.auth'
+ % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
+ shell=True)
+ # dbx_hash1 (digest of TEST_db1 certificate)
+ check_call('cd %s; %scert-to-efi-hash-list -g %s -t 0 -s 256 db1.crt dbx_hash1.crl; %ssign-efi-sig-list -t "2020-04-06" -c KEK.crt -k KEK.key dbx dbx_hash1.crl dbx_hash1.auth'
+ % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
+ shell=True)
+ # dbx_db (with TEST_db certificate)
+ check_call('cd %s; %ssign-efi-sig-list -t "2020-04-05" -c KEK.crt -k KEK.key dbx db.esl dbx_db.auth'
+ % (mnt_point, EFITOOLS_PATH),
+ shell=True)
+
+ # Copy image
+ check_call('cp %s/lib/efi_loader/helloworld.efi %s' %
+ (u_boot_config.build_dir, mnt_point), shell=True)
+
+ # Sign image
+ check_call('cd %s; sbsign --key db.key --cert db.crt helloworld.efi'
+ % mnt_point, shell=True)
+ # Sign already-signed image with another key
+ check_call('cd %s; sbsign --key db1.key --cert db1.crt --output helloworld.efi.signed_2sigs helloworld.efi.signed'
+ % mnt_point, shell=True)
+ # Digest image
+ check_call('cd %s; %shash-to-efi-sig-list helloworld.efi db_hello.hash; %ssign-efi-sig-list -t "2020-04-07" -c KEK.crt -k KEK.key db db_hello.hash db_hello.auth'
+ % (mnt_point, EFITOOLS_PATH, EFITOOLS_PATH),
+ shell=True)
+ check_call('cd %s; %shash-to-efi-sig-list helloworld.efi.signed db_hello_signed.hash; %ssign-efi-sig-list -t "2020-04-03" -c KEK.crt -k KEK.key db db_hello_signed.hash db_hello_signed.auth'
+ % (mnt_point, EFITOOLS_PATH, EFITOOLS_PATH),
+ shell=True)
+ check_call('cd %s; %ssign-efi-sig-list -t "2020-04-07" -c KEK.crt -k KEK.key dbx db_hello_signed.hash dbx_hello_signed.auth'
+ % (mnt_point, EFITOOLS_PATH),
+ shell=True)
+
+ check_call('virt-make-fs --partition=gpt --size=+1M --type=vfat {} {}'.format(
+ mnt_point, image_path), shell=True)
+ check_call('rm -rf {}'.format(mnt_point), shell=True)
+
+ except CalledProcessError as exception:
+ pytest.skip('Setup failed: %s' % exception.cmd)
+ return
+ else:
+ yield image_path
+ finally:
+ call('rm -f %s' % image_path, shell=True)
+
+#
+# Fixture for UEFI secure boot test of intermediate certificates
+#
+
+
+@pytest.fixture(scope='session')
+def efi_boot_env_intca(request, u_boot_config):
+ """Set up a file system to be used in UEFI secure boot test
+ of intermediate certificates.
+
+ Args:
+ request: Pytest request object.
+ u_boot_config: U-boot configuration.
+
+ Return:
+ A path to disk image to be used for testing
+ """
+ image_path = u_boot_config.persistent_data_dir
+ image_path = image_path + '/test_efi_secboot_intca.img'
+
+ try:
+ mnt_point = u_boot_config.persistent_data_dir + '/mnt_efi_secboot_intca'
+ check_call('rm -rf {}'.format(mnt_point), shell=True)
+ check_call('mkdir -p {}'.format(mnt_point), shell=True)
+
+ # Create signature database
+ # PK
+ check_call('cd %s; openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_PK/ -keyout PK.key -out PK.crt -nodes -days 365'
+ % mnt_point, shell=True)
+ check_call('cd %s; %scert-to-efi-sig-list -g %s PK.crt PK.esl; %ssign-efi-sig-list -c PK.crt -k PK.key PK PK.esl PK.auth'
+ % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
+ shell=True)
+ # KEK
+ check_call('cd %s; openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_KEK/ -keyout KEK.key -out KEK.crt -nodes -days 365'
+ % mnt_point, shell=True)
+ check_call('cd %s; %scert-to-efi-sig-list -g %s KEK.crt KEK.esl; %ssign-efi-sig-list -c PK.crt -k PK.key KEK KEK.esl KEK.auth'
+ % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
+ shell=True)
+
+ # We will have three-tier hierarchy of certificates:
+ # TestRoot: Root CA (self-signed)
+ # TestSub: Intermediate CA (signed by Root CA)
+ # TestCert: User certificate (signed by Intermediate CA, and used
+ # for signing an image)
+ #
+ # NOTE:
+ # I consulted the following EDK2 document for certificate options:
+ # BaseTools/Source/Python/Pkcs7Sign/Readme.md
+ # Please not use them as they are in product system. They are
+ # for test purpose only.
+
+ # TestRoot
+ check_call('cp %s/test/py/tests/test_efi_secboot/openssl.cnf %s'
+ % (u_boot_config.source_dir, mnt_point), shell=True)
+ check_call('cd %s; export OPENSSL_CONF=./openssl.cnf; openssl genrsa -out TestRoot.key 2048; openssl req -extensions v3_ca -new -x509 -days 365 -key TestRoot.key -out TestRoot.crt -subj "/CN=TEST_root/"; touch index.txt; touch index.txt.attr'
+ % mnt_point, shell=True)
+ # TestSub
+ check_call('cd %s; touch serial.new; export OPENSSL_CONF=./openssl.cnf; openssl genrsa -out TestSub.key 2048; openssl req -new -key TestSub.key -out TestSub.csr -subj "/CN=TEST_sub/"; openssl ca -in TestSub.csr -out TestSub.crt -extensions v3_int_ca -days 365 -batch -rand_serial -cert TestRoot.crt -keyfile TestRoot.key'
+ % mnt_point, shell=True)
+ # TestCert
+ check_call('cd %s; touch serial.new; export OPENSSL_CONF=./openssl.cnf; openssl genrsa -out TestCert.key 2048; openssl req -new -key TestCert.key -out TestCert.csr -subj "/CN=TEST_cert/"; openssl ca -in TestCert.csr -out TestCert.crt -extensions usr_cert -days 365 -batch -rand_serial -cert TestSub.crt -keyfile TestSub.key'
+ % mnt_point, shell=True)
+ # db
+ # for TestCert
+ check_call('cd %s; %scert-to-efi-sig-list -g %s TestCert.crt TestCert.esl; %ssign-efi-sig-list -c KEK.crt -k KEK.key db TestCert.esl db_a.auth'
+ % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
+ shell=True)
+ # for TestSub
+ check_call('cd %s; %scert-to-efi-sig-list -g %s TestSub.crt TestSub.esl; %ssign-efi-sig-list -t "2020-07-16" -c KEK.crt -k KEK.key db TestSub.esl db_b.auth'
+ % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
+ shell=True)
+ # for TestRoot
+ check_call('cd %s; %scert-to-efi-sig-list -g %s TestRoot.crt TestRoot.esl; %ssign-efi-sig-list -t "2020-07-17" -c KEK.crt -k KEK.key db TestRoot.esl db_c.auth'
+ % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
+ shell=True)
+ ## dbx (hash of certificate with revocation time)
+ # for TestCert
+ check_call('cd %s; %scert-to-efi-hash-list -g %s -t "2020-07-20" -s 256 TestCert.crt TestCert.crl; %ssign-efi-sig-list -c KEK.crt -k KEK.key dbx TestCert.crl dbx_a.auth'
+ % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
+ shell=True)
+ # for TestSub
+ check_call('cd %s; %scert-to-efi-hash-list -g %s -t "2020-07-21" -s 256 TestSub.crt TestSub.crl; %ssign-efi-sig-list -t "2020-07-18" -c KEK.crt -k KEK.key dbx TestSub.crl dbx_b.auth'
+ % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
+ shell=True)
+ # for TestRoot
+ check_call('cd %s; %scert-to-efi-hash-list -g %s -t "2020-07-22" -s 256 TestRoot.crt TestRoot.crl; %ssign-efi-sig-list -t "2020-07-19" -c KEK.crt -k KEK.key dbx TestRoot.crl dbx_c.auth'
+ % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
+ shell=True)
+
+ # Sign image
+ # additional intermediate certificates may be included
+ # in SignedData
+
+ check_call('cp %s/lib/efi_loader/helloworld.efi %s' %
+ (u_boot_config.build_dir, mnt_point), shell=True)
+ # signed by TestCert
+ check_call('cd %s; %ssbsign --key TestCert.key --cert TestCert.crt --out helloworld.efi.signed_a helloworld.efi'
+ % (mnt_point, SBSIGN_PATH), shell=True)
+ # signed by TestCert with TestSub in signature
+ check_call('cd %s; %ssbsign --key TestCert.key --cert TestCert.crt --addcert TestSub.crt --out helloworld.efi.signed_ab helloworld.efi'
+ % (mnt_point, SBSIGN_PATH), shell=True)
+ # signed by TestCert with TestSub and TestRoot in signature
+ check_call('cd %s; cat TestSub.crt TestRoot.crt > TestSubRoot.crt; %ssbsign --key TestCert.key --cert TestCert.crt --addcert TestSubRoot.crt --out helloworld.efi.signed_abc helloworld.efi'
+ % (mnt_point, SBSIGN_PATH), shell=True)
+
+ check_call('virt-make-fs --partition=gpt --size=+1M --type=vfat {} {}'.format(mnt_point, image_path), shell=True)
+ check_call('rm -rf {}'.format(mnt_point), shell=True)
+
+ except CalledProcessError as e:
+ pytest.skip('Setup failed: %s' % e.cmd)
+ return
+ else:
+ yield image_path
+ finally:
+ call('rm -f %s' % image_path, shell=True)
diff --git a/roms/u-boot/test/py/tests/test_efi_secboot/defs.py b/roms/u-boot/test/py/tests/test_efi_secboot/defs.py
new file mode 100644
index 000000000..b7a2a1185
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_efi_secboot/defs.py
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0+
+
+# Owner guid
+GUID = '11111111-2222-3333-4444-123456789abc'
+
+# v1.5.1 or earlier of efitools has a bug in sha256 calculation, and
+# you need build a newer version on your own.
+# The path must terminate with '/'.
+EFITOOLS_PATH = ''
+
+# "--addcert" option of sbsign must be available, otherwise
+# you need build a newer version on your own.
+# The path must terminate with '/'.
+SBSIGN_PATH = ''
diff --git a/roms/u-boot/test/py/tests/test_efi_secboot/openssl.cnf b/roms/u-boot/test/py/tests/test_efi_secboot/openssl.cnf
new file mode 100644
index 000000000..f684f1df7
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_efi_secboot/openssl.cnf
@@ -0,0 +1,48 @@
+[ ca ]
+default_ca = CA_default
+
+[ CA_default ]
+new_certs_dir = .
+database = ./index.txt
+serial = ./serial
+default_md = sha256
+policy = policy_min
+
+[ req ]
+distinguished_name = def_distinguished_name
+
+[def_distinguished_name]
+
+# Extensions
+# -addext " ... = ..."
+#
+[ v3_ca ]
+ # Extensions for a typical Root CA.
+ basicConstraints = critical,CA:TRUE
+ keyUsage = critical, digitalSignature, cRLSign, keyCertSign
+ subjectKeyIdentifier = hash
+ authorityKeyIdentifier = keyid:always,issuer
+
+[ v3_int_ca ]
+ # Extensions for a typical intermediate CA.
+ basicConstraints = critical, CA:TRUE
+ keyUsage = critical, digitalSignature, cRLSign, keyCertSign
+ subjectKeyIdentifier = hash
+ authorityKeyIdentifier = keyid:always,issuer
+
+[ usr_cert ]
+ # Extensions for user end certificates.
+ basicConstraints = CA:FALSE
+ keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment
+ extendedKeyUsage = clientAuth, emailProtection
+ subjectKeyIdentifier = hash
+ authorityKeyIdentifier = keyid,issuer
+
+[ policy_min ]
+ countryName = optional
+ stateOrProvinceName = optional
+ localityName = optional
+ organizationName = optional
+ organizationalUnitName = optional
+ commonName = supplied
+ emailAddress = optional
diff --git a/roms/u-boot/test/py/tests/test_efi_secboot/test_authvar.py b/roms/u-boot/test/py/tests/test_efi_secboot/test_authvar.py
new file mode 100644
index 000000000..f99b8270a
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_efi_secboot/test_authvar.py
@@ -0,0 +1,281 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2019, Linaro Limited
+# Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
+#
+# U-Boot UEFI: Variable Authentication Test
+
+"""
+This test verifies variable authentication
+"""
+
+import pytest
+
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('efi_secure_boot')
+@pytest.mark.buildconfigspec('cmd_fat')
+@pytest.mark.buildconfigspec('cmd_nvedit_efi')
+@pytest.mark.slow
+class TestEfiAuthVar(object):
+ def test_efi_var_auth1(self, u_boot_console, efi_boot_env):
+ """
+ Test Case 1 - Install signature database
+ """
+ u_boot_console.restart_uboot()
+ disk_img = efi_boot_env
+ with u_boot_console.log.section('Test Case 1a'):
+ # Test Case 1a, Initial secure state
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % disk_img,
+ 'printenv -e SecureBoot'])
+ assert '00000000: 00' in ''.join(output)
+
+ output = u_boot_console.run_command(
+ 'printenv -e SetupMode')
+ assert '00000000: 01' in output
+
+ with u_boot_console.log.section('Test Case 1b'):
+ # Test Case 1b, PK without AUTHENTICATED_WRITE_ACCESS
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 PK.auth',
+ 'setenv -e -nv -bs -rt -i 4000000:$filesize PK'])
+ assert 'Failed to set EFI variable' in ''.join(output)
+
+ with u_boot_console.log.section('Test Case 1c'):
+ # Test Case 1c, install PK
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 PK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK',
+ 'printenv -e -n PK'])
+ assert 'PK:' in ''.join(output)
+
+ output = u_boot_console.run_command(
+ 'printenv -e SecureBoot')
+ assert '00000000: 01' in output
+ output = u_boot_console.run_command(
+ 'printenv -e SetupMode')
+ assert '00000000: 00' in output
+
+ with u_boot_console.log.section('Test Case 1d'):
+ # Test Case 1d, db/dbx without KEK
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 db.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db'])
+ assert 'Failed to set EFI variable' in ''.join(output)
+
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 db.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize dbx'])
+ assert 'Failed to set EFI variable' in ''.join(output)
+
+ with u_boot_console.log.section('Test Case 1e'):
+ # Test Case 1e, install KEK
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 KEK.auth',
+ 'setenv -e -nv -bs -rt -i 4000000:$filesize KEK'])
+ assert 'Failed to set EFI variable' in ''.join(output)
+
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 KEK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK',
+ 'printenv -e -n KEK'])
+ assert 'KEK:' in ''.join(output)
+
+ output = u_boot_console.run_command(
+ 'printenv -e SecureBoot')
+ assert '00000000: 01' in output
+
+ with u_boot_console.log.section('Test Case 1f'):
+ # Test Case 1f, install db
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 db.auth',
+ 'setenv -e -nv -bs -rt -i 4000000:$filesize db'])
+ assert 'Failed to set EFI variable' in ''.join(output)
+
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 db.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db',
+ 'printenv -e -n -guid d719b2cb-3d3a-4596-a3bc-dad00e67656f db'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+ assert 'db:' in ''.join(output)
+
+ output = u_boot_console.run_command(
+ 'printenv -e SecureBoot')
+ assert '00000000: 01' in output
+
+ with u_boot_console.log.section('Test Case 1g'):
+ # Test Case 1g, install dbx
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 dbx.auth',
+ 'setenv -e -nv -bs -rt -i 4000000:$filesize dbx'])
+ assert 'Failed to set EFI variable' in ''.join(output)
+
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 dbx.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize dbx',
+ 'printenv -e -n -guid d719b2cb-3d3a-4596-a3bc-dad00e67656f dbx'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+ assert 'dbx:' in ''.join(output)
+
+ output = u_boot_console.run_command(
+ 'printenv -e SecureBoot')
+ assert '00000000: 01' in output
+
+ def test_efi_var_auth2(self, u_boot_console, efi_boot_env):
+ """
+ Test Case 2 - Update database by overwriting
+ """
+ u_boot_console.restart_uboot()
+ disk_img = efi_boot_env
+ with u_boot_console.log.section('Test Case 2a'):
+ # Test Case 2a, update without AUTHENTICATED_WRITE_ACCESS
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % disk_img,
+ 'fatload host 0:1 4000000 PK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK',
+ 'fatload host 0:1 4000000 KEK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK',
+ 'fatload host 0:1 4000000 db.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db',
+ 'printenv -e -n -guid d719b2cb-3d3a-4596-a3bc-dad00e67656f db'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+ assert 'db:' in ''.join(output)
+
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 db1.auth',
+ 'setenv -e -nv -bs -rt -i 4000000:$filesize db'])
+ assert 'Failed to set EFI variable' in ''.join(output)
+
+ with u_boot_console.log.section('Test Case 2b'):
+ # Test Case 2b, update without correct signature
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 db.esl',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db'])
+ assert 'Failed to set EFI variable' in ''.join(output)
+
+ with u_boot_console.log.section('Test Case 2c'):
+ # Test Case 2c, update with correct signature
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 db1.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db',
+ 'printenv -e -n -guid d719b2cb-3d3a-4596-a3bc-dad00e67656f db'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+ assert 'db:' in ''.join(output)
+
+ def test_efi_var_auth3(self, u_boot_console, efi_boot_env):
+ """
+ Test Case 3 - Append database
+ """
+ u_boot_console.restart_uboot()
+ disk_img = efi_boot_env
+ with u_boot_console.log.section('Test Case 3a'):
+ # Test Case 3a, update without AUTHENTICATED_WRITE_ACCESS
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % disk_img,
+ 'fatload host 0:1 4000000 PK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK',
+ 'fatload host 0:1 4000000 KEK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK',
+ 'fatload host 0:1 4000000 db.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db',
+ 'printenv -e -n -guid d719b2cb-3d3a-4596-a3bc-dad00e67656f db'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+ assert 'db:' in ''.join(output)
+
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 db1.auth',
+ 'setenv -e -nv -bs -rt -a -i 4000000:$filesize db'])
+ assert 'Failed to set EFI variable' in ''.join(output)
+
+ with u_boot_console.log.section('Test Case 3b'):
+ # Test Case 3b, update without correct signature
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 db.esl',
+ 'setenv -e -nv -bs -rt -at -a -i 4000000:$filesize db'])
+ assert 'Failed to set EFI variable' in ''.join(output)
+
+ with u_boot_console.log.section('Test Case 3c'):
+ # Test Case 3c, update with correct signature
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 db1.auth',
+ 'setenv -e -nv -bs -rt -at -a -i 4000000:$filesize db',
+ 'printenv -e -n -guid d719b2cb-3d3a-4596-a3bc-dad00e67656f db'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+ assert 'db:' in ''.join(output)
+
+ def test_efi_var_auth4(self, u_boot_console, efi_boot_env):
+ """
+ Test Case 4 - Delete database without authentication
+ """
+ u_boot_console.restart_uboot()
+ disk_img = efi_boot_env
+ with u_boot_console.log.section('Test Case 4a'):
+ # Test Case 4a, update without AUTHENTICATED_WRITE_ACCESS
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % disk_img,
+ 'fatload host 0:1 4000000 PK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK',
+ 'fatload host 0:1 4000000 KEK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK',
+ 'fatload host 0:1 4000000 db.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db',
+ 'printenv -e -n -guid d719b2cb-3d3a-4596-a3bc-dad00e67656f db'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+ assert 'db:' in ''.join(output)
+
+ output = u_boot_console.run_command_list([
+ 'setenv -e -nv -bs -rt db',
+ 'printenv -e -n -guid d719b2cb-3d3a-4596-a3bc-dad00e67656f db'])
+ assert 'Failed to set EFI variable' in ''.join(output)
+ assert 'db:' in ''.join(output)
+
+ with u_boot_console.log.section('Test Case 4b'):
+ # Test Case 4b, update without correct signature/data
+ output = u_boot_console.run_command_list([
+ 'setenv -e -nv -bs -rt -at db',
+ 'printenv -e -n -guid d719b2cb-3d3a-4596-a3bc-dad00e67656f db'])
+ assert 'Failed to set EFI variable' in ''.join(output)
+ assert 'db:' in ''.join(output)
+
+ def test_efi_var_auth5(self, u_boot_console, efi_boot_env):
+ """
+ Test Case 5 - Uninstall(delete) PK
+ """
+ u_boot_console.restart_uboot()
+ disk_img = efi_boot_env
+ with u_boot_console.log.section('Test Case 5a'):
+ # Test Case 5a, Uninstall PK without correct signature
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % disk_img,
+ 'fatload host 0:1 4000000 PK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK',
+ 'fatload host 0:1 4000000 KEK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK',
+ 'fatload host 0:1 4000000 db.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db',
+ 'printenv -e -n PK'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+ assert 'PK:' in ''.join(output)
+
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 PK_null.esl',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK',
+ 'printenv -e -n PK'])
+ assert 'Failed to set EFI variable' in ''.join(output)
+ assert 'PK:' in ''.join(output)
+
+ with u_boot_console.log.section('Test Case 5b'):
+ # Test Case 5b, Uninstall PK with correct signature
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 PK_null.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK',
+ 'printenv -e -n PK'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+ assert '\"PK\" not defined' in ''.join(output)
+
+ output = u_boot_console.run_command(
+ 'printenv -e SecureBoot')
+ assert '00000000: 00' in output
+ output = u_boot_console.run_command(
+ 'printenv -e SetupMode')
+ assert '00000000: 01' in output
diff --git a/roms/u-boot/test/py/tests/test_efi_secboot/test_signed.py b/roms/u-boot/test/py/tests/test_efi_secboot/test_signed.py
new file mode 100644
index 000000000..0aee34479
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_efi_secboot/test_signed.py
@@ -0,0 +1,259 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2019, Linaro Limited
+# Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
+#
+# U-Boot UEFI: Signed Image Authentication Test
+
+"""
+This test verifies image authentication for signed images.
+"""
+
+import pytest
+
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('efi_secure_boot')
+@pytest.mark.buildconfigspec('cmd_efidebug')
+@pytest.mark.buildconfigspec('cmd_fat')
+@pytest.mark.buildconfigspec('cmd_nvedit_efi')
+@pytest.mark.slow
+class TestEfiSignedImage(object):
+ def test_efi_signed_image_auth1(self, u_boot_console, efi_boot_env):
+ """
+ Test Case 1 - Secure boot is not in force
+ """
+ u_boot_console.restart_uboot()
+ disk_img = efi_boot_env
+ with u_boot_console.log.section('Test Case 1a'):
+ # Test Case 1a, run signed image if no PK
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % disk_img,
+ 'efidebug boot add -b 1 HELLO1 host 0:1 /helloworld.efi.signed -s ""',
+ 'efidebug boot next 1',
+ 'bootefi bootmgr'])
+ assert 'Hello, world!' in ''.join(output)
+
+ with u_boot_console.log.section('Test Case 1b'):
+ # Test Case 1b, run unsigned image if no PK
+ output = u_boot_console.run_command_list([
+ 'efidebug boot add -b 2 HELLO2 host 0:1 /helloworld.efi -s ""',
+ 'efidebug boot next 2',
+ 'bootefi bootmgr'])
+ assert 'Hello, world!' in ''.join(output)
+
+ def test_efi_signed_image_auth2(self, u_boot_console, efi_boot_env):
+ """
+ Test Case 2 - Secure boot is in force,
+ authenticated by db (TEST_db certificate in db)
+ """
+ u_boot_console.restart_uboot()
+ disk_img = efi_boot_env
+ with u_boot_console.log.section('Test Case 2a'):
+ # Test Case 2a, db is not yet installed
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % disk_img,
+ 'fatload host 0:1 4000000 KEK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK',
+ 'fatload host 0:1 4000000 PK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+ output = u_boot_console.run_command_list([
+ 'efidebug boot add -b 1 HELLO1 host 0:1 /helloworld.efi.signed -s ""',
+ 'efidebug boot next 1',
+ 'efidebug test bootmgr'])
+ assert('\'HELLO1\' failed' in ''.join(output))
+ assert('efi_start_image() returned: 26' in ''.join(output))
+ output = u_boot_console.run_command_list([
+ 'efidebug boot add -b 2 HELLO2 host 0:1 /helloworld.efi -s ""',
+ 'efidebug boot next 2',
+ 'efidebug test bootmgr'])
+ assert '\'HELLO2\' failed' in ''.join(output)
+ assert 'efi_start_image() returned: 26' in ''.join(output)
+
+ with u_boot_console.log.section('Test Case 2b'):
+ # Test Case 2b, authenticated by db
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 db.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+ output = u_boot_console.run_command_list([
+ 'efidebug boot next 2',
+ 'efidebug test bootmgr'])
+ assert '\'HELLO2\' failed' in ''.join(output)
+ assert 'efi_start_image() returned: 26' in ''.join(output)
+ output = u_boot_console.run_command_list([
+ 'efidebug boot next 1',
+ 'bootefi bootmgr'])
+ assert 'Hello, world!' in ''.join(output)
+
+ def test_efi_signed_image_auth3(self, u_boot_console, efi_boot_env):
+ """
+ Test Case 3 - rejected by dbx (TEST_db certificate in dbx)
+ """
+ u_boot_console.restart_uboot()
+ disk_img = efi_boot_env
+ with u_boot_console.log.section('Test Case 3a'):
+ # Test Case 3a, rejected by dbx
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % disk_img,
+ 'fatload host 0:1 4000000 db.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize dbx',
+ 'fatload host 0:1 4000000 KEK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK',
+ 'fatload host 0:1 4000000 PK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+ output = u_boot_console.run_command_list([
+ 'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed -s ""',
+ 'efidebug boot next 1',
+ 'efidebug test bootmgr'])
+ assert '\'HELLO\' failed' in ''.join(output)
+ assert 'efi_start_image() returned: 26' in ''.join(output)
+
+ with u_boot_console.log.section('Test Case 3b'):
+ # Test Case 3b, rejected by dbx even if db allows
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 db.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+ output = u_boot_console.run_command_list([
+ 'efidebug boot next 1',
+ 'efidebug test bootmgr'])
+ assert '\'HELLO\' failed' in ''.join(output)
+ assert 'efi_start_image() returned: 26' in ''.join(output)
+
+ def test_efi_signed_image_auth4(self, u_boot_console, efi_boot_env):
+ """
+ Test Case 4 - revoked by dbx (digest of TEST_db certificate in dbx)
+ """
+ u_boot_console.restart_uboot()
+ disk_img = efi_boot_env
+ with u_boot_console.log.section('Test Case 4'):
+ # Test Case 4, rejected by dbx
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % disk_img,
+ 'fatload host 0:1 4000000 dbx_hash.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize dbx',
+ 'fatload host 0:1 4000000 db.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db',
+ 'fatload host 0:1 4000000 KEK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK',
+ 'fatload host 0:1 4000000 PK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+ output = u_boot_console.run_command_list([
+ 'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed -s ""',
+ 'efidebug boot next 1',
+ 'efidebug test bootmgr'])
+ assert '\'HELLO\' failed' in ''.join(output)
+ assert 'efi_start_image() returned: 26' in ''.join(output)
+
+ def test_efi_signed_image_auth5(self, u_boot_console, efi_boot_env):
+ """
+ Test Case 5 - multiple signatures
+ one signed with TEST_db, and
+ one signed with TEST_db1
+ """
+ u_boot_console.restart_uboot()
+ disk_img = efi_boot_env
+ with u_boot_console.log.section('Test Case 5a'):
+ # Test Case 5a, authenticated even if only one of signatures
+ # is verified
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % disk_img,
+ 'fatload host 0:1 4000000 db.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db',
+ 'fatload host 0:1 4000000 KEK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK',
+ 'fatload host 0:1 4000000 PK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+ output = u_boot_console.run_command_list([
+ 'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed_2sigs -s ""',
+ 'efidebug boot next 1',
+ 'efidebug test bootmgr'])
+ assert 'Hello, world!' in ''.join(output)
+
+ with u_boot_console.log.section('Test Case 5b'):
+ # Test Case 5b, authenticated if both signatures are verified
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 db1.auth',
+ 'setenv -e -nv -bs -rt -at -a -i 4000000:$filesize db'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+ output = u_boot_console.run_command_list([
+ 'efidebug boot next 1',
+ 'efidebug test bootmgr'])
+ assert 'Hello, world!' in ''.join(output)
+
+ with u_boot_console.log.section('Test Case 5c'):
+ # Test Case 5c, not rejected if one of signatures (digest of
+ # certificate) is revoked
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 dbx_hash.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize dbx'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+ output = u_boot_console.run_command_list([
+ 'efidebug boot next 1',
+ 'efidebug test bootmgr'])
+ assert 'Hello, world!' in ''.join(output)
+
+ with u_boot_console.log.section('Test Case 5d'):
+ # Test Case 5d, rejected if both of signatures are revoked
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 dbx_hash1.auth',
+ 'setenv -e -nv -bs -rt -at -a -i 4000000:$filesize dbx'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+ output = u_boot_console.run_command_list([
+ 'efidebug boot next 1',
+ 'efidebug test bootmgr'])
+ assert '\'HELLO\' failed' in ''.join(output)
+ assert 'efi_start_image() returned: 26' in ''.join(output)
+
+ def test_efi_signed_image_auth6(self, u_boot_console, efi_boot_env):
+ """
+ Test Case 6 - using digest of signed image in database
+ """
+ u_boot_console.restart_uboot()
+ disk_img = efi_boot_env
+ with u_boot_console.log.section('Test Case 6a'):
+ # Test Case 6a, verified by image's digest in db
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % disk_img,
+ 'fatload host 0:1 4000000 db_hello_signed.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db',
+ 'fatload host 0:1 4000000 KEK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK',
+ 'fatload host 0:1 4000000 PK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+ output = u_boot_console.run_command_list([
+ 'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed -s ""',
+ 'efidebug boot next 1',
+ 'bootefi bootmgr'])
+ assert 'Hello, world!' in ''.join(output)
+
+ with u_boot_console.log.section('Test Case 6b'):
+ # Test Case 6b, rejected by TEST_db certificate in dbx
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 dbx_db.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize dbx'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+ output = u_boot_console.run_command_list([
+ 'efidebug boot next 1',
+ 'efidebug test bootmgr'])
+ assert '\'HELLO\' failed' in ''.join(output)
+ assert 'efi_start_image() returned: 26' in ''.join(output)
+
+ with u_boot_console.log.section('Test Case 6c'):
+ # Test Case 6c, rejected by image's digest in dbx
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 db.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db',
+ 'fatload host 0:1 4000000 dbx_hello_signed.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize dbx'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+ output = u_boot_console.run_command_list([
+ 'efidebug boot next 1',
+ 'efidebug test bootmgr'])
+ assert '\'HELLO\' failed' in ''.join(output)
+ assert 'efi_start_image() returned: 26' in ''.join(output)
diff --git a/roms/u-boot/test/py/tests/test_efi_secboot/test_signed_intca.py b/roms/u-boot/test/py/tests/test_efi_secboot/test_signed_intca.py
new file mode 100644
index 000000000..d8d599d22
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_efi_secboot/test_signed_intca.py
@@ -0,0 +1,135 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2020, Linaro Limited
+# Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
+#
+# U-Boot UEFI: Image Authentication Test (signature with certificates chain)
+
+"""
+This test verifies image authentication for a signed image which is signed
+by user certificate and contains additional intermediate certificates in its
+signature.
+"""
+
+import pytest
+
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('efi_secure_boot')
+@pytest.mark.buildconfigspec('cmd_efidebug')
+@pytest.mark.buildconfigspec('cmd_fat')
+@pytest.mark.buildconfigspec('cmd_nvedit_efi')
+@pytest.mark.slow
+class TestEfiSignedImageIntca(object):
+ def test_efi_signed_image_intca1(self, u_boot_console, efi_boot_env_intca):
+ """
+ Test Case 1 - authenticated by root CA in db
+ """
+ u_boot_console.restart_uboot()
+ disk_img = efi_boot_env_intca
+ with u_boot_console.log.section('Test Case 1a'):
+ # Test Case 1a, with no Int CA and not authenticated by root CA
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % disk_img,
+ 'fatload host 0:1 4000000 db_c.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db',
+ 'fatload host 0:1 4000000 KEK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK',
+ 'fatload host 0:1 4000000 PK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+
+ output = u_boot_console.run_command_list([
+ 'efidebug boot add -b 1 HELLO_a host 0:1 /helloworld.efi.signed_a -s ""',
+ 'efidebug boot next 1',
+ 'efidebug test bootmgr'])
+ assert '\'HELLO_a\' failed' in ''.join(output)
+ assert 'efi_start_image() returned: 26' in ''.join(output)
+
+ with u_boot_console.log.section('Test Case 1b'):
+ # Test Case 1b, signed and authenticated by root CA
+ output = u_boot_console.run_command_list([
+ 'efidebug boot add -b 2 HELLO_ab host 0:1 /helloworld.efi.signed_ab -s ""',
+ 'efidebug boot next 2',
+ 'bootefi bootmgr'])
+ assert 'Hello, world!' in ''.join(output)
+
+ def test_efi_signed_image_intca2(self, u_boot_console, efi_boot_env_intca):
+ """
+ Test Case 2 - authenticated by root CA in db
+ """
+ u_boot_console.restart_uboot()
+ disk_img = efi_boot_env_intca
+ with u_boot_console.log.section('Test Case 2a'):
+ # Test Case 2a, unsigned and not authenticated by root CA
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % disk_img,
+ 'fatload host 0:1 4000000 KEK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK',
+ 'fatload host 0:1 4000000 PK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+
+ output = u_boot_console.run_command_list([
+ 'efidebug boot add -b 1 HELLO_abc host 0:1 /helloworld.efi.signed_abc -s ""',
+ 'efidebug boot next 1',
+ 'efidebug test bootmgr'])
+ assert '\'HELLO_abc\' failed' in ''.join(output)
+ assert 'efi_start_image() returned: 26' in ''.join(output)
+
+ with u_boot_console.log.section('Test Case 2b'):
+ # Test Case 2b, signed and authenticated by root CA
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 db_b.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db',
+ 'efidebug boot next 1',
+ 'efidebug test bootmgr'])
+ assert '\'HELLO_abc\' failed' in ''.join(output)
+ assert 'efi_start_image() returned: 26' in ''.join(output)
+
+ with u_boot_console.log.section('Test Case 2c'):
+ # Test Case 2c, signed and authenticated by root CA
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 db_c.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db',
+ 'efidebug boot next 1',
+ 'efidebug test bootmgr'])
+ assert 'Hello, world!' in ''.join(output)
+
+ def test_efi_signed_image_intca3(self, u_boot_console, efi_boot_env_intca):
+ """
+ Test Case 3 - revoked by dbx
+ """
+ u_boot_console.restart_uboot()
+ disk_img = efi_boot_env_intca
+ with u_boot_console.log.section('Test Case 3a'):
+ # Test Case 3a, revoked by int CA in dbx
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % disk_img,
+ 'fatload host 0:1 4000000 dbx_b.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize dbx',
+ 'fatload host 0:1 4000000 db_c.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db',
+ 'fatload host 0:1 4000000 KEK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK',
+ 'fatload host 0:1 4000000 PK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+
+ output = u_boot_console.run_command_list([
+ 'efidebug boot add -b 1 HELLO_abc host 0:1 /helloworld.efi.signed_abc -s ""',
+ 'efidebug boot next 1',
+ 'efidebug test bootmgr'])
+ assert 'Hello, world!' in ''.join(output)
+ # Or,
+ # assert '\'HELLO_abc\' failed' in ''.join(output)
+ # assert 'efi_start_image() returned: 26' in ''.join(output)
+
+ with u_boot_console.log.section('Test Case 3b'):
+ # Test Case 3b, revoked by root CA in dbx
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 dbx_c.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize dbx',
+ 'efidebug boot next 1',
+ 'efidebug test bootmgr'])
+ assert '\'HELLO_abc\' failed' in ''.join(output)
+ assert 'efi_start_image() returned: 26' in ''.join(output)
diff --git a/roms/u-boot/test/py/tests/test_efi_secboot/test_unsigned.py b/roms/u-boot/test/py/tests/test_efi_secboot/test_unsigned.py
new file mode 100644
index 000000000..df63f0df0
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_efi_secboot/test_unsigned.py
@@ -0,0 +1,117 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2019, Linaro Limited
+# Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
+#
+# U-Boot UEFI: Signed Image Authentication Test
+
+"""
+This test verifies image authentication for unsigned images.
+"""
+
+import pytest
+
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('efi_secure_boot')
+@pytest.mark.buildconfigspec('cmd_efidebug')
+@pytest.mark.buildconfigspec('cmd_fat')
+@pytest.mark.buildconfigspec('cmd_nvedit_efi')
+@pytest.mark.slow
+class TestEfiUnsignedImage(object):
+ def test_efi_unsigned_image_auth1(self, u_boot_console, efi_boot_env):
+ """
+ Test Case 1 - rejected when not digest in db or dbx
+ """
+ u_boot_console.restart_uboot()
+ disk_img = efi_boot_env
+ with u_boot_console.log.section('Test Case 1'):
+ # Test Case 1
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % disk_img,
+ 'fatload host 0:1 4000000 KEK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK',
+ 'fatload host 0:1 4000000 PK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+
+ output = u_boot_console.run_command_list([
+ 'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi -s ""',
+ 'efidebug boot next 1',
+ 'bootefi bootmgr'])
+ assert '\'HELLO\' failed' in ''.join(output)
+ output = u_boot_console.run_command_list([
+ 'efidebug boot next 1',
+ 'efidebug test bootmgr'])
+ assert 'efi_start_image() returned: 26' in ''.join(output)
+ assert 'Hello, world!' not in ''.join(output)
+
+ def test_efi_unsigned_image_auth2(self, u_boot_console, efi_boot_env):
+ """
+ Test Case 2 - authenticated by digest in db
+ """
+ u_boot_console.restart_uboot()
+ disk_img = efi_boot_env
+ with u_boot_console.log.section('Test Case 2'):
+ # Test Case 2
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % disk_img,
+ 'fatload host 0:1 4000000 db_hello.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db',
+ 'fatload host 0:1 4000000 KEK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK',
+ 'fatload host 0:1 4000000 PK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+
+ output = u_boot_console.run_command_list([
+ 'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi -s ""',
+ 'efidebug boot next 1',
+ 'bootefi bootmgr'])
+ assert 'Hello, world!' in ''.join(output)
+
+ def test_efi_unsigned_image_auth3(self, u_boot_console, efi_boot_env):
+ """
+ Test Case 3 - rejected by digest in dbx
+ """
+ u_boot_console.restart_uboot()
+ disk_img = efi_boot_env
+ with u_boot_console.log.section('Test Case 3a'):
+ # Test Case 3a, rejected by dbx
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % disk_img,
+ 'fatload host 0:1 4000000 db_hello.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize dbx',
+ 'fatload host 0:1 4000000 KEK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK',
+ 'fatload host 0:1 4000000 PK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+
+ output = u_boot_console.run_command_list([
+ 'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi -s ""',
+ 'efidebug boot next 1',
+ 'bootefi bootmgr'])
+ assert '\'HELLO\' failed' in ''.join(output)
+ output = u_boot_console.run_command_list([
+ 'efidebug boot next 1',
+ 'efidebug test bootmgr'])
+ assert 'efi_start_image() returned: 26' in ''.join(output)
+ assert 'Hello, world!' not in ''.join(output)
+
+ with u_boot_console.log.section('Test Case 3b'):
+ # Test Case 3b, rejected by dbx even if db allows
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 db_hello.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+
+ output = u_boot_console.run_command_list([
+ 'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi -s ""',
+ 'efidebug boot next 1',
+ 'bootefi bootmgr'])
+ assert '\'HELLO\' failed' in ''.join(output)
+ output = u_boot_console.run_command_list([
+ 'efidebug boot next 1',
+ 'efidebug test bootmgr'])
+ assert 'efi_start_image() returned: 26' in ''.join(output)
+ assert 'Hello, world!' not in ''.join(output)
diff --git a/roms/u-boot/test/py/tests/test_efi_selftest.py b/roms/u-boot/test/py/tests/test_efi_selftest.py
new file mode 100644
index 000000000..63218efbc
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_efi_selftest.py
@@ -0,0 +1,214 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2017, Heinrich Schuchardt <xypron.glpk@gmx.de>
+
+"""
+Test UEFI API implementation
+"""
+
+import pytest
+
+@pytest.mark.buildconfigspec('cmd_bootefi_selftest')
+def test_efi_selftest(u_boot_console):
+ """Run UEFI unit tests
+
+ :param u_boot_console: U-Boot console
+
+ This function executes all selftests that are not marked as on request.
+ """
+ u_boot_console.run_command(cmd='setenv efi_selftest')
+ u_boot_console.run_command(cmd='bootefi selftest', wait_for_prompt=False)
+ m = u_boot_console.p.expect(['Summary: 0 failures', 'Press any key'])
+ if m != 0:
+ raise Exception('Failures occurred during the EFI selftest')
+ u_boot_console.restart_uboot()
+
+@pytest.mark.buildconfigspec('cmd_bootefi_selftest')
+@pytest.mark.buildconfigspec('hush_parser')
+@pytest.mark.buildconfigspec('of_control')
+@pytest.mark.notbuildconfigspec('generate_acpi_table')
+def test_efi_selftest_device_tree(u_boot_console):
+ """Test the device tree support in the UEFI sub-system
+
+ :param u_boot_console: U-Boot console
+
+ This test executes the UEFI unit test by calling 'bootefi selftest'.
+ """
+ u_boot_console.run_command(cmd='setenv efi_selftest list')
+ output = u_boot_console.run_command('bootefi selftest')
+ assert '\'device tree\'' in output
+ u_boot_console.run_command(cmd='setenv efi_selftest device tree')
+ # Set serial# if it is not already set.
+ u_boot_console.run_command(cmd='setenv efi_test "${serial#}x"')
+ u_boot_console.run_command(cmd='test "${efi_test}" = x && setenv serial# 0')
+ u_boot_console.run_command(cmd='bootefi selftest ${fdtcontroladdr}', wait_for_prompt=False)
+ m = u_boot_console.p.expect(['serial-number:', 'U-Boot'])
+ if m != 0:
+ raise Exception('serial-number missing in device tree')
+ u_boot_console.restart_uboot()
+
+@pytest.mark.buildconfigspec('cmd_bootefi_selftest')
+def test_efi_selftest_watchdog_reboot(u_boot_console):
+ """Test the watchdog timer
+
+ :param u_boot_console: U-Boot console
+
+ This function executes the 'watchdog reboot' unit test.
+ """
+ u_boot_console.run_command(cmd='setenv efi_selftest list')
+ output = u_boot_console.run_command('bootefi selftest')
+ assert '\'watchdog reboot\'' in output
+ u_boot_console.run_command(cmd='setenv efi_selftest watchdog reboot')
+ u_boot_console.run_command(cmd='bootefi selftest', wait_for_prompt=False)
+ m = u_boot_console.p.expect(['resetting', 'U-Boot'])
+ if m != 0:
+ raise Exception('Reset failed in \'watchdog reboot\' test')
+ u_boot_console.restart_uboot()
+
+@pytest.mark.buildconfigspec('cmd_bootefi_selftest')
+def test_efi_selftest_text_input(u_boot_console):
+ """Test the EFI_SIMPLE_TEXT_INPUT_PROTOCOL
+
+ :param u_boot_console: U-Boot console
+
+ This function calls the text input EFI selftest.
+ """
+ u_boot_console.run_command(cmd='setenv efi_selftest text input')
+ output = u_boot_console.run_command(cmd='bootefi selftest',
+ wait_for_prompt=False)
+ m = u_boot_console.p.expect([r'To terminate type \'x\''])
+ if m != 0:
+ raise Exception('No prompt for \'text input\' test')
+ u_boot_console.drain_console()
+ u_boot_console.p.timeout = 500
+ # EOT
+ u_boot_console.run_command(cmd=chr(4), wait_for_echo=False,
+ send_nl=False, wait_for_prompt=False)
+ m = u_boot_console.p.expect(
+ [r'Unicode char 4 \(unknown\), scan code 0 \(Null\)'])
+ if m != 0:
+ raise Exception('EOT failed in \'text input\' test')
+ u_boot_console.drain_console()
+ # BS
+ u_boot_console.run_command(cmd=chr(8), wait_for_echo=False,
+ send_nl=False, wait_for_prompt=False)
+ m = u_boot_console.p.expect(
+ [r'Unicode char 8 \(BS\), scan code 0 \(Null\)'])
+ if m != 0:
+ raise Exception('BS failed in \'text input\' test')
+ u_boot_console.drain_console()
+ # TAB
+ u_boot_console.run_command(cmd=chr(9), wait_for_echo=False,
+ send_nl=False, wait_for_prompt=False)
+ m = u_boot_console.p.expect(
+ [r'Unicode char 9 \(TAB\), scan code 0 \(Null\)'])
+ if m != 0:
+ raise Exception('BS failed in \'text input\' test')
+ u_boot_console.drain_console()
+ # a
+ u_boot_console.run_command(cmd='a', wait_for_echo=False, send_nl=False,
+ wait_for_prompt=False)
+ m = u_boot_console.p.expect(
+ [r'Unicode char 97 \(\'a\'\), scan code 0 \(Null\)'])
+ if m != 0:
+ raise Exception('\'a\' failed in \'text input\' test')
+ u_boot_console.drain_console()
+ # UP escape sequence
+ u_boot_console.run_command(cmd=chr(27) + '[A', wait_for_echo=False,
+ send_nl=False, wait_for_prompt=False)
+ m = u_boot_console.p.expect(
+ [r'Unicode char 0 \(Null\), scan code 1 \(Up\)'])
+ if m != 0:
+ raise Exception('UP failed in \'text input\' test')
+ u_boot_console.drain_console()
+ # Euro sign
+ u_boot_console.run_command(cmd=b'\xe2\x82\xac'.decode(), wait_for_echo=False,
+ send_nl=False, wait_for_prompt=False)
+ m = u_boot_console.p.expect([r'Unicode char 8364 \(\''])
+ if m != 0:
+ raise Exception('Euro sign failed in \'text input\' test')
+ u_boot_console.drain_console()
+ u_boot_console.run_command(cmd='x', wait_for_echo=False, send_nl=False,
+ wait_for_prompt=False)
+ m = u_boot_console.p.expect(['Summary: 0 failures', 'Press any key'])
+ if m != 0:
+ raise Exception('Failures occurred during the EFI selftest')
+ u_boot_console.restart_uboot()
+
+@pytest.mark.buildconfigspec('cmd_bootefi_selftest')
+def test_efi_selftest_text_input_ex(u_boot_console):
+ """Test the EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL
+
+ :param u_boot_console: U-Boot console
+
+ This function calls the extended text input EFI selftest.
+ """
+ u_boot_console.run_command(cmd='setenv efi_selftest extended text input')
+ output = u_boot_console.run_command(cmd='bootefi selftest',
+ wait_for_prompt=False)
+ m = u_boot_console.p.expect([r'To terminate type \'CTRL\+x\''])
+ if m != 0:
+ raise Exception('No prompt for \'text input\' test')
+ u_boot_console.drain_console()
+ u_boot_console.p.timeout = 500
+ # EOT
+ u_boot_console.run_command(cmd=chr(4), wait_for_echo=False,
+ send_nl=False, wait_for_prompt=False)
+ m = u_boot_console.p.expect(
+ [r'Unicode char 100 \(\'d\'\), scan code 0 \(CTRL\+Null\)'])
+ if m != 0:
+ raise Exception('EOT failed in \'text input\' test')
+ u_boot_console.drain_console()
+ # BS
+ u_boot_console.run_command(cmd=chr(8), wait_for_echo=False,
+ send_nl=False, wait_for_prompt=False)
+ m = u_boot_console.p.expect(
+ [r'Unicode char 8 \(BS\), scan code 0 \(\+Null\)'])
+ if m != 0:
+ raise Exception('BS failed in \'text input\' test')
+ u_boot_console.drain_console()
+ # TAB
+ u_boot_console.run_command(cmd=chr(9), wait_for_echo=False,
+ send_nl=False, wait_for_prompt=False)
+ m = u_boot_console.p.expect(
+ [r'Unicode char 9 \(TAB\), scan code 0 \(\+Null\)'])
+ if m != 0:
+ raise Exception('TAB failed in \'text input\' test')
+ u_boot_console.drain_console()
+ # a
+ u_boot_console.run_command(cmd='a', wait_for_echo=False, send_nl=False,
+ wait_for_prompt=False)
+ m = u_boot_console.p.expect(
+ [r'Unicode char 97 \(\'a\'\), scan code 0 \(Null\)'])
+ if m != 0:
+ raise Exception('\'a\' failed in \'text input\' test')
+ u_boot_console.drain_console()
+ # UP escape sequence
+ u_boot_console.run_command(cmd=chr(27) + '[A', wait_for_echo=False,
+ send_nl=False, wait_for_prompt=False)
+ m = u_boot_console.p.expect(
+ [r'Unicode char 0 \(Null\), scan code 1 \(\+Up\)'])
+ if m != 0:
+ raise Exception('UP failed in \'text input\' test')
+ u_boot_console.drain_console()
+ # Euro sign
+ u_boot_console.run_command(cmd=b'\xe2\x82\xac'.decode(), wait_for_echo=False,
+ send_nl=False, wait_for_prompt=False)
+ m = u_boot_console.p.expect([r'Unicode char 8364 \(\''])
+ if m != 0:
+ raise Exception('Euro sign failed in \'text input\' test')
+ u_boot_console.drain_console()
+ # SHIFT+ALT+FN 5
+ u_boot_console.run_command(cmd=b'\x1b\x5b\x31\x35\x3b\x34\x7e'.decode(),
+ wait_for_echo=False, send_nl=False,
+ wait_for_prompt=False)
+ m = u_boot_console.p.expect(
+ [r'Unicode char 0 \(Null\), scan code 15 \(SHIFT\+ALT\+FN 5\)'])
+ if m != 0:
+ raise Exception('SHIFT+ALT+FN 5 failed in \'text input\' test')
+ u_boot_console.drain_console()
+ u_boot_console.run_command(cmd=chr(24), wait_for_echo=False, send_nl=False,
+ wait_for_prompt=False)
+ m = u_boot_console.p.expect(['Summary: 0 failures', 'Press any key'])
+ if m != 0:
+ raise Exception('Failures occurred during the EFI selftest')
+ u_boot_console.restart_uboot()
diff --git a/roms/u-boot/test/py/tests/test_env.py b/roms/u-boot/test/py/tests/test_env.py
new file mode 100644
index 000000000..9bed2f48d
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_env.py
@@ -0,0 +1,517 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2015 Stephen Warren
+# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved.
+
+# Test operation of shell commands relating to environment variables.
+
+import os
+import os.path
+from subprocess import call, check_call, CalledProcessError
+
+import pytest
+import u_boot_utils
+
+# FIXME: This might be useful for other tests;
+# perhaps refactor it into ConsoleBase or some other state object?
+class StateTestEnv(object):
+ """Container that represents the state of all U-Boot environment variables.
+ This enables quick determination of existant/non-existant variable
+ names.
+ """
+
+ def __init__(self, u_boot_console):
+ """Initialize a new StateTestEnv object.
+
+ Args:
+ u_boot_console: A U-Boot console.
+
+ Returns:
+ Nothing.
+ """
+
+ self.u_boot_console = u_boot_console
+ self.get_env()
+ self.set_var = self.get_non_existent_var()
+
+ def get_env(self):
+ """Read all current environment variables from U-Boot.
+
+ Args:
+ None.
+
+ Returns:
+ Nothing.
+ """
+
+ if self.u_boot_console.config.buildconfig.get(
+ 'config_version_variable', 'n') == 'y':
+ with self.u_boot_console.disable_check('main_signon'):
+ response = self.u_boot_console.run_command('printenv')
+ else:
+ response = self.u_boot_console.run_command('printenv')
+ self.env = {}
+ for l in response.splitlines():
+ if not '=' in l:
+ continue
+ (var, value) = l.split('=', 1)
+ self.env[var] = value
+
+ def get_existent_var(self):
+ """Return the name of an environment variable that exists.
+
+ Args:
+ None.
+
+ Returns:
+ The name of an environment variable.
+ """
+
+ for var in self.env:
+ return var
+
+ def get_non_existent_var(self):
+ """Return the name of an environment variable that does not exist.
+
+ Args:
+ None.
+
+ Returns:
+ The name of an environment variable.
+ """
+
+ n = 0
+ while True:
+ var = 'test_env_' + str(n)
+ if var not in self.env:
+ return var
+ n += 1
+
+ste = None
+@pytest.fixture(scope='function')
+def state_test_env(u_boot_console):
+ """pytest fixture to provide a StateTestEnv object to tests."""
+
+ global ste
+ if not ste:
+ ste = StateTestEnv(u_boot_console)
+ return ste
+
+def unset_var(state_test_env, var):
+ """Unset an environment variable.
+
+ This both executes a U-Boot shell command and updates a StateTestEnv
+ object.
+
+ Args:
+ state_test_env: The StateTestEnv object to update.
+ var: The variable name to unset.
+
+ Returns:
+ Nothing.
+ """
+
+ state_test_env.u_boot_console.run_command('setenv %s' % var)
+ if var in state_test_env.env:
+ del state_test_env.env[var]
+
+def set_var(state_test_env, var, value):
+ """Set an environment variable.
+
+ This both executes a U-Boot shell command and updates a StateTestEnv
+ object.
+
+ Args:
+ state_test_env: The StateTestEnv object to update.
+ var: The variable name to set.
+ value: The value to set the variable to.
+
+ Returns:
+ Nothing.
+ """
+
+ bc = state_test_env.u_boot_console.config.buildconfig
+ if bc.get('config_hush_parser', None):
+ quote = '"'
+ else:
+ quote = ''
+ if ' ' in value:
+ pytest.skip('Space in variable value on non-Hush shell')
+
+ state_test_env.u_boot_console.run_command(
+ 'setenv %s %s%s%s' % (var, quote, value, quote))
+ state_test_env.env[var] = value
+
+def validate_empty(state_test_env, var):
+ """Validate that a variable is not set, using U-Boot shell commands.
+
+ Args:
+ var: The variable name to test.
+
+ Returns:
+ Nothing.
+ """
+
+ response = state_test_env.u_boot_console.run_command('echo ${%s}' % var)
+ assert response == ''
+
+def validate_set(state_test_env, var, value):
+ """Validate that a variable is set, using U-Boot shell commands.
+
+ Args:
+ var: The variable name to test.
+ value: The value the variable is expected to have.
+
+ Returns:
+ Nothing.
+ """
+
+ # echo does not preserve leading, internal, or trailing whitespace in the
+ # value. printenv does, and hence allows more complete testing.
+ response = state_test_env.u_boot_console.run_command('printenv %s' % var)
+ assert response == ('%s=%s' % (var, value))
+
+def test_env_echo_exists(state_test_env):
+ """Test echoing a variable that exists."""
+
+ var = state_test_env.get_existent_var()
+ value = state_test_env.env[var]
+ validate_set(state_test_env, var, value)
+
+@pytest.mark.buildconfigspec('cmd_echo')
+def test_env_echo_non_existent(state_test_env):
+ """Test echoing a variable that doesn't exist."""
+
+ var = state_test_env.set_var
+ validate_empty(state_test_env, var)
+
+def test_env_printenv_non_existent(state_test_env):
+ """Test printenv error message for non-existant variables."""
+
+ var = state_test_env.set_var
+ c = state_test_env.u_boot_console
+ with c.disable_check('error_notification'):
+ response = c.run_command('printenv %s' % var)
+ assert(response == '## Error: "%s" not defined' % var)
+
+@pytest.mark.buildconfigspec('cmd_echo')
+def test_env_unset_non_existent(state_test_env):
+ """Test unsetting a nonexistent variable."""
+
+ var = state_test_env.get_non_existent_var()
+ unset_var(state_test_env, var)
+ validate_empty(state_test_env, var)
+
+def test_env_set_non_existent(state_test_env):
+ """Test set a non-existant variable."""
+
+ var = state_test_env.set_var
+ value = 'foo'
+ set_var(state_test_env, var, value)
+ validate_set(state_test_env, var, value)
+
+def test_env_set_existing(state_test_env):
+ """Test setting an existant variable."""
+
+ var = state_test_env.set_var
+ value = 'bar'
+ set_var(state_test_env, var, value)
+ validate_set(state_test_env, var, value)
+
+@pytest.mark.buildconfigspec('cmd_echo')
+def test_env_unset_existing(state_test_env):
+ """Test unsetting a variable."""
+
+ var = state_test_env.set_var
+ unset_var(state_test_env, var)
+ validate_empty(state_test_env, var)
+
+def test_env_expansion_spaces(state_test_env):
+ """Test expanding a variable that contains a space in its value."""
+
+ var_space = None
+ var_test = None
+ try:
+ var_space = state_test_env.get_non_existent_var()
+ set_var(state_test_env, var_space, ' ')
+
+ var_test = state_test_env.get_non_existent_var()
+ value = ' 1${%(var_space)s}${%(var_space)s} 2 ' % locals()
+ set_var(state_test_env, var_test, value)
+ value = ' 1 2 '
+ validate_set(state_test_env, var_test, value)
+ finally:
+ if var_space:
+ unset_var(state_test_env, var_space)
+ if var_test:
+ unset_var(state_test_env, var_test)
+
+@pytest.mark.buildconfigspec('cmd_importenv')
+def test_env_import_checksum_no_size(state_test_env):
+ """Test that omitted ('-') size parameter with checksum validation fails the
+ env import function.
+ """
+ c = state_test_env.u_boot_console
+ ram_base = u_boot_utils.find_ram_base(state_test_env.u_boot_console)
+ addr = '%08x' % ram_base
+
+ with c.disable_check('error_notification'):
+ response = c.run_command('env import -c %s -' % addr)
+ assert(response == '## Error: external checksum format must pass size')
+
+@pytest.mark.buildconfigspec('cmd_importenv')
+def test_env_import_whitelist_checksum_no_size(state_test_env):
+ """Test that omitted ('-') size parameter with checksum validation fails the
+ env import function when variables are passed as parameters.
+ """
+ c = state_test_env.u_boot_console
+ ram_base = u_boot_utils.find_ram_base(state_test_env.u_boot_console)
+ addr = '%08x' % ram_base
+
+ with c.disable_check('error_notification'):
+ response = c.run_command('env import -c %s - foo1 foo2 foo4' % addr)
+ assert(response == '## Error: external checksum format must pass size')
+
+@pytest.mark.buildconfigspec('cmd_exportenv')
+@pytest.mark.buildconfigspec('cmd_importenv')
+def test_env_import_whitelist(state_test_env):
+ """Test importing only a handful of env variables from an environment."""
+ c = state_test_env.u_boot_console
+ ram_base = u_boot_utils.find_ram_base(state_test_env.u_boot_console)
+ addr = '%08x' % ram_base
+
+ set_var(state_test_env, 'foo1', 'bar1')
+ set_var(state_test_env, 'foo2', 'bar2')
+ set_var(state_test_env, 'foo3', 'bar3')
+
+ c.run_command('env export %s' % addr)
+
+ unset_var(state_test_env, 'foo1')
+ set_var(state_test_env, 'foo2', 'test2')
+ set_var(state_test_env, 'foo4', 'bar4')
+
+ # no foo1 in current env, foo2 overridden, foo3 should be of the value
+ # before exporting and foo4 should be of the value before importing.
+ c.run_command('env import %s - foo1 foo2 foo4' % addr)
+
+ validate_set(state_test_env, 'foo1', 'bar1')
+ validate_set(state_test_env, 'foo2', 'bar2')
+ validate_set(state_test_env, 'foo3', 'bar3')
+ validate_set(state_test_env, 'foo4', 'bar4')
+
+ # Cleanup test environment
+ unset_var(state_test_env, 'foo1')
+ unset_var(state_test_env, 'foo2')
+ unset_var(state_test_env, 'foo3')
+ unset_var(state_test_env, 'foo4')
+
+@pytest.mark.buildconfigspec('cmd_exportenv')
+@pytest.mark.buildconfigspec('cmd_importenv')
+def test_env_import_whitelist_delete(state_test_env):
+
+ """Test importing only a handful of env variables from an environment, with.
+ deletion if a var A that is passed to env import is not in the
+ environment to be imported.
+ """
+ c = state_test_env.u_boot_console
+ ram_base = u_boot_utils.find_ram_base(state_test_env.u_boot_console)
+ addr = '%08x' % ram_base
+
+ set_var(state_test_env, 'foo1', 'bar1')
+ set_var(state_test_env, 'foo2', 'bar2')
+ set_var(state_test_env, 'foo3', 'bar3')
+
+ c.run_command('env export %s' % addr)
+
+ unset_var(state_test_env, 'foo1')
+ set_var(state_test_env, 'foo2', 'test2')
+ set_var(state_test_env, 'foo4', 'bar4')
+
+ # no foo1 in current env, foo2 overridden, foo3 should be of the value
+ # before exporting and foo4 should be empty.
+ c.run_command('env import -d %s - foo1 foo2 foo4' % addr)
+
+ validate_set(state_test_env, 'foo1', 'bar1')
+ validate_set(state_test_env, 'foo2', 'bar2')
+ validate_set(state_test_env, 'foo3', 'bar3')
+ validate_empty(state_test_env, 'foo4')
+
+ # Cleanup test environment
+ unset_var(state_test_env, 'foo1')
+ unset_var(state_test_env, 'foo2')
+ unset_var(state_test_env, 'foo3')
+ unset_var(state_test_env, 'foo4')
+
+@pytest.mark.buildconfigspec('cmd_nvedit_info')
+def test_env_info(state_test_env):
+
+ """Test 'env info' command with all possible options.
+ """
+ c = state_test_env.u_boot_console
+
+ response = c.run_command('env info')
+ nb_line = 0
+ for l in response.split('\n'):
+ if 'env_valid = ' in l:
+ assert '= invalid' in l or '= valid' in l or '= redundant' in l
+ nb_line += 1
+ elif 'env_ready =' in l or 'env_use_default =' in l:
+ assert '= true' in l or '= false' in l
+ nb_line += 1
+ else:
+ assert true
+ assert nb_line == 3
+
+ response = c.run_command('env info -p -d')
+ assert 'Default environment is used' in response or "Environment was loaded from persistent storage" in response
+ assert 'Environment can be persisted' in response or "Environment cannot be persisted" in response
+
+ response = c.run_command('env info -p -d -q')
+ assert response == ""
+
+ response = c.run_command('env info -p -q')
+ assert response == ""
+
+ response = c.run_command('env info -d -q')
+ assert response == ""
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('cmd_nvedit_info')
+@pytest.mark.buildconfigspec('cmd_echo')
+def test_env_info_sandbox(state_test_env):
+ """Test 'env info' command result with several options on sandbox
+ with a known ENV configuration: ready & default & persistent
+ """
+ c = state_test_env.u_boot_console
+
+ response = c.run_command('env info')
+ assert 'env_ready = true' in response
+ assert 'env_use_default = true' in response
+
+ response = c.run_command('env info -p -d')
+ assert 'Default environment is used' in response
+ assert 'Environment cannot be persisted' in response
+
+ response = c.run_command('env info -d -q')
+ response = c.run_command('echo $?')
+ assert response == "0"
+
+ response = c.run_command('env info -p -q')
+ response = c.run_command('echo $?')
+ assert response == "1"
+
+ response = c.run_command('env info -d -p -q')
+ response = c.run_command('echo $?')
+ assert response == "1"
+
+def mk_env_ext4(state_test_env):
+
+ """Create a empty ext4 file system volume."""
+ c = state_test_env.u_boot_console
+ filename = 'env.ext4.img'
+ persistent = c.config.persistent_data_dir + '/' + filename
+ fs_img = c.config.result_dir + '/' + filename
+
+ if os.path.exists(persistent):
+ c.log.action('Disk image file ' + persistent + ' already exists')
+ else:
+ # Some distributions do not add /sbin to the default PATH, where mkfs.ext4 lives
+ os.environ["PATH"] += os.pathsep + '/sbin'
+ try:
+ u_boot_utils.run_and_log(c, 'dd if=/dev/zero of=%s bs=1M count=16' % persistent)
+ u_boot_utils.run_and_log(c, 'mkfs.ext4 %s' % persistent)
+ sb_content = u_boot_utils.run_and_log(c, 'tune2fs -l %s' % persistent)
+ if 'metadata_csum' in sb_content:
+ u_boot_utils.run_and_log(c, 'tune2fs -O ^metadata_csum %s' % persistent)
+ except CalledProcessError:
+ call('rm -f %s' % persistent, shell=True)
+ raise
+
+ u_boot_utils.run_and_log(c, ['cp', '-f', persistent, fs_img])
+ return fs_img
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('cmd_echo')
+@pytest.mark.buildconfigspec('cmd_nvedit_info')
+@pytest.mark.buildconfigspec('cmd_nvedit_load')
+@pytest.mark.buildconfigspec('cmd_nvedit_select')
+@pytest.mark.buildconfigspec('env_is_in_ext4')
+def test_env_ext4(state_test_env):
+
+ """Test ENV in EXT4 on sandbox."""
+ c = state_test_env.u_boot_console
+ fs_img = ''
+ try:
+ fs_img = mk_env_ext4(state_test_env)
+
+ c.run_command('host bind 0 %s' % fs_img)
+
+ response = c.run_command('ext4ls host 0:0')
+ assert 'uboot.env' not in response
+
+ # force env location: EXT4 (prio 1 in sandbox)
+ response = c.run_command('env select EXT4')
+ assert 'Select Environment on EXT4: OK' in response
+
+ response = c.run_command('env save')
+ assert 'Saving Environment to EXT4' in response
+
+ response = c.run_command('env load')
+ assert 'Loading Environment from EXT4... OK' in response
+
+ response = c.run_command('ext4ls host 0:0')
+ assert '8192 uboot.env' in response
+
+ response = c.run_command('env info')
+ assert 'env_valid = valid' in response
+ assert 'env_ready = true' in response
+ assert 'env_use_default = false' in response
+
+ response = c.run_command('env info -p -d')
+ assert 'Environment was loaded from persistent storage' in response
+ assert 'Environment can be persisted' in response
+
+ response = c.run_command('env info -d -q')
+ assert response == ""
+ response = c.run_command('echo $?')
+ assert response == "1"
+
+ response = c.run_command('env info -p -q')
+ assert response == ""
+ response = c.run_command('echo $?')
+ assert response == "0"
+
+ response = c.run_command('env erase')
+ assert 'OK' in response
+
+ response = c.run_command('env load')
+ assert 'Loading Environment from EXT4... ' in response
+ assert 'bad CRC, using default environment' in response
+
+ response = c.run_command('env info')
+ assert 'env_valid = invalid' in response
+ assert 'env_ready = true' in response
+ assert 'env_use_default = true' in response
+
+ response = c.run_command('env info -p -d')
+ assert 'Default environment is used' in response
+ assert 'Environment can be persisted' in response
+
+ # restore env location: NOWHERE (prio 0 in sandbox)
+ response = c.run_command('env select nowhere')
+ assert 'Select Environment on nowhere: OK' in response
+
+ response = c.run_command('env load')
+ assert 'Loading Environment from nowhere... OK' in response
+
+ response = c.run_command('env info')
+ assert 'env_valid = invalid' in response
+ assert 'env_ready = true' in response
+ assert 'env_use_default = true' in response
+
+ response = c.run_command('env info -p -d')
+ assert 'Default environment is used' in response
+ assert 'Environment cannot be persisted' in response
+
+ finally:
+ if fs_img:
+ call('rm -f %s' % fs_img, shell=True)
diff --git a/roms/u-boot/test/py/tests/test_extension.py b/roms/u-boot/test/py/tests/test_extension.py
new file mode 100644
index 000000000..267cf2ff2
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_extension.py
@@ -0,0 +1,53 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2020
+# Author: Kory Maincent <kory.maincent@bootlin.com>
+
+# Test U-Boot's "extension" commands.
+
+import os
+import pytest
+import u_boot_utils
+
+overlay_addr = 0x1000
+
+SANDBOX_DTB='arch/sandbox/dts/sandbox.dtb'
+OVERLAY_DIR='arch/sandbox/dts/'
+
+def load_dtb(u_boot_console):
+ u_boot_console.log.action('Loading devicetree to RAM...')
+ u_boot_console.run_command('host load hostfs - $fdt_addr_r %s' % (os.path.join(u_boot_console.config.build_dir, SANDBOX_DTB)))
+ u_boot_console.run_command('fdt addr $fdt_addr_r')
+
+@pytest.mark.buildconfigspec('cmd_fdt')
+@pytest.mark.boardspec('sandbox')
+def test_extension(u_boot_console):
+ """Test the 'extension' command."""
+
+ load_dtb(u_boot_console)
+
+ output = u_boot_console.run_command('extension list')
+ assert('No extension' in output)
+
+ output = u_boot_console.run_command('extension scan')
+ assert output == 'Found 2 extension board(s).'
+
+ output = u_boot_console.run_command('extension list')
+ assert('overlay0.dtbo' in output)
+ assert('overlay1.dtbo' in output)
+
+ u_boot_console.run_command_list([
+ 'setenv extension_overlay_addr %s' % (overlay_addr),
+ 'setenv extension_overlay_cmd \'host load hostfs - ${extension_overlay_addr} %s${extension_overlay_name}\'' % (os.path.join(u_boot_console.config.build_dir, OVERLAY_DIR))])
+
+ output = u_boot_console.run_command('extension apply 0')
+ assert('bytes read' in output)
+
+ output = u_boot_console.run_command('fdt print')
+ assert('button3' in output)
+
+ output = u_boot_console.run_command('extension apply all')
+ assert('bytes read' in output)
+
+ output = u_boot_console.run_command('fdt print')
+ assert('button4' in output)
+
diff --git a/roms/u-boot/test/py/tests/test_fit.py b/roms/u-boot/test/py/tests/test_fit.py
new file mode 100755
index 000000000..6d5b43c3b
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_fit.py
@@ -0,0 +1,460 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2013, Google Inc.
+#
+# Sanity check of the FIT handling in U-Boot
+
+import os
+import pytest
+import struct
+import u_boot_utils as util
+
+# Define a base ITS which we can adjust using % and a dictionary
+base_its = '''
+/dts-v1/;
+
+/ {
+ description = "Chrome OS kernel image with one or more FDT blobs";
+ #address-cells = <1>;
+
+ images {
+ kernel-1 {
+ data = /incbin/("%(kernel)s");
+ type = "kernel";
+ arch = "sandbox";
+ os = "linux";
+ compression = "%(compression)s";
+ load = <0x40000>;
+ entry = <0x8>;
+ };
+ kernel-2 {
+ data = /incbin/("%(loadables1)s");
+ type = "kernel";
+ arch = "sandbox";
+ os = "linux";
+ compression = "none";
+ %(loadables1_load)s
+ entry = <0x0>;
+ };
+ fdt-1 {
+ description = "snow";
+ data = /incbin/("%(fdt)s");
+ type = "flat_dt";
+ arch = "sandbox";
+ %(fdt_load)s
+ compression = "%(compression)s";
+ signature-1 {
+ algo = "sha1,rsa2048";
+ key-name-hint = "dev";
+ };
+ };
+ ramdisk-1 {
+ description = "snow";
+ data = /incbin/("%(ramdisk)s");
+ type = "ramdisk";
+ arch = "sandbox";
+ os = "linux";
+ %(ramdisk_load)s
+ compression = "%(compression)s";
+ };
+ ramdisk-2 {
+ description = "snow";
+ data = /incbin/("%(loadables2)s");
+ type = "ramdisk";
+ arch = "sandbox";
+ os = "linux";
+ %(loadables2_load)s
+ compression = "none";
+ };
+ };
+ configurations {
+ default = "conf-1";
+ conf-1 {
+ kernel = "kernel-1";
+ fdt = "fdt-1";
+ %(ramdisk_config)s
+ %(loadables_config)s
+ };
+ };
+};
+'''
+
+# Define a base FDT - currently we don't use anything in this
+base_fdt = '''
+/dts-v1/;
+
+/ {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ model = "Sandbox Verified Boot Test";
+ compatible = "sandbox";
+
+ reset@0 {
+ compatible = "sandbox,reset";
+ reg = <0>;
+ };
+};
+'''
+
+# This is the U-Boot script that is run for each test. First load the FIT,
+# then run the 'bootm' command, then save out memory from the places where
+# we expect 'bootm' to write things. Then quit.
+base_script = '''
+host load hostfs 0 %(fit_addr)x %(fit)s
+fdt addr %(fit_addr)x
+bootm start %(fit_addr)x
+bootm loados
+host save hostfs 0 %(kernel_addr)x %(kernel_out)s %(kernel_size)x
+host save hostfs 0 %(fdt_addr)x %(fdt_out)s %(fdt_size)x
+host save hostfs 0 %(ramdisk_addr)x %(ramdisk_out)s %(ramdisk_size)x
+host save hostfs 0 %(loadables1_addr)x %(loadables1_out)s %(loadables1_size)x
+host save hostfs 0 %(loadables2_addr)x %(loadables2_out)s %(loadables2_size)x
+'''
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('fit_signature')
+@pytest.mark.requiredtool('dtc')
+def test_fit(u_boot_console):
+ def make_fname(leaf):
+ """Make a temporary filename
+
+ Args:
+ leaf: Leaf name of file to create (within temporary directory)
+ Return:
+ Temporary filename
+ """
+
+ return os.path.join(cons.config.build_dir, leaf)
+
+ def filesize(fname):
+ """Get the size of a file
+
+ Args:
+ fname: Filename to check
+ Return:
+ Size of file in bytes
+ """
+ return os.stat(fname).st_size
+
+ def read_file(fname):
+ """Read the contents of a file
+
+ Args:
+ fname: Filename to read
+ Returns:
+ Contents of file as a string
+ """
+ with open(fname, 'rb') as fd:
+ return fd.read()
+
+ def make_dtb():
+ """Make a sample .dts file and compile it to a .dtb
+
+ Returns:
+ Filename of .dtb file created
+ """
+ src = make_fname('u-boot.dts')
+ dtb = make_fname('u-boot.dtb')
+ with open(src, 'w') as fd:
+ fd.write(base_fdt)
+ util.run_and_log(cons, ['dtc', src, '-O', 'dtb', '-o', dtb])
+ return dtb
+
+ def make_its(params):
+ """Make a sample .its file with parameters embedded
+
+ Args:
+ params: Dictionary containing parameters to embed in the %() strings
+ Returns:
+ Filename of .its file created
+ """
+ its = make_fname('test.its')
+ with open(its, 'w') as fd:
+ print(base_its % params, file=fd)
+ return its
+
+ def make_fit(mkimage, params):
+ """Make a sample .fit file ready for loading
+
+ This creates a .its script with the selected parameters and uses mkimage to
+ turn this into a .fit image.
+
+ Args:
+ mkimage: Filename of 'mkimage' utility
+ params: Dictionary containing parameters to embed in the %() strings
+ Return:
+ Filename of .fit file created
+ """
+ fit = make_fname('test.fit')
+ its = make_its(params)
+ util.run_and_log(cons, [mkimage, '-f', its, fit])
+ with open(make_fname('u-boot.dts'), 'w') as fd:
+ fd.write(base_fdt)
+ return fit
+
+ def make_kernel(filename, text):
+ """Make a sample kernel with test data
+
+ Args:
+ filename: the name of the file you want to create
+ Returns:
+ Full path and filename of the kernel it created
+ """
+ fname = make_fname(filename)
+ data = ''
+ for i in range(100):
+ data += 'this %s %d is unlikely to boot\n' % (text, i)
+ with open(fname, 'w') as fd:
+ print(data, file=fd)
+ return fname
+
+ def make_ramdisk(filename, text):
+ """Make a sample ramdisk with test data
+
+ Returns:
+ Filename of ramdisk created
+ """
+ fname = make_fname(filename)
+ data = ''
+ for i in range(100):
+ data += '%s %d was seldom used in the middle ages\n' % (text, i)
+ with open(fname, 'w') as fd:
+ print(data, file=fd)
+ return fname
+
+ def make_compressed(filename):
+ util.run_and_log(cons, ['gzip', '-f', '-k', filename])
+ return filename + '.gz'
+
+ def find_matching(text, match):
+ """Find a match in a line of text, and return the unmatched line portion
+
+ This is used to extract a part of a line from some text. The match string
+ is used to locate the line - we use the first line that contains that
+ match text.
+
+ Once we find a match, we discard the match string itself from the line,
+ and return what remains.
+
+ TODO: If this function becomes more generally useful, we could change it
+ to use regex and return groups.
+
+ Args:
+ text: Text to check (list of strings, one for each command issued)
+ match: String to search for
+ Return:
+ String containing unmatched portion of line
+ Exceptions:
+ ValueError: If match is not found
+
+ >>> find_matching(['first line:10', 'second_line:20'], 'first line:')
+ '10'
+ >>> find_matching(['first line:10', 'second_line:20'], 'second line')
+ Traceback (most recent call last):
+ ...
+ ValueError: Test aborted
+ >>> find_matching('first line:10\', 'second_line:20'], 'second_line:')
+ '20'
+ >>> find_matching('first line:10\', 'second_line:20\nthird_line:30'],
+ 'third_line:')
+ '30'
+ """
+ __tracebackhide__ = True
+ for line in '\n'.join(text).splitlines():
+ pos = line.find(match)
+ if pos != -1:
+ return line[:pos] + line[pos + len(match):]
+
+ pytest.fail("Expected '%s' but not found in output")
+
+ def check_equal(expected_fname, actual_fname, failure_msg):
+ """Check that a file matches its expected contents
+
+ This is always used on out-buffers whose size is decided by the test
+ script anyway, which in some cases may be larger than what we're
+ actually looking for. So it's safe to truncate it to the size of the
+ expected data.
+
+ Args:
+ expected_fname: Filename containing expected contents
+ actual_fname: Filename containing actual contents
+ failure_msg: Message to print on failure
+ """
+ expected_data = read_file(expected_fname)
+ actual_data = read_file(actual_fname)
+ if len(expected_data) < len(actual_data):
+ actual_data = actual_data[:len(expected_data)]
+ assert expected_data == actual_data, failure_msg
+
+ def check_not_equal(expected_fname, actual_fname, failure_msg):
+ """Check that a file does not match its expected contents
+
+ Args:
+ expected_fname: Filename containing expected contents
+ actual_fname: Filename containing actual contents
+ failure_msg: Message to print on failure
+ """
+ expected_data = read_file(expected_fname)
+ actual_data = read_file(actual_fname)
+ assert expected_data != actual_data, failure_msg
+
+ def run_fit_test(mkimage):
+ """Basic sanity check of FIT loading in U-Boot
+
+ TODO: Almost everything:
+ - hash algorithms - invalid hash/contents should be detected
+ - signature algorithms - invalid sig/contents should be detected
+ - compression
+ - checking that errors are detected like:
+ - image overwriting
+ - missing images
+ - invalid configurations
+ - incorrect os/arch/type fields
+ - empty data
+ - images too large/small
+ - invalid FDT (e.g. putting a random binary in instead)
+ - default configuration selection
+ - bootm command line parameters should have desired effect
+ - run code coverage to make sure we are testing all the code
+ """
+ # Set up invariant files
+ control_dtb = make_dtb()
+ kernel = make_kernel('test-kernel.bin', 'kernel')
+ ramdisk = make_ramdisk('test-ramdisk.bin', 'ramdisk')
+ loadables1 = make_kernel('test-loadables1.bin', 'lenrek')
+ loadables2 = make_ramdisk('test-loadables2.bin', 'ksidmar')
+ kernel_out = make_fname('kernel-out.bin')
+ fdt = make_fname('u-boot.dtb')
+ fdt_out = make_fname('fdt-out.dtb')
+ ramdisk_out = make_fname('ramdisk-out.bin')
+ loadables1_out = make_fname('loadables1-out.bin')
+ loadables2_out = make_fname('loadables2-out.bin')
+
+ # Set up basic parameters with default values
+ params = {
+ 'fit_addr' : 0x1000,
+
+ 'kernel' : kernel,
+ 'kernel_out' : kernel_out,
+ 'kernel_addr' : 0x40000,
+ 'kernel_size' : filesize(kernel),
+
+ 'fdt' : fdt,
+ 'fdt_out' : fdt_out,
+ 'fdt_addr' : 0x80000,
+ 'fdt_size' : filesize(control_dtb),
+ 'fdt_load' : '',
+
+ 'ramdisk' : ramdisk,
+ 'ramdisk_out' : ramdisk_out,
+ 'ramdisk_addr' : 0xc0000,
+ 'ramdisk_size' : filesize(ramdisk),
+ 'ramdisk_load' : '',
+ 'ramdisk_config' : '',
+
+ 'loadables1' : loadables1,
+ 'loadables1_out' : loadables1_out,
+ 'loadables1_addr' : 0x100000,
+ 'loadables1_size' : filesize(loadables1),
+ 'loadables1_load' : '',
+
+ 'loadables2' : loadables2,
+ 'loadables2_out' : loadables2_out,
+ 'loadables2_addr' : 0x140000,
+ 'loadables2_size' : filesize(loadables2),
+ 'loadables2_load' : '',
+
+ 'loadables_config' : '',
+ 'compression' : 'none',
+ }
+
+ # Make a basic FIT and a script to load it
+ fit = make_fit(mkimage, params)
+ params['fit'] = fit
+ cmd = base_script % params
+
+ # First check that we can load a kernel
+ # We could perhaps reduce duplication with some loss of readability
+ cons.config.dtb = control_dtb
+ cons.restart_uboot()
+ with cons.log.section('Kernel load'):
+ output = cons.run_command_list(cmd.splitlines())
+ check_equal(kernel, kernel_out, 'Kernel not loaded')
+ check_not_equal(control_dtb, fdt_out,
+ 'FDT loaded but should be ignored')
+ check_not_equal(ramdisk, ramdisk_out,
+ 'Ramdisk loaded but should not be')
+
+ # Find out the offset in the FIT where U-Boot has found the FDT
+ line = find_matching(output, 'Booting using the fdt blob at ')
+ fit_offset = int(line, 16) - params['fit_addr']
+ fdt_magic = struct.pack('>L', 0xd00dfeed)
+ data = read_file(fit)
+
+ # Now find where it actually is in the FIT (skip the first word)
+ real_fit_offset = data.find(fdt_magic, 4)
+ assert fit_offset == real_fit_offset, (
+ 'U-Boot loaded FDT from offset %#x, FDT is actually at %#x' %
+ (fit_offset, real_fit_offset))
+
+ # Now a kernel and an FDT
+ with cons.log.section('Kernel + FDT load'):
+ params['fdt_load'] = 'load = <%#x>;' % params['fdt_addr']
+ fit = make_fit(mkimage, params)
+ cons.restart_uboot()
+ output = cons.run_command_list(cmd.splitlines())
+ check_equal(kernel, kernel_out, 'Kernel not loaded')
+ check_equal(control_dtb, fdt_out, 'FDT not loaded')
+ check_not_equal(ramdisk, ramdisk_out,
+ 'Ramdisk loaded but should not be')
+
+ # Try a ramdisk
+ with cons.log.section('Kernel + FDT + Ramdisk load'):
+ params['ramdisk_config'] = 'ramdisk = "ramdisk-1";'
+ params['ramdisk_load'] = 'load = <%#x>;' % params['ramdisk_addr']
+ fit = make_fit(mkimage, params)
+ cons.restart_uboot()
+ output = cons.run_command_list(cmd.splitlines())
+ check_equal(ramdisk, ramdisk_out, 'Ramdisk not loaded')
+
+ # Configuration with some Loadables
+ with cons.log.section('Kernel + FDT + Ramdisk load + Loadables'):
+ params['loadables_config'] = 'loadables = "kernel-2", "ramdisk-2";'
+ params['loadables1_load'] = ('load = <%#x>;' %
+ params['loadables1_addr'])
+ params['loadables2_load'] = ('load = <%#x>;' %
+ params['loadables2_addr'])
+ fit = make_fit(mkimage, params)
+ cons.restart_uboot()
+ output = cons.run_command_list(cmd.splitlines())
+ check_equal(loadables1, loadables1_out,
+ 'Loadables1 (kernel) not loaded')
+ check_equal(loadables2, loadables2_out,
+ 'Loadables2 (ramdisk) not loaded')
+
+ # Kernel, FDT and Ramdisk all compressed
+ with cons.log.section('(Kernel + FDT + Ramdisk) compressed'):
+ params['compression'] = 'gzip'
+ params['kernel'] = make_compressed(kernel)
+ params['fdt'] = make_compressed(fdt)
+ params['ramdisk'] = make_compressed(ramdisk)
+ fit = make_fit(mkimage, params)
+ cons.restart_uboot()
+ output = cons.run_command_list(cmd.splitlines())
+ check_equal(kernel, kernel_out, 'Kernel not loaded')
+ check_equal(control_dtb, fdt_out, 'FDT not loaded')
+ check_not_equal(ramdisk, ramdisk_out, 'Ramdisk got decompressed?')
+ check_equal(ramdisk + '.gz', ramdisk_out, 'Ramdist not loaded')
+
+
+ cons = u_boot_console
+ try:
+ # We need to use our own device tree file. Remember to restore it
+ # afterwards.
+ old_dtb = cons.config.dtb
+ mkimage = cons.config.build_dir + '/tools/mkimage'
+ run_fit_test(mkimage)
+ finally:
+ # Go back to the original U-Boot with the correct dtb.
+ cons.config.dtb = old_dtb
+ cons.restart_uboot()
diff --git a/roms/u-boot/test/py/tests/test_fit_ecdsa.py b/roms/u-boot/test/py/tests/test_fit_ecdsa.py
new file mode 100644
index 000000000..87b608122
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_fit_ecdsa.py
@@ -0,0 +1,111 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright (c) 2020,2021 Alexandru Gagniuc <mr.nuke.me@gmail.com>
+
+"""
+Test ECDSA signing of FIT images
+
+This test uses mkimage to sign an existing FIT image with an ECDSA key. The
+signature is then extracted, and verified against pyCryptodome.
+This test doesn't run the sandbox. It only checks the host tool 'mkimage'
+"""
+
+import pytest
+import u_boot_utils as util
+from Cryptodome.Hash import SHA256
+from Cryptodome.PublicKey import ECC
+from Cryptodome.Signature import DSS
+
+class SignableFitImage(object):
+ """ Helper to manipulate a FIT image on disk """
+ def __init__(self, cons, file_name):
+ self.fit = file_name
+ self.cons = cons
+ self.signable_nodes = set()
+
+ def __fdt_list(self, path):
+ return util.run_and_log(self.cons, f'fdtget -l {self.fit} {path}')
+
+ def __fdt_set(self, node, **prop_value):
+ for prop, value in prop_value.items():
+ util.run_and_log(self.cons, f'fdtput -ts {self.fit} {node} {prop} {value}')
+
+ def __fdt_get_binary(self, node, prop):
+ numbers = util.run_and_log(self.cons, f'fdtget -tbi {self.fit} {node} {prop}')
+
+ bignum = bytearray()
+ for little_num in numbers.split():
+ bignum.append(int(little_num))
+
+ return bignum
+
+ def find_signable_image_nodes(self):
+ for node in self.__fdt_list('/images').split():
+ image = f'/images/{node}'
+ if 'signature' in self.__fdt_list(image):
+ self.signable_nodes.add(image)
+
+ return self.signable_nodes
+
+ def change_signature_algo_to_ecdsa(self):
+ for image in self.signable_nodes:
+ self.__fdt_set(f'{image}/signature', algo='sha256,ecdsa256')
+
+ def sign(self, mkimage, key_file):
+ util.run_and_log(self.cons, [mkimage, '-F', self.fit, f'-G{key_file}'])
+
+ def check_signatures(self, key):
+ for image in self.signable_nodes:
+ raw_sig = self.__fdt_get_binary(f'{image}/signature', 'value')
+ raw_bin = self.__fdt_get_binary(image, 'data')
+
+ sha = SHA256.new(raw_bin)
+ verifier = DSS.new(key, 'fips-186-3')
+ verifier.verify(sha, bytes(raw_sig))
+
+
+@pytest.mark.buildconfigspec('fit_signature')
+@pytest.mark.requiredtool('dtc')
+@pytest.mark.requiredtool('fdtget')
+@pytest.mark.requiredtool('fdtput')
+def test_fit_ecdsa(u_boot_console):
+ """ Test that signatures generated by mkimage are legible. """
+ def generate_ecdsa_key():
+ return ECC.generate(curve='prime256v1')
+
+ def assemble_fit_image(dest_fit, its, destdir):
+ dtc_args = f'-I dts -O dtb -i {destdir}'
+ util.run_and_log(cons, [mkimage, '-D', dtc_args, '-f', its, dest_fit])
+
+ def dtc(dts):
+ dtb = dts.replace('.dts', '.dtb')
+ util.run_and_log(cons, f'dtc {datadir}/{dts} -O dtb -o {tempdir}/{dtb}')
+
+ cons = u_boot_console
+ mkimage = cons.config.build_dir + '/tools/mkimage'
+ datadir = cons.config.source_dir + '/test/py/tests/vboot/'
+ tempdir = cons.config.result_dir
+ key_file = f'{tempdir}/ecdsa-test-key.pem'
+ fit_file = f'{tempdir}/test.fit'
+ dtc('sandbox-kernel.dts')
+
+ key = generate_ecdsa_key()
+
+ # Create a fake kernel image -- zeroes will do just fine
+ with open(f'{tempdir}/test-kernel.bin', 'w') as fd:
+ fd.write(500 * chr(0))
+
+ # invocations of mkimage expect to read the key from disk
+ with open(key_file, 'w') as f:
+ f.write(key.export_key(format='PEM'))
+
+ assemble_fit_image(fit_file, f'{datadir}/sign-images-sha256.its', tempdir)
+
+ fit = SignableFitImage(cons, fit_file)
+ nodes = fit.find_signable_image_nodes()
+ if len(nodes) == 0:
+ raise ValueError('FIT image has no "/image" nodes with "signature"')
+
+ fit.change_signature_algo_to_ecdsa()
+ fit.sign(mkimage, key_file)
+ fit.check_signatures(key)
diff --git a/roms/u-boot/test/py/tests/test_fpga.py b/roms/u-boot/test/py/tests/test_fpga.py
new file mode 100644
index 000000000..ca7ef8ea4
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_fpga.py
@@ -0,0 +1,565 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (c) 2018, Xilinx Inc.
+#
+# Michal Simek
+# Siva Durga Prasad Paladugu
+
+import pytest
+import re
+import random
+import u_boot_utils
+
+"""
+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.
+
+For example:
+
+# True if a DHCP server is attached to the network, and should be tested.
+env__net_dhcp_server = True
+
+# A list of environment variables that should be set in order to configure a
+# static IP. In this test case we atleast need serverip for performing tftpb
+# to get required files.
+env__net_static_env_vars = [
+ ('ipaddr', '10.0.0.100'),
+ ('netmask', '255.255.255.0'),
+ ('serverip', '10.0.0.1'),
+]
+
+# Details regarding the files that may be read from a TFTP server. .
+env__fpga_secure_readable_file = {
+ 'fn': 'auth_bhdr_ppk1_bit.bin',
+ 'enckupfn': 'auth_bhdr_enc_kup_load_bit.bin',
+ 'addr': 0x1000000,
+ 'keyaddr': 0x100000,
+ 'keyfn': 'key.txt',
+}
+
+env__fpga_under_test = {
+ 'dev': 0,
+ 'addr' : 0x1000000,
+ 'bitstream_load': 'compress.bin',
+ 'bitstream_load_size': 1831960,
+ 'bitstream_loadp': 'compress_pr.bin',
+ 'bitstream_loadp_size': 423352,
+ 'bitstream_loadb': 'compress.bit',
+ 'bitstream_loadb_size': 1832086,
+ 'bitstream_loadbp': 'compress_pr.bit',
+ 'bitstream_loadbp_size': 423491,
+ 'mkimage_legacy': 'download.ub',
+ 'mkimage_legacy_size': 13321468,
+ 'mkimage_legacy_gz': 'download.gz.ub',
+ 'mkimage_legacy_gz_size': 53632,
+ 'mkimage_fit': 'download-fit.ub',
+ 'mkimage_fit_size': 13322784,
+ 'loadfs': 'mmc 0 compress.bin',
+ 'loadfs_size': 1831960,
+ 'loadfs_block_size': 0x10000,
+}
+"""
+
+import test_net
+
+def check_dev(u_boot_console):
+ f = u_boot_console.config.env.get('env__fpga_under_test', None)
+ if not f:
+ pytest.skip('No FPGA to test')
+
+ dev = f.get('dev', -1)
+ if dev < 0:
+ pytest.fail('No dev specified via env__fpga_under_test')
+
+ return dev, f
+
+def load_file_from_var(u_boot_console, name):
+ dev, f = check_dev(u_boot_console)
+
+ addr = f.get('addr', -1)
+ if addr < 0:
+ pytest.fail('No address specified via env__fpga_under_test')
+
+ test_net.test_net_dhcp(u_boot_console)
+ test_net.test_net_setup_static(u_boot_console)
+ bit = f['%s' % (name)]
+ bit_size = f['%s_size' % (name)]
+
+ expected_tftp = 'Bytes transferred = %d' % bit_size
+ output = u_boot_console.run_command('tftpboot %x %s' % (addr, bit))
+ assert expected_tftp in output
+
+ return f, dev, addr, bit, bit_size
+
+###### FPGA FAIL test ######
+expected_usage = 'fpga - loadable FPGA image support'
+
+@pytest.mark.xfail
+@pytest.mark.buildconfigspec('cmd_fpga')
+def test_fpga_fail(u_boot_console):
+ # Test non valid fpga subcommand
+ expected = 'fpga: non existing command'
+ output = u_boot_console.run_command('fpga broken 0')
+ #assert expected in output
+ assert expected_usage in output
+
+@pytest.mark.buildconfigspec('cmd_fpga')
+def test_fpga_help(u_boot_console):
+ # Just show help
+ output = u_boot_console.run_command('fpga')
+ assert expected_usage in output
+
+
+###### FPGA DUMP tests ######
+
+@pytest.mark.buildconfigspec('cmd_fpga')
+def test_fpga_dump(u_boot_console):
+ pytest.skip('Not implemented now')
+
+@pytest.mark.buildconfigspec('cmd_fpga')
+def test_fpga_dump_variable(u_boot_console):
+ # Same as above but via "fpga" variable
+ pytest.skip('Not implemented now')
+
+###### FPGA INFO tests ######
+
+@pytest.mark.buildconfigspec('cmd_fpga')
+def test_fpga_info_fail(u_boot_console):
+ # Maybe this can be skipped completely
+ dev, f = check_dev(u_boot_console)
+
+ # Multiple parameters to fpga info should fail
+ expected = 'fpga: more parameters passed'
+ output = u_boot_console.run_command('fpga info 0 0')
+ #assert expected in output
+ assert expected_usage in output
+
+@pytest.mark.buildconfigspec('cmd_fpga')
+def test_fpga_info_list(u_boot_console):
+ # Maybe this can be skipped completely
+ dev, f = check_dev(u_boot_console)
+
+ # Code is design in a way that if fpga dev is not passed it should
+ # return list of all fpga devices in the system
+ u_boot_console.run_command('setenv fpga')
+ output = u_boot_console.run_command('fpga info')
+ assert expected_usage not in output
+
+@pytest.mark.buildconfigspec('cmd_fpga')
+def test_fpga_info(u_boot_console):
+ dev, f = check_dev(u_boot_console)
+
+ output = u_boot_console.run_command('fpga info %x' % (dev))
+ assert expected_usage not in output
+
+@pytest.mark.buildconfigspec('cmd_fpga')
+def test_fpga_info_variable(u_boot_console):
+ dev, f = check_dev(u_boot_console)
+
+ #
+ # fpga variable is storing device number which doesn't need to be passed
+ #
+ u_boot_console.run_command('setenv fpga %x' % (dev))
+
+ output = u_boot_console.run_command('fpga info')
+ # Variable cleanup
+ u_boot_console.run_command('setenv fpga')
+ assert expected_usage not in output
+
+###### FPGA LOAD tests ######
+
+@pytest.mark.buildconfigspec('cmd_fpga')
+@pytest.mark.buildconfigspec('cmd_echo')
+def test_fpga_load_fail(u_boot_console):
+ f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'bitstream_load')
+
+ for cmd in ['dump', 'load', 'loadb']:
+ # missing dev parameter
+ expected = 'fpga: incorrect parameters passed'
+ output = u_boot_console.run_command('fpga %s %x $filesize' % (cmd, addr))
+ #assert expected in output
+ assert expected_usage in output
+
+ # more parameters - 0 at the end
+ expected = 'fpga: more parameters passed'
+ output = u_boot_console.run_command('fpga %s %x %x $filesize 0' % (cmd, dev, addr))
+ #assert expected in output
+ assert expected_usage in output
+
+ # 0 address
+ expected = 'fpga: zero fpga_data address'
+ output = u_boot_console.run_command('fpga %s %x 0 $filesize' % (cmd, dev))
+ #assert expected in output
+ assert expected_usage in output
+
+ # 0 filesize
+ expected = 'fpga: zero size'
+ output = u_boot_console.run_command('fpga %s %x %x 0' % (cmd, dev, addr))
+ #assert expected in output
+ assert expected_usage in output
+
+@pytest.mark.buildconfigspec('cmd_fpga')
+@pytest.mark.buildconfigspec('cmd_echo')
+def test_fpga_load(u_boot_console):
+ f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'bitstream_load')
+
+ expected_text = 'FPGA loaded successfully'
+ output = u_boot_console.run_command('fpga load %x %x $filesize && echo %s' % (dev, addr, expected_text))
+ assert expected_text in output
+
+@pytest.mark.buildconfigspec('cmd_fpga')
+@pytest.mark.buildconfigspec('cmd_fpga_loadp')
+@pytest.mark.buildconfigspec('cmd_echo')
+def test_fpga_loadp(u_boot_console):
+ f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'bitstream_load')
+
+ expected_text = 'FPGA loaded successfully'
+ output = u_boot_console.run_command('fpga load %x %x $filesize && echo %s' % (dev, addr, expected_text))
+ assert expected_text in output
+
+ # And load also partial bistream
+ f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'bitstream_loadp')
+
+ expected_text = 'FPGA loaded successfully'
+ output = u_boot_console.run_command('fpga loadp %x %x $filesize && echo %s' % (dev, addr, expected_text))
+ assert expected_text in output
+
+@pytest.mark.buildconfigspec('cmd_fpga')
+@pytest.mark.buildconfigspec('cmd_echo')
+def test_fpga_loadb(u_boot_console):
+ f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'bitstream_loadb')
+
+ expected_text = 'FPGA loaded successfully'
+ output = u_boot_console.run_command('fpga loadb %x %x $filesize && echo %s' % (dev, addr, expected_text))
+ assert expected_text in output
+
+@pytest.mark.buildconfigspec('cmd_fpga')
+@pytest.mark.buildconfigspec('cmd_fpga_loadbp')
+@pytest.mark.buildconfigspec('cmd_echo')
+def test_fpga_loadbp(u_boot_console):
+ f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'bitstream_loadb')
+
+ expected_text = 'FPGA loaded successfully'
+ output = u_boot_console.run_command('fpga loadb %x %x $filesize && echo %s' % (dev, addr, expected_text))
+ assert expected_text in output
+
+ # And load also partial bistream in bit format
+ f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'bitstream_loadbp')
+
+ expected_text = 'FPGA loaded successfully'
+ output = u_boot_console.run_command('fpga loadbp %x %x $filesize && echo %s' % (dev, addr, expected_text))
+ assert expected_text in output
+
+###### FPGA LOADMK tests ######
+
+@pytest.mark.buildconfigspec('cmd_fpga')
+@pytest.mark.buildconfigspec('cmd_fpga_loadmk')
+@pytest.mark.buildconfigspec('cmd_echo')
+@pytest.mark.buildconfigspec('image_format_legacy')
+def test_fpga_loadmk_fail(u_boot_console):
+ f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'mkimage_legacy')
+
+ u_boot_console.run_command('imi %x' % (addr))
+
+ # load image but pass incorrect address to show error message
+ expected = 'Unknown image type'
+ output = u_boot_console.run_command('fpga loadmk %x %x' % (dev, addr + 0x10))
+ assert expected in output
+
+ # Pass more parameters then command expects - 0 at the end
+ output = u_boot_console.run_command('fpga loadmk %x %x 0' % (dev, addr))
+ #assert expected in output
+ assert expected_usage in output
+
+@pytest.mark.buildconfigspec('cmd_fpga')
+@pytest.mark.buildconfigspec('cmd_fpga_loadmk')
+@pytest.mark.buildconfigspec('cmd_echo')
+@pytest.mark.buildconfigspec('image_format_legacy')
+def test_fpga_loadmk_legacy(u_boot_console):
+ f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'mkimage_legacy')
+
+ u_boot_console.run_command('imi %x' % (addr))
+
+ expected_text = 'FPGA loaded successfully'
+ output = u_boot_console.run_command('fpga loadmk %x %x && echo %s' % (dev, addr, expected_text))
+ assert expected_text in output
+
+@pytest.mark.xfail
+@pytest.mark.buildconfigspec('cmd_fpga')
+@pytest.mark.buildconfigspec('cmd_fpga_loadmk')
+@pytest.mark.buildconfigspec('cmd_echo')
+@pytest.mark.buildconfigspec('image_format_legacy')
+def test_fpga_loadmk_legacy_variable_fpga(u_boot_console):
+ f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'mkimage_legacy')
+
+ u_boot_console.run_command('imi %x' % (addr))
+
+ u_boot_console.run_command('setenv fpga %x' % (dev))
+
+ # this testcase should cover case which looks like it is supported but dev pointer is broken by loading mkimage address
+ expected_text = 'FPGA loaded successfully'
+ output = u_boot_console.run_command('fpga loadmk %x && echo %s' % (addr, expected_text))
+ u_boot_console.run_command('setenv fpga')
+ assert expected_text in output
+
+@pytest.mark.buildconfigspec('cmd_fpga')
+@pytest.mark.buildconfigspec('cmd_fpga_loadmk')
+@pytest.mark.buildconfigspec('cmd_echo')
+@pytest.mark.buildconfigspec('image_format_legacy')
+def test_fpga_loadmk_legacy_variable_fpgadata(u_boot_console):
+ f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'mkimage_legacy')
+
+ u_boot_console.run_command('imi %x' % (addr))
+
+ u_boot_console.run_command('setenv fpgadata %x' % (addr))
+
+ # this testcase should cover case which looks like it is supported but dev pointer is broken by loading mkimage address
+ expected_text = 'FPGA loaded successfully'
+ output = u_boot_console.run_command('fpga loadmk %x && echo %s' % (dev, expected_text))
+ u_boot_console.run_command('setenv fpgadata')
+ assert expected_text in output
+
+@pytest.mark.buildconfigspec('cmd_fpga')
+@pytest.mark.buildconfigspec('cmd_fpga_loadmk')
+@pytest.mark.buildconfigspec('cmd_echo')
+@pytest.mark.buildconfigspec('image_format_legacy')
+def test_fpga_loadmk_legacy_variable(u_boot_console):
+ f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'mkimage_legacy')
+
+ u_boot_console.run_command('imi %x' % (addr))
+
+ u_boot_console.run_command('setenv fpga %x' % (dev))
+ u_boot_console.run_command('setenv fpgadata %x' % (addr))
+
+ # this testcase should cover case which looks like it is supported but dev pointer is broken by loading mkimage address
+ expected_text = 'FPGA loaded successfully'
+ output = u_boot_console.run_command('fpga loadmk && echo %s' % (expected_text))
+ u_boot_console.run_command('setenv fpga')
+ u_boot_console.run_command('setenv fpgadata')
+ assert expected_text in output
+
+@pytest.mark.buildconfigspec('cmd_fpga')
+@pytest.mark.buildconfigspec('cmd_fpga_loadmk')
+@pytest.mark.buildconfigspec('cmd_echo')
+@pytest.mark.buildconfigspec('image_format_legacy')
+@pytest.mark.buildconfigspec('gzip')
+def test_fpga_loadmk_legacy_gz(u_boot_console):
+ f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'mkimage_legacy_gz')
+
+ u_boot_console.run_command('imi %x' % (addr))
+
+ expected_text = 'FPGA loaded successfully'
+ output = u_boot_console.run_command('fpga loadmk %x %x && echo %s' % (dev, addr, expected_text))
+ assert expected_text in output
+
+@pytest.mark.buildconfigspec('cmd_fpga')
+@pytest.mark.buildconfigspec('cmd_fpga_loadmk')
+@pytest.mark.buildconfigspec('fit')
+@pytest.mark.buildconfigspec('cmd_echo')
+def test_fpga_loadmk_fit_external(u_boot_console):
+ f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'mkimage_fit_external')
+
+ u_boot_console.run_command('imi %x' % (addr))
+
+ expected_text = 'FPGA loaded successfully'
+ output = u_boot_console.run_command('fpga loadmk %x %x:fpga && echo %s' % (dev, addr, expected_text))
+ assert expected_text in output
+
+@pytest.mark.buildconfigspec('cmd_fpga')
+@pytest.mark.buildconfigspec('cmd_fpga_loadmk')
+@pytest.mark.buildconfigspec('fit')
+@pytest.mark.buildconfigspec('cmd_echo')
+def test_fpga_loadmk_fit(u_boot_console):
+ f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'mkimage_fit')
+
+ u_boot_console.run_command('imi %x' % (addr))
+
+ expected_text = 'FPGA loaded successfully'
+ output = u_boot_console.run_command('fpga loadmk %x %x:fpga && echo %s' % (dev, addr, expected_text))
+ assert expected_text in output
+
+@pytest.mark.buildconfigspec('cmd_fpga')
+@pytest.mark.buildconfigspec('cmd_fpga_loadmk')
+@pytest.mark.buildconfigspec('fit')
+@pytest.mark.buildconfigspec('cmd_echo')
+def test_fpga_loadmk_fit_variable_fpga(u_boot_console):
+ f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'mkimage_fit')
+
+ u_boot_console.run_command('imi %x' % (addr))
+ # FIXME this should fail - broken support in past
+ u_boot_console.run_command('setenv fpga %x' % (dev))
+
+ expected_text = 'FPGA loaded successfully'
+ output = u_boot_console.run_command('fpga loadmk %x:fpga && echo %s' % (addr, expected_text))
+ u_boot_console.run_command('setenv fpga')
+ assert expected_text in output
+
+@pytest.mark.buildconfigspec('cmd_fpga')
+@pytest.mark.buildconfigspec('cmd_fpga_loadmk')
+@pytest.mark.buildconfigspec('fit')
+@pytest.mark.buildconfigspec('cmd_echo')
+def test_fpga_loadmk_fit_variable_fpgadata(u_boot_console):
+ f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'mkimage_fit')
+
+ u_boot_console.run_command('imi %x' % (addr))
+ # FIXME this should fail - broken support in past
+ u_boot_console.run_command('setenv fpgadata %x:fpga' % (addr))
+
+ expected_text = 'FPGA loaded successfully'
+ output = u_boot_console.run_command('fpga loadmk %x && echo %s' % (dev, expected_text))
+ u_boot_console.run_command('setenv fpgadata')
+ assert expected_text in output
+
+@pytest.mark.buildconfigspec('cmd_fpga')
+@pytest.mark.buildconfigspec('cmd_fpga_loadmk')
+@pytest.mark.buildconfigspec('fit')
+@pytest.mark.buildconfigspec('cmd_echo')
+def test_fpga_loadmk_fit_variable(u_boot_console):
+ f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'mkimage_fit')
+
+ u_boot_console.run_command('imi %x' % (addr))
+
+ u_boot_console.run_command('setenv fpga %x' % (dev))
+ u_boot_console.run_command('setenv fpgadata %x:fpga' % (addr))
+
+ expected_text = 'FPGA loaded successfully'
+ output = u_boot_console.run_command('fpga loadmk && echo %s' % (expected_text))
+ u_boot_console.run_command('setenv fpga')
+ u_boot_console.run_command('setenv fpgadata')
+ assert expected_text in output
+
+###### FPGA LOAD tests ######
+
+@pytest.mark.buildconfigspec('cmd_fpga')
+def test_fpga_loadfs_fail(u_boot_console):
+ dev, f = check_dev(u_boot_console)
+
+ addr = f.get('addr', -1)
+ if addr < 0:
+ pytest.fail('No address specified via env__fpga_under_test')
+
+ bit = f['loadfs']
+ bit_size = f['loadfs_size']
+ block_size = f['loadfs_block_size']
+
+ # less params - dev number removed
+ expected = 'fpga: incorrect parameters passed'
+ output = u_boot_console.run_command('fpga loadfs %x %x %x %s' % (addr, bit_size, block_size, bit))
+ #assert expected in output
+ assert expected_usage in output
+
+ # one more param - 0 at the end
+ # This is the longest command that's why there is no message from cmd/fpga.c
+ output = u_boot_console.run_command('fpga loadfs %x %x %x %x %s 0' % (dev, addr, bit_size, block_size, bit))
+ assert expected_usage in output
+
+ # zero address 0
+ expected = 'fpga: zero fpga_data address'
+ output = u_boot_console.run_command('fpga loadfs %x %x %x %x %s' % (dev, 0, bit_size, block_size, bit))
+ #assert expected in output
+ assert expected_usage in output
+
+ # bit_size 0
+ expected = 'fpga: zero size'
+ output = u_boot_console.run_command('fpga loadfs %x %x %x %x %s' % (dev, addr, 0, block_size, bit))
+ #assert expected in output
+ assert expected_usage in output
+
+ # block size 0
+ # FIXME this should pass but it failing too
+ output = u_boot_console.run_command('fpga loadfs %x %x %x %x %s' % (dev, addr, bit_size, 0, bit))
+ assert expected_usage in output
+
+ # non existing bitstream name
+ expected = 'Unable to read file noname'
+ output = u_boot_console.run_command('fpga loadfs %x %x %x %x mmc 0 noname' % (dev, addr, bit_size, block_size))
+ assert expected in output
+ assert expected_usage in output
+
+ # -1 dev number
+ expected = 'fpga_fsload: Invalid device number -1'
+ output = u_boot_console.run_command('fpga loadfs %d %x %x %x mmc 0 noname' % (-1, addr, bit_size, block_size))
+ assert expected in output
+ assert expected_usage in output
+
+
+@pytest.mark.buildconfigspec('cmd_fpga')
+@pytest.mark.buildconfigspec('cmd_echo')
+def test_fpga_loadfs(u_boot_console):
+ dev, f = check_dev(u_boot_console)
+
+ addr = f.get('addr', -1)
+ if addr < 0:
+ pytest.fail('No address specified via env__fpga_under_test')
+
+ bit = f['loadfs']
+ bit_size = f['loadfs_size']
+ block_size = f['loadfs_block_size']
+
+ # This should be done better
+ expected_text = 'FPGA loaded successfully'
+ output = u_boot_console.run_command('fpga loadfs %x %x %x %x %s && echo %s' % (dev, addr, bit_size, block_size, bit, expected_text))
+ assert expected_text in output
+
+@pytest.mark.buildconfigspec('cmd_fpga')
+@pytest.mark.buildconfigspec('cmd_fpga_load_secure')
+@pytest.mark.buildconfigspec('cmd_net')
+@pytest.mark.buildconfigspec('cmd_dhcp')
+@pytest.mark.buildconfigspec('net')
+def test_fpga_secure_bit_auth(u_boot_console):
+
+ test_net.test_net_dhcp(u_boot_console)
+ test_net.test_net_setup_static(u_boot_console)
+
+ f = u_boot_console.config.env.get('env__fpga_secure_readable_file', None)
+ if not f:
+ pytest.skip('No TFTP readable file to read')
+
+ 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
+
+ expected_zynqmpsecure = 'Bitstream successfully loaded'
+ output = u_boot_console.run_command('fpga loads 0 %x $filesize 0 2' % (addr))
+ assert expected_zynqmpsecure in output
+
+
+@pytest.mark.buildconfigspec('cmd_fpga')
+@pytest.mark.buildconfigspec('cmd_fpga_load_secure')
+@pytest.mark.buildconfigspec('cmd_net')
+@pytest.mark.buildconfigspec('cmd_dhcp')
+@pytest.mark.buildconfigspec('net')
+def test_fpga_secure_bit_img_auth_kup(u_boot_console):
+
+ test_net.test_net_dhcp(u_boot_console)
+ test_net.test_net_setup_static(u_boot_console)
+
+ f = u_boot_console.config.env.get('env__fpga_secure_readable_file', None)
+ if not f:
+ pytest.skip('No TFTP readable file to read')
+
+ 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
+
+ expected_zynqmpsecure = 'Bitstream successfully loaded'
+ output = u_boot_console.run_command('fpga loads 0 %x $filesize 0 1 %x' % (addr, keyaddr))
+ assert expected_zynqmpsecure in output
diff --git a/roms/u-boot/test/py/tests/test_fs/conftest.py b/roms/u-boot/test/py/tests/test_fs/conftest.py
new file mode 100644
index 000000000..7325486cd
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_fs/conftest.py
@@ -0,0 +1,662 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2018, Linaro Limited
+# Author: Takahiro Akashi <takahiro.akashi@linaro.org>
+
+import os
+import os.path
+import pytest
+import re
+from subprocess import call, check_call, check_output, CalledProcessError
+from fstest_defs import *
+
+supported_fs_basic = ['fat16', 'fat32', 'ext4']
+supported_fs_ext = ['fat16', 'fat32']
+supported_fs_mkdir = ['fat16', 'fat32']
+supported_fs_unlink = ['fat16', 'fat32']
+supported_fs_symlink = ['ext4']
+
+#
+# Filesystem test specific setup
+#
+def pytest_addoption(parser):
+ """Enable --fs-type option.
+
+ See pytest_configure() about how it works.
+
+ Args:
+ parser: Pytest command-line parser.
+
+ Returns:
+ Nothing.
+ """
+ parser.addoption('--fs-type', action='append', default=None,
+ help='Targeting Filesystem Types')
+
+def pytest_configure(config):
+ """Restrict a file system(s) to be tested.
+
+ A file system explicitly named with --fs-type option is selected
+ if it belongs to a default supported_fs_xxx list.
+ Multiple options can be specified.
+
+ Args:
+ config: Pytest configuration.
+
+ Returns:
+ Nothing.
+ """
+ global supported_fs_basic
+ global supported_fs_ext
+ global supported_fs_mkdir
+ global supported_fs_unlink
+ global supported_fs_symlink
+
+ def intersect(listA, listB):
+ return [x for x in listA if x in listB]
+
+ supported_fs = config.getoption('fs_type')
+ if supported_fs:
+ print('*** FS TYPE modified: %s' % supported_fs)
+ supported_fs_basic = intersect(supported_fs, supported_fs_basic)
+ supported_fs_ext = intersect(supported_fs, supported_fs_ext)
+ supported_fs_mkdir = intersect(supported_fs, supported_fs_mkdir)
+ supported_fs_unlink = intersect(supported_fs, supported_fs_unlink)
+ supported_fs_symlink = intersect(supported_fs, supported_fs_symlink)
+
+def pytest_generate_tests(metafunc):
+ """Parametrize fixtures, fs_obj_xxx
+
+ Each fixture will be parametrized with a corresponding support_fs_xxx
+ list.
+
+ Args:
+ metafunc: Pytest test function.
+
+ Returns:
+ Nothing.
+ """
+ if 'fs_obj_basic' in metafunc.fixturenames:
+ metafunc.parametrize('fs_obj_basic', supported_fs_basic,
+ indirect=True, scope='module')
+ if 'fs_obj_ext' in metafunc.fixturenames:
+ metafunc.parametrize('fs_obj_ext', supported_fs_ext,
+ indirect=True, scope='module')
+ if 'fs_obj_mkdir' in metafunc.fixturenames:
+ metafunc.parametrize('fs_obj_mkdir', supported_fs_mkdir,
+ indirect=True, scope='module')
+ if 'fs_obj_unlink' in metafunc.fixturenames:
+ metafunc.parametrize('fs_obj_unlink', supported_fs_unlink,
+ indirect=True, scope='module')
+ if 'fs_obj_symlink' in metafunc.fixturenames:
+ metafunc.parametrize('fs_obj_symlink', supported_fs_symlink,
+ indirect=True, scope='module')
+
+#
+# Helper functions
+#
+def fstype_to_ubname(fs_type):
+ """Convert a file system type to an U-boot specific string
+
+ A generated string can be used as part of file system related commands
+ or a config name in u-boot. Currently fat16 and fat32 are handled
+ specifically.
+
+ Args:
+ fs_type: File system type.
+
+ Return:
+ A corresponding string for file system type.
+ """
+ if re.match('fat', fs_type):
+ return 'fat'
+ else:
+ return fs_type
+
+def check_ubconfig(config, fs_type):
+ """Check whether a file system is enabled in u-boot configuration.
+
+ This function is assumed to be called in a fixture function so that
+ the whole test cases will be skipped if a given file system is not
+ enabled.
+
+ Args:
+ fs_type: File system type.
+
+ Return:
+ Nothing.
+ """
+ if not config.buildconfig.get('config_cmd_%s' % fs_type, None):
+ pytest.skip('.config feature "CMD_%s" not enabled' % fs_type.upper())
+ if not config.buildconfig.get('config_%s_write' % fs_type, None):
+ pytest.skip('.config feature "%s_WRITE" not enabled'
+ % fs_type.upper())
+
+def mk_fs(config, fs_type, size, id):
+ """Create a file system volume.
+
+ Args:
+ fs_type: File system type.
+ size: Size of file system in MiB.
+ id: Prefix string of volume's file name.
+
+ Return:
+ Nothing.
+ """
+ fs_img = '%s.%s.img' % (id, fs_type)
+ fs_img = config.persistent_data_dir + '/' + fs_img
+
+ if fs_type == 'fat16':
+ mkfs_opt = '-F 16'
+ elif fs_type == 'fat32':
+ mkfs_opt = '-F 32'
+ else:
+ mkfs_opt = ''
+
+ if re.match('fat', fs_type):
+ fs_lnxtype = 'vfat'
+ else:
+ fs_lnxtype = fs_type
+
+ count = (size + 1048576 - 1) / 1048576
+
+ # Some distributions do not add /sbin to the default PATH, where mkfs lives
+ if '/sbin' not in os.environ["PATH"].split(os.pathsep):
+ os.environ["PATH"] += os.pathsep + '/sbin'
+
+ try:
+ check_call('rm -f %s' % fs_img, shell=True)
+ check_call('dd if=/dev/zero of=%s bs=1M count=%d'
+ % (fs_img, count), shell=True)
+ check_call('mkfs.%s %s %s'
+ % (fs_lnxtype, mkfs_opt, fs_img), shell=True)
+ if fs_type == 'ext4':
+ sb_content = check_output('tune2fs -l %s' % fs_img, shell=True).decode()
+ if 'metadata_csum' in sb_content:
+ check_call('tune2fs -O ^metadata_csum %s' % fs_img, shell=True)
+ return fs_img
+ except CalledProcessError:
+ call('rm -f %s' % fs_img, shell=True)
+ raise
+
+# from test/py/conftest.py
+def tool_is_in_path(tool):
+ """Check whether a given command is available on host.
+
+ Args:
+ tool: Command name.
+
+ Return:
+ True if available, False if not.
+ """
+ for path in os.environ['PATH'].split(os.pathsep):
+ fn = os.path.join(path, tool)
+ if os.path.isfile(fn) and os.access(fn, os.X_OK):
+ return True
+ return False
+
+fuse_mounted = False
+
+def mount_fs(fs_type, device, mount_point):
+ """Mount a volume.
+
+ Args:
+ fs_type: File system type.
+ device: Volume's file name.
+ mount_point: Mount point.
+
+ Return:
+ Nothing.
+ """
+ global fuse_mounted
+
+ fuse_mounted = False
+ try:
+ if tool_is_in_path('guestmount'):
+ fuse_mounted = True
+ check_call('guestmount -a %s -m /dev/sda %s'
+ % (device, mount_point), shell=True)
+ else:
+ mount_opt = 'loop,rw'
+ if re.match('fat', fs_type):
+ mount_opt += ',umask=0000'
+
+ check_call('sudo mount -o %s %s %s'
+ % (mount_opt, device, mount_point), shell=True)
+
+ # may not be effective for some file systems
+ check_call('sudo chmod a+rw %s' % mount_point, shell=True)
+ except CalledProcessError:
+ raise
+
+def umount_fs(mount_point):
+ """Unmount a volume.
+
+ Args:
+ mount_point: Mount point.
+
+ Return:
+ Nothing.
+ """
+ if fuse_mounted:
+ call('sync')
+ call('guestunmount %s' % mount_point, shell=True)
+ else:
+ call('sudo umount %s' % mount_point, shell=True)
+
+#
+# Fixture for basic fs test
+# derived from test/fs/fs-test.sh
+#
+@pytest.fixture()
+def fs_obj_basic(request, u_boot_config):
+ """Set up a file system to be used in basic fs test.
+
+ Args:
+ request: Pytest request object.
+ u_boot_config: U-boot configuration.
+
+ Return:
+ A fixture for basic fs test, i.e. a triplet of file system type,
+ volume file name and a list of MD5 hashes.
+ """
+ fs_type = request.param
+ fs_img = ''
+
+ fs_ubtype = fstype_to_ubname(fs_type)
+ check_ubconfig(u_boot_config, fs_ubtype)
+
+ mount_dir = u_boot_config.persistent_data_dir + '/mnt'
+
+ small_file = mount_dir + '/' + SMALL_FILE
+ big_file = mount_dir + '/' + BIG_FILE
+
+ try:
+
+ # 3GiB volume
+ fs_img = mk_fs(u_boot_config, fs_type, 0xc0000000, '3GB')
+ except CalledProcessError as err:
+ pytest.skip('Creating failed for filesystem: ' + fs_type + '. {}'.format(err))
+ return
+
+ try:
+ check_call('mkdir -p %s' % mount_dir, shell=True)
+ except CalledProcessError as err:
+ pytest.skip('Preparing mount folder failed for filesystem: ' + fs_type + '. {}'.format(err))
+ call('rm -f %s' % fs_img, shell=True)
+ return
+
+ try:
+ # Mount the image so we can populate it.
+ mount_fs(fs_type, fs_img, mount_dir)
+ except CalledProcessError as err:
+ pytest.skip('Mounting to folder failed for filesystem: ' + fs_type + '. {}'.format(err))
+ call('rmdir %s' % mount_dir, shell=True)
+ call('rm -f %s' % fs_img, shell=True)
+ return
+
+ try:
+ # Create a subdirectory.
+ check_call('mkdir %s/SUBDIR' % mount_dir, shell=True)
+
+ # Create big file in this image.
+ # Note that we work only on the start 1MB, couple MBs in the 2GB range
+ # and the last 1 MB of the huge 2.5GB file.
+ # So, just put random values only in those areas.
+ check_call('dd if=/dev/urandom of=%s bs=1M count=1'
+ % big_file, shell=True)
+ check_call('dd if=/dev/urandom of=%s bs=1M count=2 seek=2047'
+ % big_file, shell=True)
+ check_call('dd if=/dev/urandom of=%s bs=1M count=1 seek=2499'
+ % big_file, shell=True)
+
+ # Create a small file in this image.
+ check_call('dd if=/dev/urandom of=%s bs=1M count=1'
+ % small_file, shell=True)
+
+ # Delete the small file copies which possibly are written as part of a
+ # previous test.
+ # check_call('rm -f "%s.w"' % MB1, shell=True)
+ # check_call('rm -f "%s.w2"' % MB1, shell=True)
+
+ # Generate the md5sums of reads that we will test against small file
+ out = check_output(
+ 'dd if=%s bs=1M skip=0 count=1 2> /dev/null | md5sum'
+ % small_file, shell=True).decode()
+ md5val = [ out.split()[0] ]
+
+ # Generate the md5sums of reads that we will test against big file
+ # One from beginning of file.
+ out = check_output(
+ 'dd if=%s bs=1M skip=0 count=1 2> /dev/null | md5sum'
+ % big_file, shell=True).decode()
+ md5val.append(out.split()[0])
+
+ # One from end of file.
+ out = check_output(
+ 'dd if=%s bs=1M skip=2499 count=1 2> /dev/null | md5sum'
+ % big_file, shell=True).decode()
+ md5val.append(out.split()[0])
+
+ # One from the last 1MB chunk of 2GB
+ out = check_output(
+ 'dd if=%s bs=1M skip=2047 count=1 2> /dev/null | md5sum'
+ % big_file, shell=True).decode()
+ md5val.append(out.split()[0])
+
+ # One from the start 1MB chunk from 2GB
+ out = check_output(
+ 'dd if=%s bs=1M skip=2048 count=1 2> /dev/null | md5sum'
+ % big_file, shell=True).decode()
+ md5val.append(out.split()[0])
+
+ # One 1MB chunk crossing the 2GB boundary
+ out = check_output(
+ 'dd if=%s bs=512K skip=4095 count=2 2> /dev/null | md5sum'
+ % big_file, shell=True).decode()
+ md5val.append(out.split()[0])
+
+ except CalledProcessError as err:
+ pytest.skip('Setup failed for filesystem: ' + fs_type + '. {}'.format(err))
+ umount_fs(mount_dir)
+ return
+ else:
+ umount_fs(mount_dir)
+ yield [fs_ubtype, fs_img, md5val]
+ finally:
+ call('rmdir %s' % mount_dir, shell=True)
+ call('rm -f %s' % fs_img, shell=True)
+
+#
+# Fixture for extended fs test
+#
+@pytest.fixture()
+def fs_obj_ext(request, u_boot_config):
+ """Set up a file system to be used in extended fs test.
+
+ Args:
+ request: Pytest request object.
+ u_boot_config: U-boot configuration.
+
+ Return:
+ A fixture for extended fs test, i.e. a triplet of file system type,
+ volume file name and a list of MD5 hashes.
+ """
+ fs_type = request.param
+ fs_img = ''
+
+ fs_ubtype = fstype_to_ubname(fs_type)
+ check_ubconfig(u_boot_config, fs_ubtype)
+
+ mount_dir = u_boot_config.persistent_data_dir + '/mnt'
+
+ min_file = mount_dir + '/' + MIN_FILE
+ tmp_file = mount_dir + '/tmpfile'
+
+ try:
+
+ # 128MiB volume
+ fs_img = mk_fs(u_boot_config, fs_type, 0x8000000, '128MB')
+ except CalledProcessError as err:
+ pytest.skip('Creating failed for filesystem: ' + fs_type + '. {}'.format(err))
+ return
+
+ try:
+ check_call('mkdir -p %s' % mount_dir, shell=True)
+ except CalledProcessError as err:
+ pytest.skip('Preparing mount folder failed for filesystem: ' + fs_type + '. {}'.format(err))
+ call('rm -f %s' % fs_img, shell=True)
+ return
+
+ try:
+ # Mount the image so we can populate it.
+ mount_fs(fs_type, fs_img, mount_dir)
+ except CalledProcessError as err:
+ pytest.skip('Mounting to folder failed for filesystem: ' + fs_type + '. {}'.format(err))
+ call('rmdir %s' % mount_dir, shell=True)
+ call('rm -f %s' % fs_img, shell=True)
+ return
+
+ try:
+ # Create a test directory
+ check_call('mkdir %s/dir1' % mount_dir, shell=True)
+
+ # Create a small file and calculate md5
+ check_call('dd if=/dev/urandom of=%s bs=1K count=20'
+ % min_file, shell=True)
+ out = check_output(
+ 'dd if=%s bs=1K 2> /dev/null | md5sum'
+ % min_file, shell=True).decode()
+ md5val = [ out.split()[0] ]
+
+ # Calculate md5sum of Test Case 4
+ check_call('dd if=%s of=%s bs=1K count=20'
+ % (min_file, tmp_file), shell=True)
+ check_call('dd if=%s of=%s bs=1K seek=5 count=20'
+ % (min_file, tmp_file), shell=True)
+ out = check_output('dd if=%s bs=1K 2> /dev/null | md5sum'
+ % tmp_file, shell=True).decode()
+ md5val.append(out.split()[0])
+
+ # Calculate md5sum of Test Case 5
+ check_call('dd if=%s of=%s bs=1K count=20'
+ % (min_file, tmp_file), shell=True)
+ check_call('dd if=%s of=%s bs=1K seek=5 count=5'
+ % (min_file, tmp_file), shell=True)
+ out = check_output('dd if=%s bs=1K 2> /dev/null | md5sum'
+ % tmp_file, shell=True).decode()
+ md5val.append(out.split()[0])
+
+ # Calculate md5sum of Test Case 7
+ check_call('dd if=%s of=%s bs=1K count=20'
+ % (min_file, tmp_file), shell=True)
+ check_call('dd if=%s of=%s bs=1K seek=20 count=20'
+ % (min_file, tmp_file), shell=True)
+ out = check_output('dd if=%s bs=1K 2> /dev/null | md5sum'
+ % tmp_file, shell=True).decode()
+ md5val.append(out.split()[0])
+
+ check_call('rm %s' % tmp_file, shell=True)
+ except CalledProcessError:
+ pytest.skip('Setup failed for filesystem: ' + fs_type)
+ umount_fs(mount_dir)
+ return
+ else:
+ umount_fs(mount_dir)
+ yield [fs_ubtype, fs_img, md5val]
+ finally:
+ call('rmdir %s' % mount_dir, shell=True)
+ call('rm -f %s' % fs_img, shell=True)
+
+#
+# Fixture for mkdir test
+#
+@pytest.fixture()
+def fs_obj_mkdir(request, u_boot_config):
+ """Set up a file system to be used in mkdir test.
+
+ Args:
+ request: Pytest request object.
+ u_boot_config: U-boot configuration.
+
+ Return:
+ A fixture for mkdir test, i.e. a duplet of file system type and
+ volume file name.
+ """
+ fs_type = request.param
+ fs_img = ''
+
+ fs_ubtype = fstype_to_ubname(fs_type)
+ check_ubconfig(u_boot_config, fs_ubtype)
+
+ try:
+ # 128MiB volume
+ fs_img = mk_fs(u_boot_config, fs_type, 0x8000000, '128MB')
+ except:
+ pytest.skip('Setup failed for filesystem: ' + fs_type)
+ return
+ else:
+ yield [fs_ubtype, fs_img]
+ call('rm -f %s' % fs_img, shell=True)
+
+#
+# Fixture for unlink test
+#
+@pytest.fixture()
+def fs_obj_unlink(request, u_boot_config):
+ """Set up a file system to be used in unlink test.
+
+ Args:
+ request: Pytest request object.
+ u_boot_config: U-boot configuration.
+
+ Return:
+ A fixture for unlink test, i.e. a duplet of file system type and
+ volume file name.
+ """
+ fs_type = request.param
+ fs_img = ''
+
+ fs_ubtype = fstype_to_ubname(fs_type)
+ check_ubconfig(u_boot_config, fs_ubtype)
+
+ mount_dir = u_boot_config.persistent_data_dir + '/mnt'
+
+ try:
+
+ # 128MiB volume
+ fs_img = mk_fs(u_boot_config, fs_type, 0x8000000, '128MB')
+ except CalledProcessError as err:
+ pytest.skip('Creating failed for filesystem: ' + fs_type + '. {}'.format(err))
+ return
+
+ try:
+ check_call('mkdir -p %s' % mount_dir, shell=True)
+ except CalledProcessError as err:
+ pytest.skip('Preparing mount folder failed for filesystem: ' + fs_type + '. {}'.format(err))
+ call('rm -f %s' % fs_img, shell=True)
+ return
+
+ try:
+ # Mount the image so we can populate it.
+ mount_fs(fs_type, fs_img, mount_dir)
+ except CalledProcessError as err:
+ pytest.skip('Mounting to folder failed for filesystem: ' + fs_type + '. {}'.format(err))
+ call('rmdir %s' % mount_dir, shell=True)
+ call('rm -f %s' % fs_img, shell=True)
+ return
+
+ try:
+ # Test Case 1 & 3
+ check_call('mkdir %s/dir1' % mount_dir, shell=True)
+ check_call('dd if=/dev/urandom of=%s/dir1/file1 bs=1K count=1'
+ % mount_dir, shell=True)
+ check_call('dd if=/dev/urandom of=%s/dir1/file2 bs=1K count=1'
+ % mount_dir, shell=True)
+
+ # Test Case 2
+ check_call('mkdir %s/dir2' % mount_dir, shell=True)
+ for i in range(0, 20):
+ check_call('mkdir %s/dir2/0123456789abcdef%02x'
+ % (mount_dir, i), shell=True)
+
+ # Test Case 4
+ check_call('mkdir %s/dir4' % mount_dir, shell=True)
+
+ # Test Case 5, 6 & 7
+ check_call('mkdir %s/dir5' % mount_dir, shell=True)
+ check_call('dd if=/dev/urandom of=%s/dir5/file1 bs=1K count=1'
+ % mount_dir, shell=True)
+
+ except CalledProcessError:
+ pytest.skip('Setup failed for filesystem: ' + fs_type)
+ umount_fs(mount_dir)
+ return
+ else:
+ umount_fs(mount_dir)
+ yield [fs_ubtype, fs_img]
+ finally:
+ call('rmdir %s' % mount_dir, shell=True)
+ call('rm -f %s' % fs_img, shell=True)
+
+#
+# Fixture for symlink fs test
+#
+@pytest.fixture()
+def fs_obj_symlink(request, u_boot_config):
+ """Set up a file system to be used in symlink fs test.
+
+ Args:
+ request: Pytest request object.
+ u_boot_config: U-boot configuration.
+
+ Return:
+ A fixture for basic fs test, i.e. a triplet of file system type,
+ volume file name and a list of MD5 hashes.
+ """
+ fs_type = request.param
+ fs_img = ''
+
+ fs_ubtype = fstype_to_ubname(fs_type)
+ check_ubconfig(u_boot_config, fs_ubtype)
+
+ mount_dir = u_boot_config.persistent_data_dir + '/mnt'
+
+ small_file = mount_dir + '/' + SMALL_FILE
+ medium_file = mount_dir + '/' + MEDIUM_FILE
+
+ try:
+
+ # 1GiB volume
+ fs_img = mk_fs(u_boot_config, fs_type, 0x40000000, '1GB')
+ except CalledProcessError as err:
+ pytest.skip('Creating failed for filesystem: ' + fs_type + '. {}'.format(err))
+ return
+
+ try:
+ check_call('mkdir -p %s' % mount_dir, shell=True)
+ except CalledProcessError as err:
+ pytest.skip('Preparing mount folder failed for filesystem: ' + fs_type + '. {}'.format(err))
+ call('rm -f %s' % fs_img, shell=True)
+ return
+
+ try:
+ # Mount the image so we can populate it.
+ mount_fs(fs_type, fs_img, mount_dir)
+ except CalledProcessError as err:
+ pytest.skip('Mounting to folder failed for filesystem: ' + fs_type + '. {}'.format(err))
+ call('rmdir %s' % mount_dir, shell=True)
+ call('rm -f %s' % fs_img, shell=True)
+ return
+
+ try:
+ # Create a subdirectory.
+ check_call('mkdir %s/SUBDIR' % mount_dir, shell=True)
+
+ # Create a small file in this image.
+ check_call('dd if=/dev/urandom of=%s bs=1M count=1'
+ % small_file, shell=True)
+
+ # Create a medium file in this image.
+ check_call('dd if=/dev/urandom of=%s bs=10M count=1'
+ % medium_file, shell=True)
+
+ # Generate the md5sums of reads that we will test against small file
+ out = check_output(
+ 'dd if=%s bs=1M skip=0 count=1 2> /dev/null | md5sum'
+ % small_file, shell=True).decode()
+ md5val = [out.split()[0]]
+ out = check_output(
+ 'dd if=%s bs=10M skip=0 count=1 2> /dev/null | md5sum'
+ % medium_file, shell=True).decode()
+ md5val.extend([out.split()[0]])
+
+ except CalledProcessError:
+ pytest.skip('Setup failed for filesystem: ' + fs_type)
+ umount_fs(mount_dir)
+ return
+ else:
+ umount_fs(mount_dir)
+ yield [fs_ubtype, fs_img, md5val]
+ finally:
+ call('rmdir %s' % mount_dir, shell=True)
+ call('rm -f %s' % fs_img, shell=True)
diff --git a/roms/u-boot/test/py/tests/test_fs/fstest_defs.py b/roms/u-boot/test/py/tests/test_fs/fstest_defs.py
new file mode 100644
index 000000000..35b2bb651
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_fs/fstest_defs.py
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0+
+
+# $MIN_FILE is the name of the 20KB file in the file system image
+MIN_FILE='testfile'
+
+# $SMALL_FILE is the name of the 1MB file in the file system image
+SMALL_FILE='1MB.file'
+
+# $MEDIUM_FILE is the name of the 10MB file in the file system image
+MEDIUM_FILE='10MB.file'
+
+# $BIG_FILE is the name of the 2.5GB file in the file system image
+BIG_FILE='2.5GB.file'
+
+ADDR=0x01000008
+LENGTH=0x00100000
diff --git a/roms/u-boot/test/py/tests/test_fs/fstest_helpers.py b/roms/u-boot/test/py/tests/test_fs/fstest_helpers.py
new file mode 100644
index 000000000..faec29824
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_fs/fstest_helpers.py
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2019, Texas Instrument
+# Author: JJ Hiblot <jjhiblot@ti.com>
+#
+
+from subprocess import check_call, CalledProcessError
+
+def assert_fs_integrity(fs_type, fs_img):
+ try:
+ if fs_type == 'ext4':
+ check_call('fsck.ext4 -n -f %s' % fs_img, shell=True)
+ except CalledProcessError:
+ raise
diff --git a/roms/u-boot/test/py/tests/test_fs/test_basic.py b/roms/u-boot/test/py/tests/test_fs/test_basic.py
new file mode 100644
index 000000000..71f3e86fb
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_fs/test_basic.py
@@ -0,0 +1,292 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2018, Linaro Limited
+# Author: Takahiro Akashi <takahiro.akashi@linaro.org>
+#
+# U-Boot File System:Basic Test
+
+"""
+This test verifies basic read/write operation on file system.
+"""
+
+import pytest
+import re
+from fstest_defs import *
+from fstest_helpers import assert_fs_integrity
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.slow
+class TestFsBasic(object):
+ def test_fs1(self, u_boot_console, fs_obj_basic):
+ """
+ Test Case 1 - ls command, listing a root directory and invalid directory
+ """
+ fs_type,fs_img,md5val = fs_obj_basic
+ with u_boot_console.log.section('Test Case 1a - ls'):
+ # Test Case 1 - ls
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%sls host 0:0' % fs_type])
+ assert(re.search('2621440000 *%s' % BIG_FILE, ''.join(output)))
+ assert(re.search('1048576 *%s' % SMALL_FILE, ''.join(output)))
+
+ with u_boot_console.log.section('Test Case 1b - ls (invalid dir)'):
+ # In addition, test with a nonexistent directory to see if we crash.
+ output = u_boot_console.run_command(
+ '%sls host 0:0 invalid_d' % fs_type)
+ if fs_type == 'ext4':
+ assert('Can not find directory' in output)
+ else:
+ assert('' == output)
+
+ def test_fs2(self, u_boot_console, fs_obj_basic):
+ """
+ Test Case 2 - size command for a small file
+ """
+ fs_type,fs_img,md5val = fs_obj_basic
+ with u_boot_console.log.section('Test Case 2a - size (small)'):
+ # 1MB is 0x0010 0000
+ # Test Case 2a - size of small file
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%ssize host 0:0 /%s' % (fs_type, SMALL_FILE),
+ 'printenv filesize',
+ 'setenv filesize'])
+ assert('filesize=100000' in ''.join(output))
+
+ with u_boot_console.log.section('Test Case 2b - size (/../<file>)'):
+ # Test Case 2b - size of small file via a path using '..'
+ output = u_boot_console.run_command_list([
+ '%ssize host 0:0 /SUBDIR/../%s' % (fs_type, SMALL_FILE),
+ 'printenv filesize',
+ 'setenv filesize'])
+ assert('filesize=100000' in ''.join(output))
+
+ def test_fs3(self, u_boot_console, fs_obj_basic):
+ """
+ Test Case 3 - size command for a large file
+ """
+ fs_type,fs_img,md5val = fs_obj_basic
+ with u_boot_console.log.section('Test Case 3 - size (large)'):
+ # 2.5GB (1024*1024*2500) is 0x9C40 0000
+ # Test Case 3 - size of big file
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%ssize host 0:0 /%s' % (fs_type, BIG_FILE),
+ 'printenv filesize',
+ 'setenv filesize'])
+ assert('filesize=9c400000' in ''.join(output))
+
+ def test_fs4(self, u_boot_console, fs_obj_basic):
+ """
+ Test Case 4 - load a small file, 1MB
+ """
+ fs_type,fs_img,md5val = fs_obj_basic
+ with u_boot_console.log.section('Test Case 4 - load (small)'):
+ # Test Case 4a - Read full 1MB of small file
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%sload host 0:0 %x /%s' % (fs_type, ADDR, SMALL_FILE),
+ 'printenv filesize'])
+ assert('filesize=100000' in ''.join(output))
+
+ # Test Case 4b - Read full 1MB of small file
+ output = u_boot_console.run_command_list([
+ 'md5sum %x $filesize' % ADDR,
+ 'setenv filesize'])
+ assert(md5val[0] in ''.join(output))
+
+ def test_fs5(self, u_boot_console, fs_obj_basic):
+ """
+ Test Case 5 - load, reading first 1MB of 3GB file
+ """
+ fs_type,fs_img,md5val = fs_obj_basic
+ with u_boot_console.log.section('Test Case 5 - load (first 1MB)'):
+ # Test Case 5a - First 1MB of big file
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%sload host 0:0 %x /%s %x 0x0' % (fs_type, ADDR, BIG_FILE, LENGTH),
+ 'printenv filesize'])
+ assert('filesize=100000' in ''.join(output))
+
+ # Test Case 5b - First 1MB of big file
+ output = u_boot_console.run_command_list([
+ 'md5sum %x $filesize' % ADDR,
+ 'setenv filesize'])
+ assert(md5val[1] in ''.join(output))
+
+ def test_fs6(self, u_boot_console, fs_obj_basic):
+ """
+ Test Case 6 - load, reading last 1MB of 3GB file
+ """
+ fs_type,fs_img,md5val = fs_obj_basic
+ with u_boot_console.log.section('Test Case 6 - load (last 1MB)'):
+ # fails for ext as no offset support
+ # Test Case 6a - Last 1MB of big file
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%sload host 0:0 %x /%s %x 0x9c300000'
+ % (fs_type, ADDR, BIG_FILE, LENGTH),
+ 'printenv filesize'])
+ assert('filesize=100000' in ''.join(output))
+
+ # Test Case 6b - Last 1MB of big file
+ output = u_boot_console.run_command_list([
+ 'md5sum %x $filesize' % ADDR,
+ 'setenv filesize'])
+ assert(md5val[2] in ''.join(output))
+
+ def test_fs7(self, u_boot_console, fs_obj_basic):
+ """
+ Test Case 7 - load, 1MB from the last 1MB in 2GB
+ """
+ fs_type,fs_img,md5val = fs_obj_basic
+ with u_boot_console.log.section('Test Case 7 - load (last 1MB in 2GB)'):
+ # fails for ext as no offset support
+ # Test Case 7a - One from the last 1MB chunk of 2GB
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%sload host 0:0 %x /%s %x 0x7ff00000'
+ % (fs_type, ADDR, BIG_FILE, LENGTH),
+ 'printenv filesize'])
+ assert('filesize=100000' in ''.join(output))
+
+ # Test Case 7b - One from the last 1MB chunk of 2GB
+ output = u_boot_console.run_command_list([
+ 'md5sum %x $filesize' % ADDR,
+ 'setenv filesize'])
+ assert(md5val[3] in ''.join(output))
+
+ def test_fs8(self, u_boot_console, fs_obj_basic):
+ """
+ Test Case 8 - load, reading first 1MB in 2GB
+ """
+ fs_type,fs_img,md5val = fs_obj_basic
+ with u_boot_console.log.section('Test Case 8 - load (first 1MB in 2GB)'):
+ # fails for ext as no offset support
+ # Test Case 8a - One from the start 1MB chunk from 2GB
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%sload host 0:0 %x /%s %x 0x80000000'
+ % (fs_type, ADDR, BIG_FILE, LENGTH),
+ 'printenv filesize'])
+ assert('filesize=100000' in ''.join(output))
+
+ # Test Case 8b - One from the start 1MB chunk from 2GB
+ output = u_boot_console.run_command_list([
+ 'md5sum %x $filesize' % ADDR,
+ 'setenv filesize'])
+ assert(md5val[4] in ''.join(output))
+
+ def test_fs9(self, u_boot_console, fs_obj_basic):
+ """
+ Test Case 9 - load, 1MB crossing 2GB boundary
+ """
+ fs_type,fs_img,md5val = fs_obj_basic
+ with u_boot_console.log.section('Test Case 9 - load (crossing 2GB boundary)'):
+ # fails for ext as no offset support
+ # Test Case 9a - One 1MB chunk crossing the 2GB boundary
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%sload host 0:0 %x /%s %x 0x7ff80000'
+ % (fs_type, ADDR, BIG_FILE, LENGTH),
+ 'printenv filesize'])
+ assert('filesize=100000' in ''.join(output))
+
+ # Test Case 9b - One 1MB chunk crossing the 2GB boundary
+ output = u_boot_console.run_command_list([
+ 'md5sum %x $filesize' % ADDR,
+ 'setenv filesize'])
+ assert(md5val[5] in ''.join(output))
+
+ def test_fs10(self, u_boot_console, fs_obj_basic):
+ """
+ Test Case 10 - load, reading beyond file end'):
+ """
+ fs_type,fs_img,md5val = fs_obj_basic
+ with u_boot_console.log.section('Test Case 10 - load (beyond file end)'):
+ # Generic failure case
+ # Test Case 10 - 2MB chunk from the last 1MB of big file
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%sload host 0:0 %x /%s 0x00200000 0x9c300000'
+ % (fs_type, ADDR, BIG_FILE),
+ 'printenv filesize',
+ 'md5sum %x $filesize' % ADDR,
+ 'setenv filesize'])
+ assert('filesize=100000' in ''.join(output))
+
+ def test_fs11(self, u_boot_console, fs_obj_basic):
+ """
+ Test Case 11 - write'
+ """
+ fs_type,fs_img,md5val = fs_obj_basic
+ with u_boot_console.log.section('Test Case 11 - write'):
+ # Read 1MB from small file
+ # Write it back to test the writes
+ # Test Case 11a - Check that the write succeeded
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%sload host 0:0 %x /%s' % (fs_type, ADDR, SMALL_FILE),
+ '%swrite host 0:0 %x /%s.w $filesize'
+ % (fs_type, ADDR, SMALL_FILE)])
+ assert('1048576 bytes written' in ''.join(output))
+
+ # Test Case 11b - Check md5 of written to is same
+ # as the one read from
+ output = u_boot_console.run_command_list([
+ '%sload host 0:0 %x /%s.w' % (fs_type, ADDR, SMALL_FILE),
+ 'md5sum %x $filesize' % ADDR,
+ 'setenv filesize'])
+ assert(md5val[0] in ''.join(output))
+ assert_fs_integrity(fs_type, fs_img)
+
+ def test_fs12(self, u_boot_console, fs_obj_basic):
+ """
+ Test Case 12 - write to "." directory
+ """
+ fs_type,fs_img,md5val = fs_obj_basic
+ with u_boot_console.log.section('Test Case 12 - write (".")'):
+ # Next test case checks writing a file whose dirent
+ # is the first in the block, which is always true for "."
+ # The write should fail, but the lookup should work
+ # Test Case 12 - Check directory traversal
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%swrite host 0:0 %x /. 0x10' % (fs_type, ADDR)])
+ assert('Unable to write' in ''.join(output))
+ assert_fs_integrity(fs_type, fs_img)
+
+ def test_fs13(self, u_boot_console, fs_obj_basic):
+ """
+ Test Case 13 - write to a file with "/./<filename>"
+ """
+ fs_type,fs_img,md5val = fs_obj_basic
+ with u_boot_console.log.section('Test Case 13 - write ("./<file>")'):
+ # Read 1MB from small file
+ # Write it via "same directory", i.e. "." dirent
+ # Test Case 13a - Check directory traversal
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%sload host 0:0 %x /%s' % (fs_type, ADDR, SMALL_FILE),
+ '%swrite host 0:0 %x /./%s2 $filesize'
+ % (fs_type, ADDR, SMALL_FILE)])
+ assert('1048576 bytes written' in ''.join(output))
+
+ # Test Case 13b - Check md5 of written to is same
+ # as the one read from
+ output = u_boot_console.run_command_list([
+ 'mw.b %x 00 100' % ADDR,
+ '%sload host 0:0 %x /./%s2' % (fs_type, ADDR, SMALL_FILE),
+ 'md5sum %x $filesize' % ADDR,
+ 'setenv filesize'])
+ assert(md5val[0] in ''.join(output))
+
+ # Test Case 13c - Check md5 of written to is same
+ # as the one read from
+ output = u_boot_console.run_command_list([
+ 'mw.b %x 00 100' % ADDR,
+ '%sload host 0:0 %x /%s2' % (fs_type, ADDR, SMALL_FILE),
+ 'md5sum %x $filesize' % ADDR,
+ 'setenv filesize'])
+ assert(md5val[0] in ''.join(output))
+ assert_fs_integrity(fs_type, fs_img)
diff --git a/roms/u-boot/test/py/tests/test_fs/test_ext.py b/roms/u-boot/test/py/tests/test_fs/test_ext.py
new file mode 100644
index 000000000..dba874fc5
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_fs/test_ext.py
@@ -0,0 +1,319 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2018, Linaro Limited
+# Author: Takahiro Akashi <takahiro.akashi@linaro.org>
+#
+# U-Boot File System:Exntented Test
+
+"""
+This test verifies extended write operation on file system.
+"""
+
+import pytest
+import re
+from fstest_defs import *
+from fstest_helpers import assert_fs_integrity
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.slow
+class TestFsExt(object):
+ def test_fs_ext1(self, u_boot_console, fs_obj_ext):
+ """
+ Test Case 1 - write a file with absolute path
+ """
+ fs_type,fs_img,md5val = fs_obj_ext
+ with u_boot_console.log.section('Test Case 1 - write with abs path'):
+ # Test Case 1a - Check if command successfully returned
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%sload host 0:0 %x /%s' % (fs_type, ADDR, MIN_FILE),
+ '%swrite host 0:0 %x /dir1/%s.w1 $filesize'
+ % (fs_type, ADDR, MIN_FILE)])
+ assert('20480 bytes written' in ''.join(output))
+
+ # Test Case 1b - Check md5 of file content
+ output = u_boot_console.run_command_list([
+ 'mw.b %x 00 100' % ADDR,
+ '%sload host 0:0 %x /dir1/%s.w1' % (fs_type, ADDR, MIN_FILE),
+ 'md5sum %x $filesize' % ADDR,
+ 'setenv filesize'])
+ assert(md5val[0] in ''.join(output))
+ assert_fs_integrity(fs_type, fs_img)
+
+ def test_fs_ext2(self, u_boot_console, fs_obj_ext):
+ """
+ Test Case 2 - write to a file with relative path
+ """
+ fs_type,fs_img,md5val = fs_obj_ext
+ with u_boot_console.log.section('Test Case 2 - write with rel path'):
+ # Test Case 2a - Check if command successfully returned
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%sload host 0:0 %x /%s' % (fs_type, ADDR, MIN_FILE),
+ '%swrite host 0:0 %x dir1/%s.w2 $filesize'
+ % (fs_type, ADDR, MIN_FILE)])
+ assert('20480 bytes written' in ''.join(output))
+
+ # Test Case 2b - Check md5 of file content
+ output = u_boot_console.run_command_list([
+ 'mw.b %x 00 100' % ADDR,
+ '%sload host 0:0 %x dir1/%s.w2' % (fs_type, ADDR, MIN_FILE),
+ 'md5sum %x $filesize' % ADDR,
+ 'setenv filesize'])
+ assert(md5val[0] in ''.join(output))
+ assert_fs_integrity(fs_type, fs_img)
+
+ def test_fs_ext3(self, u_boot_console, fs_obj_ext):
+ """
+ Test Case 3 - write to a file with invalid path
+ """
+ fs_type,fs_img,md5val = fs_obj_ext
+ with u_boot_console.log.section('Test Case 3 - write with invalid path'):
+ # Test Case 3 - Check if command expectedly failed
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%sload host 0:0 %x /%s' % (fs_type, ADDR, MIN_FILE),
+ '%swrite host 0:0 %x /dir1/none/%s.w3 $filesize'
+ % (fs_type, ADDR, MIN_FILE)])
+ assert('Unable to write file /dir1/none/' in ''.join(output))
+ assert_fs_integrity(fs_type, fs_img)
+
+ def test_fs_ext4(self, u_boot_console, fs_obj_ext):
+ """
+ Test Case 4 - write at non-zero offset, enlarging file size
+ """
+ fs_type,fs_img,md5val = fs_obj_ext
+ with u_boot_console.log.section('Test Case 4 - write at non-zero offset, enlarging file size'):
+ # Test Case 4a - Check if command successfully returned
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%sload host 0:0 %x /%s' % (fs_type, ADDR, MIN_FILE),
+ '%swrite host 0:0 %x /dir1/%s.w4 $filesize'
+ % (fs_type, ADDR, MIN_FILE)])
+ output = u_boot_console.run_command(
+ '%swrite host 0:0 %x /dir1/%s.w4 $filesize 0x1400'
+ % (fs_type, ADDR, MIN_FILE))
+ assert('20480 bytes written' in output)
+
+ # Test Case 4b - Check size of written file
+ output = u_boot_console.run_command_list([
+ '%ssize host 0:0 /dir1/%s.w4' % (fs_type, MIN_FILE),
+ 'printenv filesize',
+ 'setenv filesize'])
+ assert('filesize=6400' in ''.join(output))
+
+ # Test Case 4c - Check md5 of file content
+ output = u_boot_console.run_command_list([
+ 'mw.b %x 00 100' % ADDR,
+ '%sload host 0:0 %x /dir1/%s.w4' % (fs_type, ADDR, MIN_FILE),
+ 'md5sum %x $filesize' % ADDR,
+ 'setenv filesize'])
+ assert(md5val[1] in ''.join(output))
+ assert_fs_integrity(fs_type, fs_img)
+
+ def test_fs_ext5(self, u_boot_console, fs_obj_ext):
+ """
+ Test Case 5 - write at non-zero offset, shrinking file size
+ """
+ fs_type,fs_img,md5val = fs_obj_ext
+ with u_boot_console.log.section('Test Case 5 - write at non-zero offset, shrinking file size'):
+ # Test Case 5a - Check if command successfully returned
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%sload host 0:0 %x /%s' % (fs_type, ADDR, MIN_FILE),
+ '%swrite host 0:0 %x /dir1/%s.w5 $filesize'
+ % (fs_type, ADDR, MIN_FILE)])
+ output = u_boot_console.run_command(
+ '%swrite host 0:0 %x /dir1/%s.w5 0x1400 0x1400'
+ % (fs_type, ADDR, MIN_FILE))
+ assert('5120 bytes written' in output)
+
+ # Test Case 5b - Check size of written file
+ output = u_boot_console.run_command_list([
+ '%ssize host 0:0 /dir1/%s.w5' % (fs_type, MIN_FILE),
+ 'printenv filesize',
+ 'setenv filesize'])
+ assert('filesize=2800' in ''.join(output))
+
+ # Test Case 5c - Check md5 of file content
+ output = u_boot_console.run_command_list([
+ 'mw.b %x 00 100' % ADDR,
+ '%sload host 0:0 %x /dir1/%s.w5' % (fs_type, ADDR, MIN_FILE),
+ 'md5sum %x $filesize' % ADDR,
+ 'setenv filesize'])
+ assert(md5val[2] in ''.join(output))
+ assert_fs_integrity(fs_type, fs_img)
+
+ def test_fs_ext6(self, u_boot_console, fs_obj_ext):
+ """
+ Test Case 6 - write nothing at the start, truncating to zero
+ """
+ fs_type,fs_img,md5val = fs_obj_ext
+ with u_boot_console.log.section('Test Case 6 - write nothing at the start, truncating to zero'):
+ # Test Case 6a - Check if command successfully returned
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%sload host 0:0 %x /%s' % (fs_type, ADDR, MIN_FILE),
+ '%swrite host 0:0 %x /dir1/%s.w6 $filesize'
+ % (fs_type, ADDR, MIN_FILE)])
+ output = u_boot_console.run_command(
+ '%swrite host 0:0 %x /dir1/%s.w6 0 0'
+ % (fs_type, ADDR, MIN_FILE))
+ assert('0 bytes written' in output)
+
+ # Test Case 6b - Check size of written file
+ output = u_boot_console.run_command_list([
+ '%ssize host 0:0 /dir1/%s.w6' % (fs_type, MIN_FILE),
+ 'printenv filesize',
+ 'setenv filesize'])
+ assert('filesize=0' in ''.join(output))
+ assert_fs_integrity(fs_type, fs_img)
+
+ def test_fs_ext7(self, u_boot_console, fs_obj_ext):
+ """
+ Test Case 7 - write at the end (append)
+ """
+ fs_type,fs_img,md5val = fs_obj_ext
+ with u_boot_console.log.section('Test Case 7 - write at the end (append)'):
+ # Test Case 7a - Check if command successfully returned
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%sload host 0:0 %x /%s' % (fs_type, ADDR, MIN_FILE),
+ '%swrite host 0:0 %x /dir1/%s.w7 $filesize'
+ % (fs_type, ADDR, MIN_FILE)])
+ output = u_boot_console.run_command(
+ '%swrite host 0:0 %x /dir1/%s.w7 $filesize $filesize'
+ % (fs_type, ADDR, MIN_FILE))
+ assert('20480 bytes written' in output)
+
+ # Test Case 7b - Check size of written file
+ output = u_boot_console.run_command_list([
+ '%ssize host 0:0 /dir1/%s.w7' % (fs_type, MIN_FILE),
+ 'printenv filesize',
+ 'setenv filesize'])
+ assert('filesize=a000' in ''.join(output))
+
+ # Test Case 7c - Check md5 of file content
+ output = u_boot_console.run_command_list([
+ 'mw.b %x 00 100' % ADDR,
+ '%sload host 0:0 %x /dir1/%s.w7' % (fs_type, ADDR, MIN_FILE),
+ 'md5sum %x $filesize' % ADDR,
+ 'setenv filesize'])
+ assert(md5val[3] in ''.join(output))
+ assert_fs_integrity(fs_type, fs_img)
+
+ def test_fs_ext8(self, u_boot_console, fs_obj_ext):
+ """
+ Test Case 8 - write at offset beyond the end of file
+ """
+ fs_type,fs_img,md5val = fs_obj_ext
+ with u_boot_console.log.section('Test Case 8 - write beyond the end'):
+ # Test Case 8a - Check if command expectedly failed
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%sload host 0:0 %x /%s' % (fs_type, ADDR, MIN_FILE),
+ '%swrite host 0:0 %x /dir1/%s.w8 $filesize'
+ % (fs_type, ADDR, MIN_FILE)])
+ output = u_boot_console.run_command(
+ '%swrite host 0:0 %x /dir1/%s.w8 0x1400 %x'
+ % (fs_type, ADDR, MIN_FILE, 0x100000 + 0x1400))
+ assert('Unable to write file /dir1' in output)
+ assert_fs_integrity(fs_type, fs_img)
+
+ def test_fs_ext9(self, u_boot_console, fs_obj_ext):
+ """
+ Test Case 9 - write to a non-existing file at non-zero offset
+ """
+ fs_type,fs_img,md5val = fs_obj_ext
+ with u_boot_console.log.section('Test Case 9 - write to non-existing file with non-zero offset'):
+ # Test Case 9a - Check if command expectedly failed
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%sload host 0:0 %x /%s' % (fs_type, ADDR, MIN_FILE),
+ '%swrite host 0:0 %x /dir1/%s.w9 0x1400 0x1400'
+ % (fs_type, ADDR, MIN_FILE)])
+ assert('Unable to write file /dir1' in ''.join(output))
+ assert_fs_integrity(fs_type, fs_img)
+
+ def test_fs_ext10(self, u_boot_console, fs_obj_ext):
+ """
+ 'Test Case 10 - create/delete as many directories under root directory
+ as amount of directory entries goes beyond one cluster size)'
+ """
+ fs_type,fs_img,md5val = fs_obj_ext
+ with u_boot_console.log.section('Test Case 10 - create/delete (many)'):
+ # Test Case 10a - Create many files
+ # Please note that the size of directory entry is 32 bytes.
+ # So one typical cluster may holds 64 (2048/32) entries.
+ output = u_boot_console.run_command(
+ 'host bind 0 %s' % fs_img)
+
+ for i in range(0, 66):
+ output = u_boot_console.run_command(
+ '%swrite host 0:0 %x /FILE0123456789_%02x 100'
+ % (fs_type, ADDR, i))
+ output = u_boot_console.run_command('%sls host 0:0 /' % fs_type)
+ assert('FILE0123456789_00' in output)
+ assert('FILE0123456789_41' in output)
+
+ # Test Case 10b - Delete many files
+ for i in range(0, 66):
+ output = u_boot_console.run_command(
+ '%srm host 0:0 /FILE0123456789_%02x'
+ % (fs_type, i))
+ output = u_boot_console.run_command('%sls host 0:0 /' % fs_type)
+ assert(not 'FILE0123456789_00' in output)
+ assert(not 'FILE0123456789_41' in output)
+
+ # Test Case 10c - Create many files again
+ # Please note no.64 and 65 are intentionally re-created
+ for i in range(64, 128):
+ output = u_boot_console.run_command(
+ '%swrite host 0:0 %x /FILE0123456789_%02x 100'
+ % (fs_type, ADDR, i))
+ output = u_boot_console.run_command('%sls host 0:0 /' % fs_type)
+ assert('FILE0123456789_40' in output)
+ assert('FILE0123456789_79' in output)
+
+ assert_fs_integrity(fs_type, fs_img)
+
+ def test_fs_ext11(self, u_boot_console, fs_obj_ext):
+ """
+ 'Test Case 11 - create/delete as many directories under non-root
+ directory as amount of directory entries goes beyond one cluster size)'
+ """
+ fs_type,fs_img,md5val = fs_obj_ext
+ with u_boot_console.log.section('Test Case 11 - create/delete (many)'):
+ # Test Case 11a - Create many files
+ # Please note that the size of directory entry is 32 bytes.
+ # So one typical cluster may holds 64 (2048/32) entries.
+ output = u_boot_console.run_command(
+ 'host bind 0 %s' % fs_img)
+
+ for i in range(0, 66):
+ output = u_boot_console.run_command(
+ '%swrite host 0:0 %x /dir1/FILE0123456789_%02x 100'
+ % (fs_type, ADDR, i))
+ output = u_boot_console.run_command('%sls host 0:0 /dir1' % fs_type)
+ assert('FILE0123456789_00' in output)
+ assert('FILE0123456789_41' in output)
+
+ # Test Case 11b - Delete many files
+ for i in range(0, 66):
+ output = u_boot_console.run_command(
+ '%srm host 0:0 /dir1/FILE0123456789_%02x'
+ % (fs_type, i))
+ output = u_boot_console.run_command('%sls host 0:0 /dir1' % fs_type)
+ assert(not 'FILE0123456789_00' in output)
+ assert(not 'FILE0123456789_41' in output)
+
+ # Test Case 11c - Create many files again
+ # Please note no.64 and 65 are intentionally re-created
+ for i in range(64, 128):
+ output = u_boot_console.run_command(
+ '%swrite host 0:0 %x /dir1/FILE0123456789_%02x 100'
+ % (fs_type, ADDR, i))
+ output = u_boot_console.run_command('%sls host 0:0 /dir1' % fs_type)
+ assert('FILE0123456789_40' in output)
+ assert('FILE0123456789_79' in output)
+
+ assert_fs_integrity(fs_type, fs_img)
diff --git a/roms/u-boot/test/py/tests/test_fs/test_fs_cmd.py b/roms/u-boot/test/py/tests/test_fs/test_fs_cmd.py
new file mode 100644
index 000000000..ba39a5315
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_fs/test_fs_cmd.py
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2020
+# Niel Fourie, DENX Software Engineering, lusus@denx.de
+
+import pytest
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('cmd_fs_generic')
+def test_dm_compat(u_boot_console):
+ """Test that `fstypes` prints a result which includes `sandbox`."""
+ output = u_boot_console.run_command('fstypes')
+ assert "Supported filesystems:" in output
+ assert "sandbox" in output
diff --git a/roms/u-boot/test/py/tests/test_fs/test_mkdir.py b/roms/u-boot/test/py/tests/test_fs/test_mkdir.py
new file mode 100644
index 000000000..fa9561ec3
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_fs/test_mkdir.py
@@ -0,0 +1,121 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2018, Linaro Limited
+# Author: Takahiro Akashi <takahiro.akashi@linaro.org>
+#
+# U-Boot File System:mkdir Test
+
+"""
+This test verifies mkdir operation on file system.
+"""
+
+import pytest
+from fstest_helpers import assert_fs_integrity
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.slow
+class TestMkdir(object):
+ def test_mkdir1(self, u_boot_console, fs_obj_mkdir):
+ """
+ Test Case 1 - create a directory under a root
+ """
+ fs_type,fs_img = fs_obj_mkdir
+ with u_boot_console.log.section('Test Case 1 - mkdir'):
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%smkdir host 0:0 dir1' % fs_type,
+ '%sls host 0:0 /' % fs_type])
+ assert('dir1/' in ''.join(output))
+
+ output = u_boot_console.run_command(
+ '%sls host 0:0 dir1' % fs_type)
+ assert('./' in output)
+ assert('../' in output)
+ assert_fs_integrity(fs_type, fs_img)
+
+
+ def test_mkdir2(self, u_boot_console, fs_obj_mkdir):
+ """
+ Test Case 2 - create a directory under a sub-directory
+ """
+ fs_type,fs_img = fs_obj_mkdir
+ with u_boot_console.log.section('Test Case 2 - mkdir (sub-sub directory)'):
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%smkdir host 0:0 dir1/dir2' % fs_type,
+ '%sls host 0:0 dir1' % fs_type])
+ assert('dir2/' in ''.join(output))
+
+ output = u_boot_console.run_command(
+ '%sls host 0:0 dir1/dir2' % fs_type)
+ assert('./' in output)
+ assert('../' in output)
+ assert_fs_integrity(fs_type, fs_img)
+
+ def test_mkdir3(self, u_boot_console, fs_obj_mkdir):
+ """
+ Test Case 3 - trying to create a directory with a non-existing
+ path should fail
+ """
+ fs_type,fs_img = fs_obj_mkdir
+ with u_boot_console.log.section('Test Case 3 - mkdir (non-existing path)'):
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%smkdir host 0:0 none/dir3' % fs_type])
+ assert('Unable to create a directory' in ''.join(output))
+ assert_fs_integrity(fs_type, fs_img)
+
+ def test_mkdir4(self, u_boot_console, fs_obj_mkdir):
+ """
+ Test Case 4 - trying to create "." should fail
+ """
+ fs_type,fs_img = fs_obj_mkdir
+ with u_boot_console.log.section('Test Case 4 - mkdir (".")'):
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%smkdir host 0:0 .' % fs_type])
+ assert('Unable to create a directory' in ''.join(output))
+ assert_fs_integrity(fs_type, fs_img)
+
+ def test_mkdir5(self, u_boot_console, fs_obj_mkdir):
+ """
+ Test Case 5 - trying to create ".." should fail
+ """
+ fs_type,fs_img = fs_obj_mkdir
+ with u_boot_console.log.section('Test Case 5 - mkdir ("..")'):
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%smkdir host 0:0 ..' % fs_type])
+ assert('Unable to create a directory' in ''.join(output))
+ assert_fs_integrity(fs_type, fs_img)
+
+ def test_mkdir6(self, u_boot_console, fs_obj_mkdir):
+ """
+ 'Test Case 6 - create as many directories as amount of directory
+ entries goes beyond a cluster size)'
+ """
+ fs_type,fs_img = fs_obj_mkdir
+ with u_boot_console.log.section('Test Case 6 - mkdir (create many)'):
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%smkdir host 0:0 dir6' % fs_type,
+ '%sls host 0:0 /' % fs_type])
+ assert('dir6/' in ''.join(output))
+
+ for i in range(0, 20):
+ output = u_boot_console.run_command(
+ '%smkdir host 0:0 dir6/0123456789abcdef%02x'
+ % (fs_type, i))
+ output = u_boot_console.run_command('%sls host 0:0 dir6' % fs_type)
+ assert('0123456789abcdef00/' in output)
+ assert('0123456789abcdef13/' in output)
+
+ output = u_boot_console.run_command(
+ '%sls host 0:0 dir6/0123456789abcdef13/.' % fs_type)
+ assert('./' in output)
+ assert('../' in output)
+
+ output = u_boot_console.run_command(
+ '%sls host 0:0 dir6/0123456789abcdef13/..' % fs_type)
+ assert('0123456789abcdef00/' in output)
+ assert('0123456789abcdef13/' in output)
+ assert_fs_integrity(fs_type, fs_img)
diff --git a/roms/u-boot/test/py/tests/test_fs/test_squashfs/sqfs_common.py b/roms/u-boot/test/py/tests/test_fs/test_squashfs/sqfs_common.py
new file mode 100644
index 000000000..c96f92c1d
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_fs/test_squashfs/sqfs_common.py
@@ -0,0 +1,76 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2020 Bootlin
+# Author: Joao Marcos Costa <joaomarcos.costa@bootlin.com>
+
+import os
+import random
+import string
+import subprocess
+
+def sqfs_get_random_letters(size):
+ letters = []
+ for i in range(0, size):
+ letters.append(random.choice(string.ascii_letters))
+
+ return ''.join(letters)
+
+def sqfs_generate_file(path, size):
+ content = sqfs_get_random_letters(size)
+ file = open(path, "w")
+ file.write(content)
+ file.close()
+
+class Compression:
+ def __init__(self, name, files, sizes, block_size = 4096):
+ self.name = name
+ self.files = files
+ self.sizes = sizes
+ self.mksquashfs_opts = " -b " + str(block_size) + " -comp " + self.name
+
+ def add_opt(self, opt):
+ self.mksquashfs_opts += " " + opt
+
+ def gen_image(self, build_dir):
+ src = os.path.join(build_dir, "sqfs_src/")
+ os.mkdir(src)
+ for (f, s) in zip(self.files, self.sizes):
+ sqfs_generate_file(src + f, s)
+
+ # the symbolic link always targets the first file
+ os.symlink(self.files[0], src + "sym")
+
+ sqfs_img = os.path.join(build_dir, "sqfs-" + self.name)
+ i_o = src + " " + sqfs_img
+ opts = self.mksquashfs_opts
+ try:
+ subprocess.run(["mksquashfs " + i_o + opts], shell = True, check = True)
+ except:
+ print("mksquashfs error. Compression type: " + self.name)
+ raise RuntimeError
+
+ def clean_source(self, build_dir):
+ src = os.path.join(build_dir, "sqfs_src/")
+ for f in self.files:
+ os.remove(src + f)
+ os.remove(src + "sym")
+ os.rmdir(src)
+
+ def cleanup(self, build_dir):
+ self.clean_source(build_dir)
+ sqfs_img = os.path.join(build_dir, "sqfs-" + self.name)
+ os.remove(sqfs_img)
+
+files = ["blks_only", "blks_frag", "frag_only"]
+sizes = [4096, 5100, 100]
+gzip = Compression("gzip", files, sizes)
+zstd = Compression("zstd", files, sizes)
+lzo = Compression("lzo", files, sizes)
+
+# use fragment blocks for files larger than block_size
+gzip.add_opt("-always-use-fragments")
+zstd.add_opt("-always-use-fragments")
+
+# avoid fragments if lzo is used
+lzo.add_opt("-no-fragments")
+
+comp_opts = [gzip, zstd, lzo]
diff --git a/roms/u-boot/test/py/tests/test_fs/test_squashfs/test_sqfs_load.py b/roms/u-boot/test/py/tests/test_fs/test_squashfs/test_sqfs_load.py
new file mode 100644
index 000000000..9e9006238
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_fs/test_squashfs/test_sqfs_load.py
@@ -0,0 +1,46 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2020 Bootlin
+# Author: Joao Marcos Costa <joaomarcos.costa@bootlin.com>
+
+import os
+import pytest
+from sqfs_common import *
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('cmd_fs_generic')
+@pytest.mark.buildconfigspec('cmd_squashfs')
+@pytest.mark.buildconfigspec('fs_squashfs')
+@pytest.mark.requiredtool('mksquashfs')
+def test_sqfs_load(u_boot_console):
+ build_dir = u_boot_console.config.build_dir
+ command = "sqfsload host 0 $kernel_addr_r "
+
+ for opt in comp_opts:
+ # generate and load the squashfs image
+ try:
+ opt.gen_image(build_dir)
+ except RuntimeError:
+ opt.clean_source(build_dir)
+ # skip unsupported compression types
+ continue
+
+ path = os.path.join(build_dir, "sqfs-" + opt.name)
+ output = u_boot_console.run_command("host bind 0 " + path)
+
+ output = u_boot_console.run_command(command + "xxx")
+ assert "File not found." in output
+
+ for (f, s) in zip(opt.files, opt.sizes):
+ try:
+ output = u_boot_console.run_command(command + f)
+ assert str(s) in output
+ except:
+ assert False
+ opt.cleanup(build_dir)
+
+ # test symbolic link
+ output = u_boot_console.run_command(command + "sym")
+ assert str(opt.sizes[0]) in output
+
+ # remove generated files
+ opt.cleanup(build_dir)
diff --git a/roms/u-boot/test/py/tests/test_fs/test_squashfs/test_sqfs_ls.py b/roms/u-boot/test/py/tests/test_fs/test_squashfs/test_sqfs_ls.py
new file mode 100644
index 000000000..a0dca2e2f
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_fs/test_squashfs/test_sqfs_ls.py
@@ -0,0 +1,36 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2020 Bootlin
+# Author: Joao Marcos Costa <joaomarcos.costa@bootlin.com>
+
+import os
+import pytest
+from sqfs_common import *
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('cmd_fs_generic')
+@pytest.mark.buildconfigspec('cmd_squashfs')
+@pytest.mark.buildconfigspec('fs_squashfs')
+@pytest.mark.requiredtool('mksquashfs')
+def test_sqfs_ls(u_boot_console):
+ build_dir = u_boot_console.config.build_dir
+ for opt in comp_opts:
+ try:
+ opt.gen_image(build_dir)
+ except RuntimeError:
+ opt.clean_source(build_dir)
+ # skip unsupported compression types
+ continue
+ path = os.path.join(build_dir, "sqfs-" + opt.name)
+ output = u_boot_console.run_command("host bind 0 " + path)
+
+ try:
+ # list files in root directory
+ output = u_boot_console.run_command("sqfsls host 0")
+ assert str(len(opt.files) + 1) + " file(s), 0 dir(s)" in output
+ assert "<SYM> sym" in output
+ output = u_boot_console.run_command("sqfsls host 0 xxx")
+ assert "** Cannot find directory. **" in output
+ except:
+ opt.cleanup(build_dir)
+ assert False
+ opt.cleanup(build_dir)
diff --git a/roms/u-boot/test/py/tests/test_fs/test_symlink.py b/roms/u-boot/test/py/tests/test_fs/test_symlink.py
new file mode 100644
index 000000000..9ced101a2
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_fs/test_symlink.py
@@ -0,0 +1,130 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2019, Texas Instrument
+# Author: Jean-Jacques Hiblot <jjhiblot@ti.com>
+#
+# U-Boot File System:symlink Test
+
+"""
+This test verifies unlink operation (deleting a file or a directory)
+on file system.
+"""
+
+import pytest
+import re
+from fstest_defs import *
+from fstest_helpers import assert_fs_integrity
+
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.slow
+class TestSymlink(object):
+ def test_symlink1(self, u_boot_console, fs_obj_symlink):
+ """
+ Test Case 1 - create a link. and follow it when reading
+ """
+ fs_type, fs_img, md5val = fs_obj_symlink
+ with u_boot_console.log.section('Test Case 1 - create link and read'):
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ 'setenv filesize',
+ 'ln host 0:0 %s /%s.link ' % (SMALL_FILE, SMALL_FILE),
+ ])
+ assert('' in ''.join(output))
+
+ output = u_boot_console.run_command_list([
+ '%sload host 0:0 %x /%s.link' % (fs_type, ADDR, SMALL_FILE),
+ 'printenv filesize'])
+ assert('filesize=100000' in ''.join(output))
+
+ # Test Case 4b - Read full 1MB of small file
+ output = u_boot_console.run_command_list([
+ 'md5sum %x $filesize' % ADDR,
+ 'setenv filesize'])
+ assert(md5val[0] in ''.join(output))
+ assert_fs_integrity(fs_type, fs_img)
+
+ def test_symlink2(self, u_boot_console, fs_obj_symlink):
+ """
+ Test Case 2 - create chained links
+ """
+ fs_type, fs_img, md5val = fs_obj_symlink
+ with u_boot_console.log.section('Test Case 2 - create chained links'):
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ 'setenv filesize',
+ 'ln host 0:0 %s /%s.link1 ' % (SMALL_FILE, SMALL_FILE),
+ 'ln host 0:0 /%s.link1 /SUBDIR/%s.link2' % (
+ SMALL_FILE, SMALL_FILE),
+ 'ln host 0:0 SUBDIR/%s.link2 /%s.link3' % (
+ SMALL_FILE, SMALL_FILE),
+ ])
+ assert('' in ''.join(output))
+
+ output = u_boot_console.run_command_list([
+ '%sload host 0:0 %x /%s.link3' % (fs_type, ADDR, SMALL_FILE),
+ 'printenv filesize'])
+ assert('filesize=100000' in ''.join(output))
+
+ # Test Case 4b - Read full 1MB of small file
+ output = u_boot_console.run_command_list([
+ 'md5sum %x $filesize' % ADDR,
+ 'setenv filesize'])
+ assert(md5val[0] in ''.join(output))
+ assert_fs_integrity(fs_type, fs_img)
+
+ def test_symlink3(self, u_boot_console, fs_obj_symlink):
+ """
+ Test Case 3 - replace file/link with link
+ """
+ fs_type, fs_img, md5val = fs_obj_symlink
+ with u_boot_console.log.section('Test Case 1 - create link and read'):
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ 'setenv filesize',
+ 'ln host 0:0 %s /%s ' % (MEDIUM_FILE, SMALL_FILE),
+ 'ln host 0:0 %s /%s.link ' % (MEDIUM_FILE, MEDIUM_FILE),
+ ])
+ assert('' in ''.join(output))
+
+ output = u_boot_console.run_command_list([
+ '%sload host 0:0 %x /%s' % (fs_type, ADDR, SMALL_FILE),
+ 'printenv filesize'])
+ assert('filesize=a00000' in ''.join(output))
+
+ output = u_boot_console.run_command_list([
+ 'md5sum %x $filesize' % ADDR,
+ 'setenv filesize'])
+ assert(md5val[1] in ''.join(output))
+
+ output = u_boot_console.run_command_list([
+ 'ln host 0:0 %s.link /%s ' % (MEDIUM_FILE, SMALL_FILE),
+ '%sload host 0:0 %x /%s' % (fs_type, ADDR, SMALL_FILE),
+ 'printenv filesize'])
+ assert('filesize=a00000' in ''.join(output))
+
+ output = u_boot_console.run_command_list([
+ 'md5sum %x $filesize' % ADDR,
+ 'setenv filesize'])
+ assert(md5val[1] in ''.join(output))
+ assert_fs_integrity(fs_type, fs_img)
+
+ def test_symlink4(self, u_boot_console, fs_obj_symlink):
+ """
+ Test Case 4 - create a broken link
+ """
+ fs_type, fs_img, md5val = fs_obj_symlink
+ with u_boot_console.log.section('Test Case 1 - create link and read'):
+
+ output = u_boot_console.run_command_list([
+ 'setenv filesize',
+ 'ln host 0:0 nowhere /link ',
+ ])
+ assert('' in ''.join(output))
+
+ output = u_boot_console.run_command(
+ '%sload host 0:0 %x /link' %
+ (fs_type, ADDR))
+ with u_boot_console.disable_check('error_notification'):
+ output = u_boot_console.run_command('printenv filesize')
+ assert('"filesize" not defined' in ''.join(output))
+ assert_fs_integrity(fs_type, fs_img)
diff --git a/roms/u-boot/test/py/tests/test_fs/test_unlink.py b/roms/u-boot/test/py/tests/test_fs/test_unlink.py
new file mode 100644
index 000000000..97aafc63b
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_fs/test_unlink.py
@@ -0,0 +1,118 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2018, Linaro Limited
+# Author: Takahiro Akashi <takahiro.akashi@linaro.org>
+#
+# U-Boot File System:unlink Test
+
+"""
+This test verifies unlink operation (deleting a file or a directory)
+on file system.
+"""
+
+import pytest
+from fstest_helpers import assert_fs_integrity
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.slow
+class TestUnlink(object):
+ def test_unlink1(self, u_boot_console, fs_obj_unlink):
+ """
+ Test Case 1 - delete a file
+ """
+ fs_type,fs_img = fs_obj_unlink
+ with u_boot_console.log.section('Test Case 1 - unlink (file)'):
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%srm host 0:0 dir1/file1' % fs_type,
+ '%sls host 0:0 dir1/file1' % fs_type])
+ assert('' == ''.join(output))
+
+ output = u_boot_console.run_command(
+ '%sls host 0:0 dir1/' % fs_type)
+ assert(not 'file1' in output)
+ assert('file2' in output)
+ assert_fs_integrity(fs_type, fs_img)
+
+ def test_unlink2(self, u_boot_console, fs_obj_unlink):
+ """
+ Test Case 2 - delete many files
+ """
+ fs_type,fs_img = fs_obj_unlink
+ with u_boot_console.log.section('Test Case 2 - unlink (many)'):
+ output = u_boot_console.run_command('host bind 0 %s' % fs_img)
+
+ for i in range(0, 20):
+ output = u_boot_console.run_command_list([
+ '%srm host 0:0 dir2/0123456789abcdef%02x' % (fs_type, i),
+ '%sls host 0:0 dir2/0123456789abcdef%02x' % (fs_type, i)])
+ assert('' == ''.join(output))
+
+ output = u_boot_console.run_command(
+ '%sls host 0:0 dir2' % fs_type)
+ assert('0 file(s), 2 dir(s)' in output)
+ assert_fs_integrity(fs_type, fs_img)
+
+ def test_unlink3(self, u_boot_console, fs_obj_unlink):
+ """
+ Test Case 3 - trying to delete a non-existing file should fail
+ """
+ fs_type,fs_img = fs_obj_unlink
+ with u_boot_console.log.section('Test Case 3 - unlink (non-existing)'):
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%srm host 0:0 dir1/nofile' % fs_type])
+ assert('nofile: doesn\'t exist' in ''.join(output))
+ assert_fs_integrity(fs_type, fs_img)
+
+ def test_unlink4(self, u_boot_console, fs_obj_unlink):
+ """
+ Test Case 4 - delete an empty directory
+ """
+ fs_type,fs_img = fs_obj_unlink
+ with u_boot_console.log.section('Test Case 4 - unlink (directory)'):
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%srm host 0:0 dir4' % fs_type])
+ assert('' == ''.join(output))
+
+ output = u_boot_console.run_command(
+ '%sls host 0:0 /' % fs_type)
+ assert(not 'dir4' in output)
+ assert_fs_integrity(fs_type, fs_img)
+
+ def test_unlink5(self, u_boot_console, fs_obj_unlink):
+ """
+ Test Case 5 - trying to deleting a non-empty directory ".."
+ should fail
+ """
+ fs_type,fs_img = fs_obj_unlink
+ with u_boot_console.log.section('Test Case 5 - unlink ("non-empty directory")'):
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%srm host 0:0 dir5' % fs_type])
+ assert('directory is not empty' in ''.join(output))
+ assert_fs_integrity(fs_type, fs_img)
+
+ def test_unlink6(self, u_boot_console, fs_obj_unlink):
+ """
+ Test Case 6 - trying to deleting a "." should fail
+ """
+ fs_type,fs_img = fs_obj_unlink
+ with u_boot_console.log.section('Test Case 6 - unlink (".")'):
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%srm host 0:0 dir5/.' % fs_type])
+ assert('directory is not empty' in ''.join(output))
+ assert_fs_integrity(fs_type, fs_img)
+
+ def test_unlink7(self, u_boot_console, fs_obj_unlink):
+ """
+ Test Case 7 - trying to deleting a ".." should fail
+ """
+ fs_type,fs_img = fs_obj_unlink
+ with u_boot_console.log.section('Test Case 7 - unlink ("..")'):
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%srm host 0:0 dir5/..' % fs_type])
+ assert('directory is not empty' in ''.join(output))
+ assert_fs_integrity(fs_type, fs_img)
diff --git a/roms/u-boot/test/py/tests/test_gpio.py b/roms/u-boot/test/py/tests/test_gpio.py
new file mode 100644
index 000000000..8c64f686b
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_gpio.py
@@ -0,0 +1,37 @@
+# SPDX-License-Identifier: GPL-2.0+
+
+import pytest
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('cmd_gpio')
+def test_gpio_input(u_boot_console):
+ """Test that gpio input correctly returns the value of a gpio pin."""
+
+ response = u_boot_console.run_command('gpio input 0; echo rc:$?')
+ expected_response = 'rc:0'
+ assert(expected_response in response)
+ response = u_boot_console.run_command('gpio toggle 0; gpio input 0; echo rc:$?')
+ expected_response = 'rc:1'
+ assert(expected_response in response)
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('cmd_gpio')
+def test_gpio_exit_statuses(u_boot_console):
+ """Test that non-input gpio commands correctly return the command
+ success/failure status."""
+
+ expected_response = 'rc:0'
+ response = u_boot_console.run_command('gpio clear 0; echo rc:$?')
+ assert(expected_response in response)
+ response = u_boot_console.run_command('gpio set 0; echo rc:$?')
+ assert(expected_response in response)
+ response = u_boot_console.run_command('gpio toggle 0; echo rc:$?')
+ assert(expected_response in response)
+ response = u_boot_console.run_command('gpio status -a; echo rc:$?')
+ assert(expected_response in response)
+
+ expected_response = 'rc:1'
+ response = u_boot_console.run_command('gpio nonexistent-command; echo rc:$?')
+ assert(expected_response in response)
+ response = u_boot_console.run_command('gpio input 200; echo rc:$?')
+ assert(expected_response in response)
diff --git a/roms/u-boot/test/py/tests/test_gpt.py b/roms/u-boot/test/py/tests/test_gpt.py
new file mode 100644
index 000000000..229d7eb2c
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_gpt.py
@@ -0,0 +1,178 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2017 Alison Chaiken
+# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved.
+
+# Test GPT manipulation commands.
+
+import os
+import pytest
+import u_boot_utils
+
+"""
+These tests rely on a 4 MB disk image, which is automatically created by
+the test.
+"""
+
+class GptTestDiskImage(object):
+ """Disk Image used by the GPT tests."""
+
+ def __init__(self, u_boot_console):
+ """Initialize a new GptTestDiskImage object.
+
+ Args:
+ u_boot_console: A U-Boot console.
+
+ Returns:
+ Nothing.
+ """
+
+ filename = 'test_gpt_disk_image.bin'
+
+ persistent = u_boot_console.config.persistent_data_dir + '/' + filename
+ self.path = u_boot_console.config.result_dir + '/' + filename
+
+ with u_boot_utils.persistent_file_helper(u_boot_console.log, persistent):
+ if os.path.exists(persistent):
+ u_boot_console.log.action('Disk image file ' + persistent +
+ ' already exists')
+ else:
+ u_boot_console.log.action('Generating ' + persistent)
+ fd = os.open(persistent, os.O_RDWR | os.O_CREAT)
+ os.ftruncate(fd, 4194304)
+ os.close(fd)
+ cmd = ('sgdisk',
+ '--disk-guid=375a56f7-d6c9-4e81-b5f0-09d41ca89efe',
+ persistent)
+ u_boot_utils.run_and_log(u_boot_console, cmd)
+ # part1 offset 1MB size 1MB
+ cmd = ('sgdisk', '--new=1:2048:4095', '--change-name=1:part1',
+ persistent)
+ # part2 offset 2MB size 1.5MB
+ u_boot_utils.run_and_log(u_boot_console, cmd)
+ cmd = ('sgdisk', '--new=2:4096:7167', '--change-name=2:part2',
+ persistent)
+ u_boot_utils.run_and_log(u_boot_console, cmd)
+ cmd = ('sgdisk', '--load-backup=' + persistent)
+ u_boot_utils.run_and_log(u_boot_console, cmd)
+
+ cmd = ('cp', persistent, self.path)
+ u_boot_utils.run_and_log(u_boot_console, cmd)
+
+gtdi = None
+@pytest.fixture(scope='function')
+def state_disk_image(u_boot_console):
+ """pytest fixture to provide a GptTestDiskImage object to tests.
+ This is function-scoped because it uses u_boot_console, which is also
+ function-scoped. However, we don't need to actually do any function-scope
+ work, so this simply returns the same object over and over each time."""
+
+ global gtdi
+ if not gtdi:
+ gtdi = GptTestDiskImage(u_boot_console)
+ return gtdi
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('cmd_gpt')
+@pytest.mark.buildconfigspec('cmd_part')
+@pytest.mark.requiredtool('sgdisk')
+def test_gpt_read(state_disk_image, u_boot_console):
+ """Test the gpt read command."""
+
+ u_boot_console.run_command('host bind 0 ' + state_disk_image.path)
+ output = u_boot_console.run_command('gpt read host 0')
+ assert 'Start 1MiB, size 1MiB' in output
+ assert 'Block size 512, name part1' in output
+ assert 'Start 2MiB, size 1MiB' in output
+ assert 'Block size 512, name part2' in output
+ output = u_boot_console.run_command('part list host 0')
+ assert '0x00000800 0x00000fff "part1"' in output
+ assert '0x00001000 0x00001bff "part2"' in output
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('cmd_gpt')
+@pytest.mark.requiredtool('sgdisk')
+def test_gpt_verify(state_disk_image, u_boot_console):
+ """Test the gpt verify command."""
+
+ u_boot_console.run_command('host bind 0 ' + state_disk_image.path)
+ output = u_boot_console.run_command('gpt verify host 0')
+ assert 'Verify GPT: success!' in output
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('cmd_gpt')
+@pytest.mark.requiredtool('sgdisk')
+def test_gpt_guid(state_disk_image, u_boot_console):
+ """Test the gpt guid command."""
+
+ u_boot_console.run_command('host bind 0 ' + state_disk_image.path)
+ output = u_boot_console.run_command('gpt guid host 0')
+ assert '375a56f7-d6c9-4e81-b5f0-09d41ca89efe' in output
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('cmd_gpt')
+@pytest.mark.requiredtool('sgdisk')
+def test_gpt_save_guid(state_disk_image, u_boot_console):
+ """Test the gpt guid command to save GUID into a string."""
+
+ if u_boot_console.config.buildconfig.get('config_cmd_gpt', 'n') != 'y':
+ pytest.skip('gpt command not supported')
+ u_boot_console.run_command('host bind 0 ' + state_disk_image.path)
+ output = u_boot_console.run_command('gpt guid host 0 newguid')
+ output = u_boot_console.run_command('printenv newguid')
+ assert '375a56f7-d6c9-4e81-b5f0-09d41ca89efe' in output
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('cmd_gpt')
+@pytest.mark.buildconfigspec('cmd_gpt_rename')
+@pytest.mark.buildconfigspec('cmd_part')
+@pytest.mark.requiredtool('sgdisk')
+def test_gpt_rename_partition(state_disk_image, u_boot_console):
+ """Test the gpt rename command to write partition names."""
+
+ u_boot_console.run_command('host bind 0 ' + state_disk_image.path)
+ u_boot_console.run_command('gpt rename host 0 1 first')
+ output = u_boot_console.run_command('gpt read host 0')
+ assert 'name first' in output
+ u_boot_console.run_command('gpt rename host 0 2 second')
+ output = u_boot_console.run_command('gpt read host 0')
+ assert 'name second' in output
+ output = u_boot_console.run_command('part list host 0')
+ assert '0x00000800 0x00000fff "first"' in output
+ assert '0x00001000 0x00001bff "second"' in output
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('cmd_gpt')
+@pytest.mark.buildconfigspec('cmd_gpt_rename')
+@pytest.mark.buildconfigspec('cmd_part')
+@pytest.mark.requiredtool('sgdisk')
+def test_gpt_swap_partitions(state_disk_image, u_boot_console):
+ """Test the gpt swap command to exchange two partition names."""
+
+ u_boot_console.run_command('host bind 0 ' + state_disk_image.path)
+ output = u_boot_console.run_command('part list host 0')
+ assert '0x00000800 0x00000fff "first"' in output
+ assert '0x00001000 0x00001bff "second"' in output
+ u_boot_console.run_command('gpt swap host 0 first second')
+ output = u_boot_console.run_command('part list host 0')
+ assert '0x00000800 0x00000fff "second"' in output
+ assert '0x00001000 0x00001bff "first"' in output
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('cmd_gpt')
+@pytest.mark.buildconfigspec('cmd_part')
+@pytest.mark.requiredtool('sgdisk')
+def test_gpt_write(state_disk_image, u_boot_console):
+ """Test the gpt write command."""
+
+ u_boot_console.run_command('host bind 0 ' + state_disk_image.path)
+ output = u_boot_console.run_command('gpt write host 0 "name=all,size=0"')
+ assert 'Writing GPT: success!' in output
+ output = u_boot_console.run_command('part list host 0')
+ assert '0x00000022 0x00001fde "all"' in output
+ output = u_boot_console.run_command('gpt write host 0 "uuid_disk=375a56f7-d6c9-4e81-b5f0-09d41ca89efe;name=first,start=1M,size=1M;name=second,start=0x200000,size=0x180000;"')
+ assert 'Writing GPT: success!' in output
+ output = u_boot_console.run_command('part list host 0')
+ assert '0x00000800 0x00000fff "first"' in output
+ assert '0x00001000 0x00001bff "second"' in output
+ output = u_boot_console.run_command('gpt guid host 0')
+ assert '375a56f7-d6c9-4e81-b5f0-09d41ca89efe' in output
diff --git a/roms/u-boot/test/py/tests/test_handoff.py b/roms/u-boot/test/py/tests/test_handoff.py
new file mode 100644
index 000000000..038f03064
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_handoff.py
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2016 Google, Inc
+
+import pytest
+
+# Magic number to check that SPL handoff is working
+TEST_HANDOFF_MAGIC = 0x14f93c7b
+
+@pytest.mark.boardspec('sandbox_spl')
+@pytest.mark.buildconfigspec('spl')
+def test_handoff(u_boot_console):
+ """Test that of-platdata can be generated and used in sandbox"""
+ cons = u_boot_console
+ response = cons.run_command('sb handoff')
+ assert ('SPL handoff magic %x' % TEST_HANDOFF_MAGIC) in response
diff --git a/roms/u-boot/test/py/tests/test_help.py b/roms/u-boot/test/py/tests/test_help.py
new file mode 100644
index 000000000..d50295e5b
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_help.py
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2015 Stephen Warren
+# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
+
+def test_help(u_boot_console):
+ """Test that the "help" command can be executed."""
+
+ u_boot_console.run_command('help')
diff --git a/roms/u-boot/test/py/tests/test_hush_if_test.py b/roms/u-boot/test/py/tests/test_hush_if_test.py
new file mode 100644
index 000000000..d117921a6
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_hush_if_test.py
@@ -0,0 +1,192 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved.
+
+# Test operation of the "if" shell command.
+
+import os
+import os.path
+import pytest
+
+# TODO: These tests should be converted to a C test.
+# For more information please take a look at the thread
+# https://lists.denx.de/pipermail/u-boot/2019-October/388732.html
+
+pytestmark = pytest.mark.buildconfigspec('hush_parser')
+
+# The list of "if test" conditions to test.
+subtests = (
+ # Base if functionality.
+
+ ('true', True),
+ ('false', False),
+
+ # Basic operators.
+
+ ('test aaa = aaa', True),
+ ('test aaa = bbb', False),
+
+ ('test aaa != bbb', True),
+ ('test aaa != aaa', False),
+
+ ('test aaa < bbb', True),
+ ('test bbb < aaa', False),
+
+ ('test bbb > aaa', True),
+ ('test aaa > bbb', False),
+
+ ('test 123 -eq 123', True),
+ ('test 123 -eq 456', False),
+
+ ('test 123 -ne 456', True),
+ ('test 123 -ne 123', False),
+
+ ('test 123 -lt 456', True),
+ ('test 123 -lt 123', False),
+ ('test 456 -lt 123', False),
+
+ ('test 123 -le 456', True),
+ ('test 123 -le 123', True),
+ ('test 456 -le 123', False),
+
+ ('test 456 -gt 123', True),
+ ('test 123 -gt 123', False),
+ ('test 123 -gt 456', False),
+
+ ('test 456 -ge 123', True),
+ ('test 123 -ge 123', True),
+ ('test 123 -ge 456', False),
+
+ # Octal tests
+
+ ('test 010 -eq 010', True),
+ ('test 010 -eq 011', False),
+
+ ('test 010 -ne 011', True),
+ ('test 010 -ne 010', False),
+
+ # Hexadecimal tests
+
+ ('test 0x2000000 -gt 0x2000001', False),
+ ('test 0x2000000 -gt 0x2000000', False),
+ ('test 0x2000000 -gt 0x1ffffff', True),
+
+ # Mixed tests
+
+ ('test 010 -eq 10', False),
+ ('test 010 -ne 10', True),
+ ('test 0xa -eq 10', True),
+ ('test 0xa -eq 012', True),
+
+ ('test 2000000 -gt 0x1ffffff', False),
+ ('test 0x2000000 -gt 1ffffff', True),
+ ('test 0x2000000 -lt 1ffffff', False),
+ ('test 0x2000000 -eq 2000000', False),
+ ('test 0x2000000 -ne 2000000', True),
+
+ ('test -z ""', True),
+ ('test -z "aaa"', False),
+
+ ('test -n "aaa"', True),
+ ('test -n ""', False),
+
+ # Inversion of simple tests.
+
+ ('test ! aaa = aaa', False),
+ ('test ! aaa = bbb', True),
+ ('test ! ! aaa = aaa', True),
+ ('test ! ! aaa = bbb', False),
+
+ # Binary operators.
+
+ ('test aaa != aaa -o bbb != bbb', False),
+ ('test aaa != aaa -o bbb = bbb', True),
+ ('test aaa = aaa -o bbb != bbb', True),
+ ('test aaa = aaa -o bbb = bbb', True),
+
+ ('test aaa != aaa -a bbb != bbb', False),
+ ('test aaa != aaa -a bbb = bbb', False),
+ ('test aaa = aaa -a bbb != bbb', False),
+ ('test aaa = aaa -a bbb = bbb', True),
+
+ # Inversion within binary operators.
+
+ ('test ! aaa != aaa -o ! bbb != bbb', True),
+ ('test ! aaa != aaa -o ! bbb = bbb', True),
+ ('test ! aaa = aaa -o ! bbb != bbb', True),
+ ('test ! aaa = aaa -o ! bbb = bbb', False),
+
+ ('test ! ! aaa != aaa -o ! ! bbb != bbb', False),
+ ('test ! ! aaa != aaa -o ! ! bbb = bbb', True),
+ ('test ! ! aaa = aaa -o ! ! bbb != bbb', True),
+ ('test ! ! aaa = aaa -o ! ! bbb = bbb', True),
+
+ # -z operator.
+
+ ('test -z "$ut_var_nonexistent"', True),
+ ('test -z "$ut_var_exists"', False),
+)
+
+def exec_hush_if(u_boot_console, expr, result):
+ """Execute a shell "if" command, and validate its result."""
+
+ config = u_boot_console.config.buildconfig
+ maxargs = int(config.get('config_sys_maxargs', '0'))
+ args = len(expr.split(' ')) - 1
+ if args > maxargs:
+ u_boot_console.log.warning('CONFIG_SYS_MAXARGS too low; need ' +
+ str(args))
+ pytest.skip()
+
+ cmd = 'if ' + expr + '; then echo true; else echo false; fi'
+ response = u_boot_console.run_command(cmd)
+ assert response.strip() == str(result).lower()
+
+def test_hush_if_test_setup(u_boot_console):
+ """Set up environment variables used during the "if" tests."""
+
+ u_boot_console.run_command('setenv ut_var_nonexistent')
+ u_boot_console.run_command('setenv ut_var_exists 1')
+
+@pytest.mark.buildconfigspec('cmd_echo')
+@pytest.mark.parametrize('expr,result', subtests)
+def test_hush_if_test(u_boot_console, expr, result):
+ """Test a single "if test" condition."""
+
+ exec_hush_if(u_boot_console, expr, result)
+
+def test_hush_if_test_teardown(u_boot_console):
+ """Clean up environment variables used during the "if" tests."""
+
+ u_boot_console.run_command('setenv ut_var_exists')
+
+# We might test this on real filesystems via UMS, DFU, 'save', etc.
+# Of those, only UMS currently allows file removal though.
+@pytest.mark.buildconfigspec('cmd_echo')
+@pytest.mark.boardspec('sandbox')
+def test_hush_if_test_host_file_exists(u_boot_console):
+ """Test the "if test -e" shell command."""
+
+ test_file = u_boot_console.config.result_dir + \
+ '/creating_this_file_breaks_u_boot_tests'
+
+ try:
+ os.unlink(test_file)
+ except:
+ pass
+ assert not os.path.exists(test_file)
+
+ expr = 'test -e hostfs - ' + test_file
+ exec_hush_if(u_boot_console, expr, False)
+
+ try:
+ with open(test_file, 'wb'):
+ pass
+ assert os.path.exists(test_file)
+
+ expr = 'test -e hostfs - ' + test_file
+ exec_hush_if(u_boot_console, expr, True)
+ finally:
+ os.unlink(test_file)
+
+ expr = 'test -e hostfs - ' + test_file
+ exec_hush_if(u_boot_console, expr, False)
diff --git a/roms/u-boot/test/py/tests/test_log.py b/roms/u-boot/test/py/tests/test_log.py
new file mode 100644
index 000000000..140dcb9aa
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_log.py
@@ -0,0 +1,48 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2016, Google Inc.
+#
+# U-Boot Verified Boot Test
+
+"""
+This tests U-Boot logging. It uses the 'log test' command with various options
+and checks that the output is correct.
+"""
+
+import pytest
+
+@pytest.mark.buildconfigspec('cmd_log')
+def test_log_format(u_boot_console):
+ """Test the 'log format' and 'log rec' commands"""
+ def run_with_format(fmt, expected_output):
+ """Set up the log format and then write a log record
+
+ Args:
+ fmt: Format to use for 'log format'
+ expected_output: Expected output from the 'log rec' command
+ """
+ output = cons.run_command('log format %s' % fmt)
+ assert output == ''
+ output = cons.run_command('log rec arch notice file.c 123 func msg')
+ assert output == expected_output
+
+ cons = u_boot_console
+ with cons.log.section('format'):
+ run_with_format('all', 'NOTICE.arch,file.c:123-func() msg')
+ output = cons.run_command('log format')
+ assert output == 'Log format: clFLfm'
+
+ run_with_format('fm', 'func() msg')
+ run_with_format('clfm', 'NOTICE.arch,func() msg')
+ run_with_format('FLfm', 'file.c:123-func() msg')
+ run_with_format('lm', 'NOTICE. msg')
+ run_with_format('m', 'msg')
+
+@pytest.mark.buildconfigspec('debug_uart')
+@pytest.mark.boardspec('sandbox')
+def test_log_dropped(u_boot_console):
+ """Test dropped 'log' message when debug_uart is activated"""
+
+ cons = u_boot_console
+ cons.restart_uboot()
+ output = cons.get_spawn_output().replace('\r', '')
+ assert (not 'debug: main' in output)
diff --git a/roms/u-boot/test/py/tests/test_lsblk.py b/roms/u-boot/test/py/tests/test_lsblk.py
new file mode 100644
index 000000000..40ffe0126
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_lsblk.py
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (C) 2020
+# Niel Fourie, DENX Software Engineering, lusus@denx.de
+
+import pytest
+
+@pytest.mark.buildconfigspec('blk')
+@pytest.mark.buildconfigspec('cmd_lsblk')
+def test_lsblk(u_boot_console):
+ """Test that `lsblk` prints a result which includes `host`."""
+ output = u_boot_console.run_command('lsblk')
+ assert "Block Driver" in output
+ assert "sandbox_host_blk" in output
diff --git a/roms/u-boot/test/py/tests/test_md.py b/roms/u-boot/test/py/tests/test_md.py
new file mode 100644
index 000000000..83e3c546f
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_md.py
@@ -0,0 +1,36 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2015 Stephen Warren
+# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved.
+
+import pytest
+import u_boot_utils
+
+@pytest.mark.buildconfigspec('cmd_memory')
+def test_md(u_boot_console):
+ """Test that md reads memory as expected, and that memory can be modified
+ using the mw command."""
+
+ ram_base = u_boot_utils.find_ram_base(u_boot_console)
+ addr = '%08x' % ram_base
+ val = 'a5f09876'
+ expected_response = addr + ': ' + val
+ u_boot_console.run_command('mw ' + addr + ' 0 10')
+ response = u_boot_console.run_command('md ' + addr + ' 10')
+ assert(not (expected_response in response))
+ u_boot_console.run_command('mw ' + addr + ' ' + val)
+ response = u_boot_console.run_command('md ' + addr + ' 10')
+ assert(expected_response in response)
+
+@pytest.mark.buildconfigspec('cmd_memory')
+def test_md_repeat(u_boot_console):
+ """Test command repeat (via executing an empty command) operates correctly
+ for "md"; the command must repeat and dump an incrementing address."""
+
+ ram_base = u_boot_utils.find_ram_base(u_boot_console)
+ addr_base = '%08x' % ram_base
+ words = 0x10
+ addr_repeat = '%08x' % (ram_base + (words * 4))
+ u_boot_console.run_command('md %s %x' % (addr_base, words))
+ response = u_boot_console.run_command('')
+ expected_response = addr_repeat + ': '
+ assert(expected_response in response)
diff --git a/roms/u-boot/test/py/tests/test_mmc_rd.py b/roms/u-boot/test/py/tests/test_mmc_rd.py
new file mode 100644
index 000000000..ea652f913
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_mmc_rd.py
@@ -0,0 +1,286 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved.
+
+# Test U-Boot's "mmc read" command. The test reads data from the eMMC or SD
+# card, and validates the no errors occurred, and that the expected data was
+# read if the test configuration contains a CRC of the expected data.
+
+import pytest
+import time
+import u_boot_utils
+
+"""
+This test relies on boardenv_* to containing configuration values to define
+which MMC devices should be tested. For example:
+
+# Configuration data for test_mmc_dev, test_mmc_rescan, test_mmc_info; defines
+# whole MMC devices that mmc dev/rescan/info commands may operate upon.
+env__mmc_dev_configs = (
+ {
+ 'fixture_id': 'emmc-boot0',
+ 'is_emmc': True,
+ 'devid': 0,
+ 'partid': 1,
+ 'info_device': ???,
+ 'info_speed': ???,
+ 'info_mode': ???,
+ 'info_buswidth': ???.
+ },
+ {
+ 'fixture_id': 'emmc-boot1',
+ 'is_emmc': True,
+ 'devid': 0,
+ 'partid': 2,
+ 'info_device': ???,
+ 'info_speed': ???,
+ 'info_mode': ???,
+ 'info_buswidth': ???.
+ },
+ {
+ 'fixture_id': 'emmc-data',
+ 'is_emmc': True,
+ 'devid': 0,
+ 'partid': 0,
+ 'info_device': ???,
+ 'info_speed': ???,
+ 'info_mode': ???,
+ 'info_buswidth': ???.
+ },
+ {
+ 'fixture_id': 'sd',
+ 'is_emmc': False,
+ 'devid': 1,
+ 'partid': None,
+ 'info_device': ???,
+ 'info_speed': ???,
+ 'info_mode': ???,
+ 'info_buswidth': ???.
+ },
+)
+
+# Configuration data for test_mmc_rd; defines regions of the MMC (entire
+# devices, or ranges of sectors) which can be read:
+env__mmc_rd_configs = (
+ {
+ 'fixture_id': 'emmc-boot0',
+ 'is_emmc': True,
+ 'devid': 0,
+ 'partid': 1,
+ 'sector': 0x10,
+ 'count': 1,
+ },
+ {
+ 'fixture_id': 'emmc-boot1',
+ 'is_emmc': True,
+ 'devid': 0,
+ 'partid': 2,
+ 'sector': 0x10,
+ 'count': 1,
+ },
+ {
+ 'fixture_id': 'emmc-data',
+ 'is_emmc': True,
+ 'devid': 0,
+ 'partid': 0,
+ 'sector': 0x10,
+ 'count': 0x1000,
+ },
+ {
+ 'fixture_id': 'sd-mbr',
+ 'is_emmc': False,
+ 'devid': 1,
+ 'partid': None,
+ 'sector': 0,
+ 'count': 1,
+ 'crc32': '8f6ecf0d',
+ },
+ {
+ 'fixture_id': 'sd-large',
+ 'is_emmc': False,
+ 'devid': 1,
+ 'partid': None,
+ 'sector': 0x10,
+ 'count': 0x1000,
+ },
+)
+"""
+
+def mmc_dev(u_boot_console, is_emmc, devid, partid):
+ """Run the "mmc dev" command.
+
+ Args:
+ u_boot_console: A U-Boot console connection.
+ is_emmc: Whether the device is eMMC
+ devid: Device ID
+ partid: Partition ID
+
+ Returns:
+ Nothing.
+ """
+
+ # Select MMC device
+ cmd = 'mmc dev %d' % devid
+ if is_emmc:
+ cmd += ' %d' % partid
+ response = u_boot_console.run_command(cmd)
+ assert 'no card present' not in response
+ if is_emmc:
+ partid_response = '(part %d)' % partid
+ else:
+ partid_response = ''
+ good_response = 'mmc%d%s is current device' % (devid, partid_response)
+ assert good_response in response
+
+@pytest.mark.buildconfigspec('cmd_mmc')
+def test_mmc_dev(u_boot_console, env__mmc_dev_config):
+ """Test the "mmc dev" command.
+
+ Args:
+ u_boot_console: A U-Boot console connection.
+ env__mmc_dev_config: The single MMC configuration on which
+ to run the test. See the file-level comment above for details
+ of the format.
+
+ Returns:
+ Nothing.
+ """
+
+ is_emmc = env__mmc_dev_config['is_emmc']
+ devid = env__mmc_dev_config['devid']
+ partid = env__mmc_dev_config.get('partid', 0)
+
+ # Select MMC device
+ mmc_dev(u_boot_console, is_emmc, devid, partid)
+
+@pytest.mark.buildconfigspec('cmd_mmc')
+def test_mmc_rescan(u_boot_console, env__mmc_dev_config):
+ """Test the "mmc rescan" command.
+
+ Args:
+ u_boot_console: A U-Boot console connection.
+ env__mmc_dev_config: The single MMC configuration on which
+ to run the test. See the file-level comment above for details
+ of the format.
+
+ Returns:
+ Nothing.
+ """
+
+ is_emmc = env__mmc_dev_config['is_emmc']
+ devid = env__mmc_dev_config['devid']
+ partid = env__mmc_dev_config.get('partid', 0)
+
+ # Select MMC device
+ mmc_dev(u_boot_console, is_emmc, devid, partid)
+
+ # Rescan MMC device
+ cmd = 'mmc rescan'
+ response = u_boot_console.run_command(cmd)
+ assert 'no card present' not in response
+
+@pytest.mark.buildconfigspec('cmd_mmc')
+def test_mmc_info(u_boot_console, env__mmc_dev_config):
+ """Test the "mmc info" command.
+
+ Args:
+ u_boot_console: A U-Boot console connection.
+ env__mmc_dev_config: The single MMC configuration on which
+ to run the test. See the file-level comment above for details
+ of the format.
+
+ Returns:
+ Nothing.
+ """
+
+ is_emmc = env__mmc_dev_config['is_emmc']
+ devid = env__mmc_dev_config['devid']
+ partid = env__mmc_dev_config.get('partid', 0)
+ info_device = env__mmc_dev_config['info_device']
+ info_speed = env__mmc_dev_config['info_speed']
+ info_mode = env__mmc_dev_config['info_mode']
+ info_buswidth = env__mmc_dev_config['info_buswidth']
+
+ # Select MMC device
+ mmc_dev(u_boot_console, is_emmc, devid, partid)
+
+ # Read MMC device information
+ cmd = 'mmc info'
+ response = u_boot_console.run_command(cmd)
+ good_response = "Device: %s" % info_device
+ assert good_response in response
+ good_response = "Bus Speed: %s" % info_speed
+ assert good_response in response
+ good_response = "Mode: %s" % info_mode
+ assert good_response in response
+ good_response = "Bus Width: %s" % info_buswidth
+ assert good_response in response
+
+@pytest.mark.buildconfigspec('cmd_mmc')
+def test_mmc_rd(u_boot_console, env__mmc_rd_config):
+ """Test the "mmc read" command.
+
+ Args:
+ u_boot_console: A U-Boot console connection.
+ env__mmc_rd_config: The single MMC configuration on which
+ to run the test. See the file-level comment above for details
+ of the format.
+
+ Returns:
+ Nothing.
+ """
+
+ is_emmc = env__mmc_rd_config['is_emmc']
+ devid = env__mmc_rd_config['devid']
+ partid = env__mmc_rd_config.get('partid', 0)
+ sector = env__mmc_rd_config.get('sector', 0)
+ count_sectors = env__mmc_rd_config.get('count', 1)
+ expected_crc32 = env__mmc_rd_config.get('crc32', None)
+ read_duration_max = env__mmc_rd_config.get('read_duration_max', 0)
+
+ count_bytes = count_sectors * 512
+ bcfg = u_boot_console.config.buildconfig
+ has_cmd_memory = bcfg.get('config_cmd_memory', 'n') == 'y'
+ has_cmd_crc32 = bcfg.get('config_cmd_crc32', 'n') == 'y'
+ ram_base = u_boot_utils.find_ram_base(u_boot_console)
+ addr = '0x%08x' % ram_base
+
+ # Select MMC device
+ mmc_dev(u_boot_console, is_emmc, devid, partid)
+
+ # Clear target RAM
+ if expected_crc32:
+ if has_cmd_memory and has_cmd_crc32:
+ cmd = 'mw.b %s 0 0x%x' % (addr, count_bytes)
+ u_boot_console.run_command(cmd)
+
+ cmd = 'crc32 %s 0x%x' % (addr, count_bytes)
+ response = u_boot_console.run_command(cmd)
+ assert expected_crc32 not in response
+ else:
+ u_boot_console.log.warning(
+ 'CONFIG_CMD_MEMORY or CONFIG_CMD_CRC32 != y: Skipping RAM clear')
+
+ # Read data
+ cmd = 'mmc read %s %x %x' % (addr, sector, count_sectors)
+ tstart = time.time()
+ response = u_boot_console.run_command(cmd)
+ tend = time.time()
+ good_response = 'MMC read: dev # %d, block # %d, count %d ... %d blocks read: OK' % (
+ devid, sector, count_sectors, count_sectors)
+ assert good_response in response
+
+ # Check target RAM
+ if expected_crc32:
+ if has_cmd_crc32:
+ cmd = 'crc32 %s 0x%x' % (addr, count_bytes)
+ response = u_boot_console.run_command(cmd)
+ assert expected_crc32 in response
+ else:
+ u_boot_console.log.warning('CONFIG_CMD_CRC32 != y: Skipping check')
+
+ # Check if the command did not take too long
+ if read_duration_max:
+ elapsed = tend - tstart
+ u_boot_console.log.info('Reading %d bytes took %f seconds' %
+ (count_bytes, elapsed))
+ assert elapsed <= (read_duration_max - 0.01)
diff --git a/roms/u-boot/test/py/tests/test_mmc_wr.py b/roms/u-boot/test/py/tests/test_mmc_wr.py
new file mode 100644
index 000000000..05e5c1ee8
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_mmc_wr.py
@@ -0,0 +1,105 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2019, Texas Instrument
+# Author: Jean-Jacques Hiblot <jjhiblot@ti.com>
+
+# Test U-Boot's "mmc write" command. The test generates random data, writes it
+# to the eMMC or SD card, then reads it back and performs a comparison.
+
+import pytest
+import u_boot_utils
+
+"""
+This test relies on boardenv_* to containing configuration values to define
+which MMC devices should be tested. For example:
+
+env__mmc_wr_configs = (
+ {
+ "fixture_id": "emmc-boot0",
+ "is_emmc": True,
+ "devid": 1,
+ "partid": 1,
+ "sector": 0x10,
+ "count": 100,
+ "test_iterations": 50,
+ },
+ {
+ "fixture_id": "emmc-boot1",
+ "is_emmc": True,
+ "devid": 1,
+ "partid": 2,
+ "sector": 0x10,
+ "count": 100,
+ "test_iterations": 50,
+ },
+)
+
+"""
+
+@pytest.mark.buildconfigspec('cmd_mmc')
+@pytest.mark.buildconfigspec('cmd_memory')
+@pytest.mark.buildconfigspec('cmd_random')
+def test_mmc_wr(u_boot_console, env__mmc_wr_config):
+ """Test the "mmc write" command.
+
+ Args:
+ u_boot_console: A U-Boot console connection.
+ env__mmc_wr_config: The single MMC configuration on which
+ to run the test. See the file-level comment above for details
+ of the format.
+
+ Returns:
+ Nothing.
+ """
+
+ is_emmc = env__mmc_wr_config['is_emmc']
+ devid = env__mmc_wr_config['devid']
+ partid = env__mmc_wr_config.get('partid', 0)
+ sector = env__mmc_wr_config.get('sector', 0)
+ count_sectors = env__mmc_wr_config.get('count', 1)
+ test_iterations = env__mmc_wr_config.get('test_iterations', 1)
+
+
+ count_bytes = count_sectors * 512
+ bcfg = u_boot_console.config.buildconfig
+ ram_base = u_boot_utils.find_ram_base(u_boot_console)
+ src_addr = '0x%08x' % ram_base
+ dst_addr = '0x%08x' % (ram_base + count_bytes)
+
+
+ for i in range(test_iterations):
+ # Generate random data
+ cmd = 'random %s %x' % (src_addr, count_bytes)
+ response = u_boot_console.run_command(cmd)
+ good_response = '%d bytes filled with random data' % (count_bytes)
+ assert good_response in response
+
+ # Select MMC device
+ cmd = 'mmc dev %d' % devid
+ if is_emmc:
+ cmd += ' %d' % partid
+ response = u_boot_console.run_command(cmd)
+ assert 'no card present' not in response
+ if is_emmc:
+ partid_response = "(part %d)" % partid
+ else:
+ partid_response = ""
+ good_response = 'mmc%d%s is current device' % (devid, partid_response)
+ assert good_response in response
+
+ # Write data
+ cmd = 'mmc write %s %x %x' % (src_addr, sector, count_sectors)
+ response = u_boot_console.run_command(cmd)
+ good_response = 'MMC write: dev # %d, block # %d, count %d ... %d blocks written: OK' % (devid, sector, count_sectors, count_sectors)
+ assert good_response in response
+
+ # Read data
+ cmd = 'mmc read %s %x %x' % (dst_addr, sector, count_sectors)
+ response = u_boot_console.run_command(cmd)
+ good_response = 'MMC read: dev # %d, block # %d, count %d ... %d blocks read: OK' % (devid, sector, count_sectors, count_sectors)
+ assert good_response in response
+
+ # Compare src and dst data
+ cmd = 'cmp.b %s %s %x' % (src_addr, dst_addr, count_bytes)
+ response = u_boot_console.run_command(cmd)
+ good_response = 'Total of %d byte(s) were the same' % (count_bytes)
+ assert good_response in response
diff --git a/roms/u-boot/test/py/tests/test_net.py b/roms/u-boot/test/py/tests/test_net.py
new file mode 100644
index 000000000..9ca6743af
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_net.py
@@ -0,0 +1,208 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
+
+# Test various network-related functionality, such as the dhcp, ping, and
+# tftpboot commands.
+
+import pytest
+import u_boot_utils
+
+"""
+Note: This test relies on boardenv_* containing configuration values to define
+which the network environment available for testing. Without this, this test
+will be automatically skipped.
+
+For example:
+
+# Boolean indicating whether the Ethernet device is attached to USB, and hence
+# USB enumeration needs to be performed prior to network tests.
+# This variable may be omitted if its value is False.
+env__net_uses_usb = False
+
+# Boolean indicating whether the Ethernet device is attached to PCI, and hence
+# PCI enumeration needs to be performed prior to network tests.
+# This variable may be omitted if its value is False.
+env__net_uses_pci = True
+
+# True if a DHCP server is attached to the network, and should be tested.
+# If DHCP testing is not possible or desired, this variable may be omitted or
+# set to False.
+env__net_dhcp_server = True
+
+# A list of environment variables that should be set in order to configure a
+# static IP. If solely relying on DHCP, this variable may be omitted or set to
+# an empty list.
+env__net_static_env_vars = [
+ ('ipaddr', '10.0.0.100'),
+ ('netmask', '255.255.255.0'),
+ ('serverip', '10.0.0.1'),
+]
+
+# Details regarding a file that may be read from a TFTP server. This variable
+# may be omitted or set to None if TFTP testing is not possible or desired.
+env__net_tftp_readable_file = {
+ 'fn': 'ubtest-readable.bin',
+ 'addr': 0x10000000,
+ 'size': 5058624,
+ 'crc32': 'c2244b26',
+}
+
+# Details regarding a file that may be read from a NFS server. This variable
+# may be omitted or set to None if NFS testing is not possible or desired.
+env__net_nfs_readable_file = {
+ 'fn': 'ubtest-readable.bin',
+ 'addr': 0x10000000,
+ 'size': 5058624,
+ 'crc32': 'c2244b26',
+}
+"""
+
+net_set_up = False
+
+def test_net_pre_commands(u_boot_console):
+ """Execute any commands required to enable network hardware.
+
+ These commands are provided by the boardenv_* file; see the comment at the
+ beginning of this file.
+ """
+
+ init_usb = u_boot_console.config.env.get('env__net_uses_usb', False)
+ if init_usb:
+ u_boot_console.run_command('usb start')
+
+ init_pci = u_boot_console.config.env.get('env__net_uses_pci', False)
+ if init_pci:
+ u_boot_console.run_command('pci enum')
+
+@pytest.mark.buildconfigspec('cmd_dhcp')
+def test_net_dhcp(u_boot_console):
+ """Test the dhcp command.
+
+ The boardenv_* file may be used to enable/disable this test; see the
+ comment at the beginning of this file.
+ """
+
+ test_dhcp = u_boot_console.config.env.get('env__net_dhcp_server', False)
+ if not test_dhcp:
+ pytest.skip('No DHCP server available')
+
+ u_boot_console.run_command('setenv autoload no')
+ output = u_boot_console.run_command('dhcp')
+ assert 'DHCP client bound to address ' in output
+
+ global net_set_up
+ net_set_up = True
+
+@pytest.mark.buildconfigspec('net')
+def test_net_setup_static(u_boot_console):
+ """Set up a static IP configuration.
+
+ The configuration is provided by the boardenv_* file; see the comment at
+ the beginning of this file.
+ """
+
+ env_vars = u_boot_console.config.env.get('env__net_static_env_vars', None)
+ if not env_vars:
+ pytest.skip('No static network configuration is defined')
+
+ for (var, val) in env_vars:
+ u_boot_console.run_command('setenv %s %s' % (var, val))
+
+ global net_set_up
+ net_set_up = True
+
+@pytest.mark.buildconfigspec('cmd_ping')
+def test_net_ping(u_boot_console):
+ """Test the ping command.
+
+ The $serverip (as set up by either test_net_dhcp or test_net_setup_static)
+ is pinged. The test validates that the host is alive, as reported by the
+ ping command's output.
+ """
+
+ if not net_set_up:
+ pytest.skip('Network not initialized')
+
+ output = u_boot_console.run_command('ping $serverip')
+ assert 'is alive' in output
+
+@pytest.mark.buildconfigspec('cmd_net')
+def test_net_tftpboot(u_boot_console):
+ """Test the tftpboot command.
+
+ A file is downloaded from the TFTP server, its size and optionally its
+ CRC32 are validated.
+
+ The details of the file to download are provided by the boardenv_* file;
+ see the comment at the beginning of this file.
+ """
+
+ if not net_set_up:
+ pytest.skip('Network not initialized')
+
+ f = u_boot_console.config.env.get('env__net_tftp_readable_file', None)
+ if not f:
+ pytest.skip('No TFTP readable file to read')
+
+ addr = f.get('addr', None)
+
+ fn = f['fn']
+ if not addr:
+ output = u_boot_console.run_command('tftpboot %s' % (fn))
+ else:
+ output = u_boot_console.run_command('tftpboot %x %s' % (addr, fn))
+ expected_text = 'Bytes transferred = '
+ sz = f.get('size', None)
+ if sz:
+ expected_text += '%d' % sz
+ assert expected_text in output
+
+ expected_crc = f.get('crc32', None)
+ if not expected_crc:
+ return
+
+ if u_boot_console.config.buildconfig.get('config_cmd_crc32', 'n') != 'y':
+ return
+
+ output = u_boot_console.run_command('crc32 $fileaddr $filesize')
+ assert expected_crc in output
+
+@pytest.mark.buildconfigspec('cmd_nfs')
+def test_net_nfs(u_boot_console):
+ """Test the nfs command.
+
+ A file is downloaded from the NFS server, its size and optionally its
+ CRC32 are validated.
+
+ The details of the file to download are provided by the boardenv_* file;
+ see the comment at the beginning of this file.
+ """
+
+ if not net_set_up:
+ pytest.skip('Network not initialized')
+
+ f = u_boot_console.config.env.get('env__net_nfs_readable_file', None)
+ if not f:
+ pytest.skip('No NFS readable file to read')
+
+ addr = f.get('addr', None)
+ if not addr:
+ addr = u_boot_utils.find_ram_base(u_boot_console)
+
+ fn = f['fn']
+ output = u_boot_console.run_command('nfs %x %s' % (addr, fn))
+ expected_text = 'Bytes transferred = '
+ sz = f.get('size', None)
+ if sz:
+ expected_text += '%d' % sz
+ assert expected_text in output
+
+ expected_crc = f.get('crc32', None)
+ if not expected_crc:
+ return
+
+ if u_boot_console.config.buildconfig.get('config_cmd_crc32', 'n') != 'y':
+ return
+
+ output = u_boot_console.run_command('crc32 %x $filesize' % addr)
+ assert expected_crc in output
diff --git a/roms/u-boot/test/py/tests/test_ofplatdata.py b/roms/u-boot/test/py/tests/test_ofplatdata.py
new file mode 100644
index 000000000..e9cce4daf
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_ofplatdata.py
@@ -0,0 +1,23 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2016 Google, Inc
+
+import pytest
+import u_boot_utils as util
+
+@pytest.mark.boardspec('sandbox_spl')
+@pytest.mark.buildconfigspec('spl_of_platdata')
+def test_spl_devicetree(u_boot_console):
+ """Test content of spl device-tree"""
+ cons = u_boot_console
+ dtb = cons.config.build_dir + '/spl/u-boot-spl.dtb'
+ fdtgrep = cons.config.build_dir + '/tools/fdtgrep'
+ output = util.run_and_log(cons, [fdtgrep, '-l', dtb])
+
+ assert "u-boot,dm-pre-reloc" not in output
+ assert "u-boot,dm-pre-proper" not in output
+ assert "u-boot,dm-spl" not in output
+ assert "u-boot,dm-tpl" not in output
+
+ assert "spl-test5" not in output
+ assert "spl-test6" not in output
+ assert "spl-test7" in output
diff --git a/roms/u-boot/test/py/tests/test_part.py b/roms/u-boot/test/py/tests/test_part.py
new file mode 100644
index 000000000..cba980451
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_part.py
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2020
+# Niel Fourie, DENX Software Engineering, lusus@denx.de
+
+import pytest
+
+@pytest.mark.buildconfigspec('cmd_part')
+@pytest.mark.buildconfigspec('partitions')
+@pytest.mark.buildconfigspec('efi_partition')
+def test_dm_compat(u_boot_console):
+ """Test that `part types` prints a result which includes `EFI`."""
+ output = u_boot_console.run_command('part types')
+ assert "Supported partition tables:" in output
+ assert "EFI" in output
diff --git a/roms/u-boot/test/py/tests/test_pinmux.py b/roms/u-boot/test/py/tests/test_pinmux.py
new file mode 100644
index 000000000..0cbbae000
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_pinmux.py
@@ -0,0 +1,84 @@
+# SPDX-License-Identifier: GPL-2.0
+
+import pytest
+import u_boot_utils
+
+@pytest.mark.buildconfigspec('cmd_pinmux')
+def test_pinmux_usage_1(u_boot_console):
+ """Test that 'pinmux' command without parameters displays
+ pinmux usage."""
+ output = u_boot_console.run_command('pinmux')
+ assert 'Usage:' in output
+
+@pytest.mark.buildconfigspec('cmd_pinmux')
+def test_pinmux_usage_2(u_boot_console):
+ """Test that 'pinmux status' executed without previous "pinmux dev"
+ command displays pinmux usage."""
+ output = u_boot_console.run_command('pinmux status')
+ assert 'Usage:' in output
+
+@pytest.mark.buildconfigspec('cmd_pinmux')
+@pytest.mark.boardspec('sandbox')
+def test_pinmux_status_all(u_boot_console):
+ """Test that 'pinmux status -a' displays pin's muxing."""
+ output = u_boot_console.run_command('pinmux status -a')
+
+ assert ('pinctrl-gpio:' in output)
+ assert ('a5 : gpio output .' in output)
+ assert ('a6 : gpio output .' in output)
+
+ assert ('pinctrl:' in output)
+ assert ('P0 : UART TX.' in output)
+ assert ('P1 : UART RX.' in output)
+ assert ('P2 : I2S SCK.' in output)
+ assert ('P3 : I2S SD.' in output)
+ assert ('P4 : I2S WS.' in output)
+ assert ('P5 : GPIO0 bias-pull-up input-disable.' in output)
+ assert ('P6 : GPIO1 drive-open-drain.' in output)
+ assert ('P7 : GPIO2 bias-pull-down input-enable.' in output)
+ assert ('P8 : GPIO3 bias-disable.' in output)
+
+@pytest.mark.buildconfigspec('cmd_pinmux')
+@pytest.mark.boardspec('sandbox')
+def test_pinmux_list(u_boot_console):
+ """Test that 'pinmux list' returns the pin-controller list."""
+ output = u_boot_console.run_command('pinmux list')
+ assert 'sandbox_pinctrl' in output
+
+@pytest.mark.buildconfigspec('cmd_pinmux')
+def test_pinmux_dev_bad(u_boot_console):
+ """Test that 'pinmux dev' returns an error when trying to select a
+ wrong pin controller."""
+ pincontroller = 'bad_pin_controller_name'
+ output = u_boot_console.run_command('pinmux dev ' + pincontroller)
+ expected_output = 'Can\'t get the pin-controller: ' + pincontroller + '!'
+ assert (expected_output in output)
+
+@pytest.mark.buildconfigspec('cmd_pinmux')
+@pytest.mark.boardspec('sandbox')
+def test_pinmux_dev(u_boot_console):
+ """Test that 'pinmux dev' select the wanted pin controller."""
+ pincontroller = 'pinctrl'
+ output = u_boot_console.run_command('pinmux dev ' + pincontroller)
+ expected_output = 'dev: ' + pincontroller
+ assert (expected_output in output)
+
+@pytest.mark.buildconfigspec('cmd_pinmux')
+@pytest.mark.boardspec('sandbox')
+def test_pinmux_status(u_boot_console):
+ """Test that 'pinmux status' displays selected pincontroller's pin
+ muxing descriptions."""
+ output = u_boot_console.run_command('pinmux status')
+
+ assert (not 'pinctrl-gpio:' in output)
+ assert (not 'pinctrl:' in output)
+
+ assert ('P0 : UART TX.' in output)
+ assert ('P1 : UART RX.' in output)
+ assert ('P2 : I2S SCK.' in output)
+ assert ('P3 : I2S SD.' in output)
+ assert ('P4 : I2S WS.' in output)
+ assert ('P5 : GPIO0 bias-pull-up input-disable.' in output)
+ assert ('P6 : GPIO1 drive-open-drain.' in output)
+ assert ('P7 : GPIO2 bias-pull-down input-enable.' in output)
+ assert ('P8 : GPIO3 bias-disable.' in output)
diff --git a/roms/u-boot/test/py/tests/test_pstore.py b/roms/u-boot/test/py/tests/test_pstore.py
new file mode 100644
index 000000000..5a35724f6
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_pstore.py
@@ -0,0 +1,77 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2020, Collabora
+# Author: Frédéric Danis <frederic.danis@collabora.com>
+
+import pytest
+import u_boot_utils
+import os
+import tempfile
+import shutil
+
+PSTORE_ADDR=0x3000000
+PSTORE_LENGTH=0x100000
+PSTORE_PANIC1='test/py/tests/test_pstore_data_panic1.hex'
+PSTORE_PANIC2='test/py/tests/test_pstore_data_panic2.hex'
+PSTORE_CONSOLE='test/py/tests/test_pstore_data_console.hex'
+ADDR=0x01000008
+
+def load_pstore(u_boot_console):
+ """Load PStore records from sample files"""
+
+ output = u_boot_console.run_command_list([
+ 'host load hostfs - 0x%x %s' % (PSTORE_ADDR,
+ os.path.join(u_boot_console.config.source_dir, PSTORE_PANIC1)),
+ 'host load hostfs - 0x%x %s' % (PSTORE_ADDR + 4096,
+ os.path.join(u_boot_console.config.source_dir, PSTORE_PANIC2)),
+ 'host load hostfs - 0x%x %s' % (PSTORE_ADDR + 253 * 4096,
+ os.path.join(u_boot_console.config.source_dir, PSTORE_CONSOLE)),
+ 'pstore set 0x%x 0x%x' % (PSTORE_ADDR, PSTORE_LENGTH)])
+
+def checkfile(u_boot_console, path, filesize, checksum):
+ """Check file against MD5 checksum"""
+
+ output = u_boot_console.run_command_list([
+ 'load hostfs - %x %s' % (ADDR, path),
+ 'printenv filesize'])
+ assert('filesize=%x' % (filesize) in ''.join(output))
+
+ output = u_boot_console.run_command_list([
+ 'md5sum %x $filesize' % ADDR,
+ 'setenv filesize'])
+ assert(checksum in ''.join(output))
+
+@pytest.mark.buildconfigspec('cmd_pstore')
+def test_pstore_display_all_records(u_boot_console):
+ """Test that pstore displays all records."""
+
+ u_boot_console.run_command('')
+ load_pstore(u_boot_console)
+ response = u_boot_console.run_command('pstore display')
+ assert('**** Dump' in response)
+ assert('**** Console' in response)
+
+@pytest.mark.buildconfigspec('cmd_pstore')
+def test_pstore_display_one_record(u_boot_console):
+ """Test that pstore displays only one record."""
+
+ u_boot_console.run_command('')
+ load_pstore(u_boot_console)
+ response = u_boot_console.run_command('pstore display dump 1')
+ assert('Panic#2 Part1' in response)
+ assert('**** Console' not in response)
+
+@pytest.mark.buildconfigspec('cmd_pstore')
+def test_pstore_save_records(u_boot_console):
+ """Test that pstore saves all records."""
+
+ outdir = tempfile.mkdtemp()
+
+ u_boot_console.run_command('')
+ load_pstore(u_boot_console)
+ u_boot_console.run_command('pstore save hostfs - %s' % (outdir))
+
+ checkfile(u_boot_console, '%s/dmesg-ramoops-0' % (outdir), 3798, '8059335ab4cfa62c77324c491659c503')
+ checkfile(u_boot_console, '%s/dmesg-ramoops-1' % (outdir), 4035, '3ff30df3429d81939c75d0070b5187b9')
+ checkfile(u_boot_console, '%s/console-ramoops-0' % (outdir), 4084, 'bb44de4a9b8ebd9b17ae98003287325b')
+
+ shutil.rmtree(outdir)
diff --git a/roms/u-boot/test/py/tests/test_pstore_data_console.hex b/roms/u-boot/test/py/tests/test_pstore_data_console.hex
new file mode 100644
index 000000000..e7f426e89
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_pstore_data_console.hex
Binary files differ
diff --git a/roms/u-boot/test/py/tests/test_pstore_data_panic1.hex b/roms/u-boot/test/py/tests/test_pstore_data_panic1.hex
new file mode 100644
index 000000000..988929d12
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_pstore_data_panic1.hex
Binary files differ
diff --git a/roms/u-boot/test/py/tests/test_pstore_data_panic2.hex b/roms/u-boot/test/py/tests/test_pstore_data_panic2.hex
new file mode 100644
index 000000000..8f9d56cbe
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_pstore_data_panic2.hex
Binary files differ
diff --git a/roms/u-boot/test/py/tests/test_qfw.py b/roms/u-boot/test/py/tests/test_qfw.py
new file mode 100644
index 000000000..8b668c972
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_qfw.py
@@ -0,0 +1,26 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2021, Asherah Connor <ashe@kivikakk.ee>
+
+# Test qfw command implementation
+
+import pytest
+
+@pytest.mark.buildconfigspec('cmd_qfw')
+def test_qfw_cpus(u_boot_console):
+ "Test QEMU firmware config reports the CPU count."
+
+ output = u_boot_console.run_command('qfw cpus')
+ # The actual number varies depending on the board under test, so only
+ # assert a non-zero output.
+ assert 'cpu(s) online' in output
+ assert '0 cpu(s) online' not in output
+
+@pytest.mark.buildconfigspec('cmd_qfw')
+def test_qfw_list(u_boot_console):
+ "Test QEMU firmware config lists devices."
+
+ output = u_boot_console.run_command('qfw list')
+ # Assert either:
+ # 1) 'test-one', from the sandbox driver, or
+ # 2) 'bootorder', found in every real QEMU implementation.
+ assert ("bootorder" in output) or ("test-one" in output)
diff --git a/roms/u-boot/test/py/tests/test_sandbox_exit.py b/roms/u-boot/test/py/tests/test_sandbox_exit.py
new file mode 100644
index 000000000..706f5fa35
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_sandbox_exit.py
@@ -0,0 +1,45 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2015 Stephen Warren
+# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved.
+
+import pytest
+import signal
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('sysreset_cmd_poweroff')
+def test_poweroff(u_boot_console):
+ """Test that the "poweroff" command exits sandbox process."""
+
+ u_boot_console.run_command('poweroff', wait_for_prompt=False)
+ assert(u_boot_console.validate_exited())
+
+@pytest.mark.boardspec('sandbox')
+def test_ctrl_c(u_boot_console):
+ """Test that sending SIGINT to sandbox causes it to exit."""
+
+ u_boot_console.kill(signal.SIGINT)
+ assert(u_boot_console.validate_exited())
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('cmd_exception')
+@pytest.mark.buildconfigspec('sandbox_crash_reset')
+def test_exception_reset(u_boot_console):
+ """Test that SIGILL causes a reset."""
+
+ u_boot_console.run_command('exception undefined', wait_for_prompt=False)
+ m = u_boot_console.p.expect(['resetting ...', 'U-Boot'])
+ if m != 0:
+ raise Exception('SIGILL did not lead to reset')
+ m = u_boot_console.p.expect(['U-Boot', '=>'])
+ if m != 0:
+ raise Exception('SIGILL did not lead to reset')
+ u_boot_console.restart_uboot()
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('cmd_exception')
+@pytest.mark.notbuildconfigspec('sandbox_crash_reset')
+def test_exception_exit(u_boot_console):
+ """Test that SIGILL causes a reset."""
+
+ u_boot_console.run_command('exception undefined', wait_for_prompt=False)
+ assert(u_boot_console.validate_exited())
diff --git a/roms/u-boot/test/py/tests/test_scp03.py b/roms/u-boot/test/py/tests/test_scp03.py
new file mode 100644
index 000000000..1f689252d
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_scp03.py
@@ -0,0 +1,27 @@
+# Copyright (c) 2021 Foundries.io Ltd
+#
+# SPDX-License-Identifier: GPL-2.0+
+#
+# SCP03 command test
+
+"""
+This tests SCP03 command in U-boot.
+
+For additional details check doc/usage/scp03.rst
+"""
+
+import pytest
+import u_boot_utils as util
+
+@pytest.mark.buildconfigspec('cmd_scp03')
+def test_scp03(u_boot_console):
+ """Enable and provision keys with SCP03
+ """
+
+ success_str1 = "SCP03 is enabled"
+ success_str2 = "SCP03 is provisioned"
+
+ response = u_boot_console.run_command('scp03 enable')
+ assert success_str1 in response
+ response = u_boot_console.run_command('scp03 provision')
+ assert success_str2 in response
diff --git a/roms/u-boot/test/py/tests/test_sf.py b/roms/u-boot/test/py/tests/test_sf.py
new file mode 100644
index 000000000..adf8b7dc8
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_sf.py
@@ -0,0 +1,217 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2016, Xilinx Inc. Michal Simek
+# Copyright (c) 2017, Xiphos Systems Corp. All rights reserved.
+
+import re
+import pytest
+import random
+import u_boot_utils
+
+"""
+Note: This test relies on boardenv_* containing configuration values to define
+which SPI Flash areas are available for testing. Without this, this test will
+be automatically skipped.
+For example:
+
+# A list of sections of Flash memory to be tested.
+env__sf_configs = (
+ {
+ # Where in SPI Flash should the test operate.
+ 'offset': 0x00000000,
+ # This value is optional.
+ # If present, specifies the [[bus:]cs] argument used in `sf probe`
+ # If missing, defaults to 0.
+ 'id': '0:1',
+ # This value is optional.
+ # If set as a number, specifies the speed of the SPI Flash.
+ # If set as an array of 2, specifies a range for a random speed.
+ # If missing, defaults to 0.
+ 'speed': 1000000,
+ # This value is optional.
+ # If present, specifies the size to use for read/write operations.
+ # If missing, the SPI Flash page size is used as a default (based on
+ # the `sf probe` output).
+ 'len': 0x10000,
+ # This value is optional.
+ # If present, specifies if the test can write to Flash offset
+ # If missing, defaults to False.
+ 'writeable': False,
+ # This value is optional.
+ # If present, specifies the expected CRC32 value of the flash area.
+ # If missing, extra check is ignored.
+ 'crc32': 0xCAFECAFE,
+ },
+)
+"""
+
+def sf_prepare(u_boot_console, env__sf_config):
+ """Check global state of the SPI Flash before running any test.
+
+ Args:
+ u_boot_console: A U-Boot console connection.
+ env__sf_config: The single SPI Flash device configuration on which to
+ run the tests.
+
+ Returns:
+ sf_params: a dictionary of SPI Flash parameters.
+ """
+
+ sf_params = {}
+ sf_params['ram_base'] = u_boot_utils.find_ram_base(u_boot_console)
+
+ probe_id = env__sf_config.get('id', 0)
+ speed = env__sf_config.get('speed', 0)
+ if isinstance(speed, int):
+ sf_params['speed'] = speed
+ else:
+ assert len(speed) == 2, "If speed is a list, it must have 2 entries"
+ sf_params['speed'] = random.randint(speed[0], speed[1])
+
+ cmd = 'sf probe %d %d' % (probe_id, sf_params['speed'])
+
+ output = u_boot_console.run_command(cmd)
+ assert 'SF: Detected' in output, 'No Flash device available'
+
+ m = re.search('page size (.+?) Bytes', output)
+ assert m, 'SPI Flash page size not recognized'
+ sf_params['page_size'] = int(m.group(1))
+
+ m = re.search('erase size (.+?) KiB', output)
+ assert m, 'SPI Flash erase size not recognized'
+ sf_params['erase_size'] = int(m.group(1))
+ sf_params['erase_size'] *= 1024
+
+ m = re.search('total (.+?) MiB', output)
+ assert m, 'SPI Flash total size not recognized'
+ sf_params['total_size'] = int(m.group(1))
+ sf_params['total_size'] *= 1024 * 1024
+
+ assert 'offset' in env__sf_config, \
+ '\'offset\' is required for this test.'
+ sf_params['len'] = env__sf_config.get('len', sf_params['erase_size'])
+
+ assert not env__sf_config['offset'] % sf_params['erase_size'], \
+ 'offset not multiple of erase size.'
+ assert not sf_params['len'] % sf_params['erase_size'], \
+ 'erase length not multiple of erase size.'
+
+ assert not (env__sf_config.get('writeable', False) and
+ 'crc32' in env__sf_config), \
+ 'Cannot check crc32 on writeable sections'
+
+ return sf_params
+
+def sf_read(u_boot_console, env__sf_config, sf_params):
+ """Helper function used to read and compute the CRC32 value of a section of
+ SPI Flash memory.
+
+ Args:
+ u_boot_console: A U-Boot console connection.
+ env__sf_config: The single SPI Flash device configuration on which to
+ run the tests.
+ sf_params: SPI Flash parameters.
+
+ Returns:
+ CRC32 value of SPI Flash section
+ """
+
+ addr = sf_params['ram_base']
+ offset = env__sf_config['offset']
+ count = sf_params['len']
+ pattern = random.randint(0, 0xFF)
+ crc_expected = env__sf_config.get('crc32', None)
+
+ cmd = 'mw.b %08x %02x %x' % (addr, pattern, count)
+ u_boot_console.run_command(cmd)
+ crc_pattern = u_boot_utils.crc32(u_boot_console, addr, count)
+ if crc_expected:
+ assert crc_pattern != crc_expected
+
+ cmd = 'sf read %08x %08x %x' % (addr, offset, count)
+ response = u_boot_console.run_command(cmd)
+ assert 'Read: OK' in response, 'Read operation failed'
+ crc_readback = u_boot_utils.crc32(u_boot_console, addr, count)
+ assert crc_pattern != crc_readback, 'sf read did not update RAM content.'
+ if crc_expected:
+ assert crc_readback == crc_expected
+
+ return crc_readback
+
+def sf_update(u_boot_console, env__sf_config, sf_params):
+ """Helper function used to update a section of SPI Flash memory.
+
+ Args:
+ u_boot_console: A U-Boot console connection.
+ env__sf_config: The single SPI Flash device configuration on which to
+ run the tests.
+
+ Returns:
+ CRC32 value of SPI Flash section
+ """
+
+ addr = sf_params['ram_base']
+ offset = env__sf_config['offset']
+ count = sf_params['len']
+ pattern = int(random.random() * 0xFF)
+
+ cmd = 'mw.b %08x %02x %x' % (addr, pattern, count)
+ u_boot_console.run_command(cmd)
+ crc_pattern = u_boot_utils.crc32(u_boot_console, addr, count)
+
+ cmd = 'sf update %08x %08x %x' % (addr, offset, count)
+ u_boot_console.run_command(cmd)
+ crc_readback = sf_read(u_boot_console, env__sf_config, sf_params)
+
+ assert crc_readback == crc_pattern
+
+@pytest.mark.buildconfigspec('cmd_sf')
+@pytest.mark.buildconfigspec('cmd_crc32')
+@pytest.mark.buildconfigspec('cmd_memory')
+def test_sf_read(u_boot_console, env__sf_config):
+ sf_params = sf_prepare(u_boot_console, env__sf_config)
+ sf_read(u_boot_console, env__sf_config, sf_params)
+
+@pytest.mark.buildconfigspec('cmd_sf')
+@pytest.mark.buildconfigspec('cmd_crc32')
+@pytest.mark.buildconfigspec('cmd_memory')
+def test_sf_read_twice(u_boot_console, env__sf_config):
+ sf_params = sf_prepare(u_boot_console, env__sf_config)
+
+ crc1 = sf_read(u_boot_console, env__sf_config, sf_params)
+ sf_params['ram_base'] += 0x100
+ crc2 = sf_read(u_boot_console, env__sf_config, sf_params)
+
+ assert crc1 == crc2, 'CRC32 of two successive read operation do not match'
+
+@pytest.mark.buildconfigspec('cmd_sf')
+@pytest.mark.buildconfigspec('cmd_crc32')
+@pytest.mark.buildconfigspec('cmd_memory')
+def test_sf_erase(u_boot_console, env__sf_config):
+ if not env__sf_config.get('writeable', False):
+ pytest.skip('Flash config is tagged as not writeable')
+
+ sf_params = sf_prepare(u_boot_console, env__sf_config)
+ addr = sf_params['ram_base']
+ offset = env__sf_config['offset']
+ count = sf_params['len']
+
+ cmd = 'sf erase %08x %x' % (offset, count)
+ output = u_boot_console.run_command(cmd)
+ assert 'Erased: OK' in output, 'Erase operation failed'
+
+ cmd = 'mw.b %08x ff %x' % (addr, count)
+ u_boot_console.run_command(cmd)
+ crc_ffs = u_boot_utils.crc32(u_boot_console, addr, count)
+
+ crc_read = sf_read(u_boot_console, env__sf_config, sf_params)
+ assert crc_ffs == crc_read, 'Unexpected CRC32 after erase operation.'
+
+@pytest.mark.buildconfigspec('cmd_sf')
+@pytest.mark.buildconfigspec('cmd_crc32')
+@pytest.mark.buildconfigspec('cmd_memory')
+def test_sf_update(u_boot_console, env__sf_config):
+ if not env__sf_config.get('writeable', False):
+ pytest.skip('Flash config is tagged as not writeable')
+
+ sf_params = sf_prepare(u_boot_console, env__sf_config)
+ sf_update(u_boot_console, env__sf_config, sf_params)
diff --git a/roms/u-boot/test/py/tests/test_shell_basics.py b/roms/u-boot/test/py/tests/test_shell_basics.py
new file mode 100644
index 000000000..68a3f892f
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_shell_basics.py
@@ -0,0 +1,45 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved.
+
+# Test basic shell functionality, such as commands separate by semi-colons.
+
+import pytest
+
+pytestmark = pytest.mark.buildconfigspec('cmd_echo')
+
+def test_shell_execute(u_boot_console):
+ """Test any shell command."""
+
+ response = u_boot_console.run_command('echo hello')
+ assert response.strip() == 'hello'
+
+def test_shell_semicolon_two(u_boot_console):
+ """Test two shell commands separate by a semi-colon."""
+
+ cmd = 'echo hello; echo world'
+ response = u_boot_console.run_command(cmd)
+ # This validation method ignores the exact whitespace between the strings
+ assert response.index('hello') < response.index('world')
+
+def test_shell_semicolon_three(u_boot_console):
+ """Test three shell commands separate by a semi-colon, with variable
+ expansion dependencies between them."""
+
+ cmd = 'setenv list 1; setenv list ${list}2; setenv list ${list}3; ' + \
+ 'echo ${list}'
+ response = u_boot_console.run_command(cmd)
+ assert response.strip() == '123'
+ u_boot_console.run_command('setenv list')
+
+def test_shell_run(u_boot_console):
+ """Test the "run" shell command."""
+
+ u_boot_console.run_command('setenv foo \'setenv monty 1; setenv python 2\'')
+ u_boot_console.run_command('run foo')
+ response = u_boot_console.run_command('echo ${monty}')
+ assert response.strip() == '1'
+ response = u_boot_console.run_command('echo ${python}')
+ assert response.strip() == '2'
+ u_boot_console.run_command('setenv foo')
+ u_boot_console.run_command('setenv monty')
+ u_boot_console.run_command('setenv python')
diff --git a/roms/u-boot/test/py/tests/test_sleep.py b/roms/u-boot/test/py/tests/test_sleep.py
new file mode 100644
index 000000000..392af29db
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_sleep.py
@@ -0,0 +1,43 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
+
+import pytest
+import time
+
+"""
+Note: This test doesn't rely on boardenv_* configuration values but they can
+change test behavior.
+
+# Setup env__sleep_accurate to False if time is not accurate on your platform
+env__sleep_accurate = False
+
+# Setup env__sleep_time time in seconds board is set to sleep
+env__sleep_time = 3
+
+# Setup env__sleep_margin set a margin for any system overhead
+env__sleep_margin = 0.25
+
+"""
+
+def test_sleep(u_boot_console):
+ """Test the sleep command, and validate that it sleeps for approximately
+ the correct amount of time."""
+
+ sleep_skip = u_boot_console.config.env.get('env__sleep_accurate', True)
+ if not sleep_skip:
+ pytest.skip('sleep is not accurate')
+
+ if u_boot_console.config.buildconfig.get('config_cmd_misc', 'n') != 'y':
+ pytest.skip('sleep command not supported')
+
+ # 3s isn't too long, but is enough to cross a few second boundaries.
+ sleep_time = u_boot_console.config.env.get('env__sleep_time', 3)
+ sleep_margin = u_boot_console.config.env.get('env__sleep_margin', 0.25)
+ tstart = time.time()
+ u_boot_console.run_command('sleep %d' % sleep_time)
+ tend = time.time()
+ elapsed = tend - tstart
+ assert elapsed >= (sleep_time - 0.01)
+ if not u_boot_console.config.gdbserver:
+ # margin is hopefully enough to account for any system overhead.
+ assert elapsed < (sleep_time + sleep_margin)
diff --git a/roms/u-boot/test/py/tests/test_spl.py b/roms/u-boot/test/py/tests/test_spl.py
new file mode 100644
index 000000000..bd273dad8
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_spl.py
@@ -0,0 +1,34 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright 2020 Google LLC
+# Written by Simon Glass <sjg@chromium.org>
+
+import os.path
+import pytest
+
+def test_spl(u_boot_console, ut_spl_subtest):
+ """Execute a "ut" subtest.
+
+ The subtests are collected in function generate_ut_subtest() from linker
+ generated lists by applying a regular expression to the lines of file
+ spl/u-boot-spl.sym. The list entries are created using the C macro
+ UNIT_TEST().
+
+ Strict naming conventions have to be followed to match the regular
+ expression. Use UNIT_TEST(foo_test_bar, _flags, foo_test) for a test bar in
+ test suite foo that can be executed via command 'ut foo bar' and is
+ implemented in C function foo_test_bar().
+
+ Args:
+ u_boot_console (ConsoleBase): U-Boot console
+ ut_subtest (str): SPL test to be executed (e.g. 'dm platdata_phandle')
+ """
+ try:
+ cons = u_boot_console
+ cons.restart_uboot_with_flags(['-u', '-k', ut_spl_subtest.split()[1]])
+ output = cons.get_spawn_output().replace('\r', '')
+ assert 'Failures: 0' in output
+ finally:
+ # Restart afterward in case a non-SPL test is run next. This should not
+ # happen since SPL tests are run in their own invocation of test.py, but
+ # the cost of doing this is not too great at present.
+ u_boot_console.restart_uboot()
diff --git a/roms/u-boot/test/py/tests/test_stackprotector.py b/roms/u-boot/test/py/tests/test_stackprotector.py
new file mode 100644
index 000000000..b009437e5
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_stackprotector.py
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2021 Broadcom
+
+import pytest
+import signal
+
+@pytest.mark.buildconfigspec('cmd_stackprotector_test')
+def test_stackprotector(u_boot_console):
+ """Test that the stackprotector function works."""
+
+ u_boot_console.run_command('stackprot_test',wait_for_prompt=False)
+ expected_response = 'Stack smashing detected'
+ u_boot_console.wait_for(expected_response)
+ u_boot_console.restart_uboot()
diff --git a/roms/u-boot/test/py/tests/test_tpm2.py b/roms/u-boot/test/py/tests/test_tpm2.py
new file mode 100644
index 000000000..70f906da5
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_tpm2.py
@@ -0,0 +1,233 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2018, Bootlin
+# Author: Miquel Raynal <miquel.raynal@bootlin.com>
+
+import os.path
+import pytest
+import u_boot_utils
+import re
+import time
+
+"""
+Test the TPMv2.x related commands. You must have a working hardware setup in
+order to do these tests.
+
+Notes:
+* These tests will prove the password mechanism. The TPM chip must be cleared of
+any password.
+* Commands like pcr_setauthpolicy and pcr_resetauthpolicy are not implemented
+here because they would fail the tests in most cases (TPMs do not implement them
+and return an error).
+"""
+
+updates = 0
+
+def force_init(u_boot_console, force=False):
+ """When a test fails, U-Boot is reset. Because TPM stack must be initialized
+ after each reboot, we must ensure these lines are always executed before
+ trying any command or they will fail with no reason. Executing 'tpm init'
+ twice will spawn an error used to detect that the TPM was not reset and no
+ initialization code should be run.
+ """
+ output = u_boot_console.run_command('tpm2 init')
+ if force or not 'Error' in output:
+ u_boot_console.run_command('echo --- start of init ---')
+ u_boot_console.run_command('tpm2 startup TPM2_SU_CLEAR')
+ u_boot_console.run_command('tpm2 self_test full')
+ u_boot_console.run_command('tpm2 clear TPM2_RH_LOCKOUT')
+ output = u_boot_console.run_command('echo $?')
+ if not output.endswith('0'):
+ u_boot_console.run_command('tpm2 clear TPM2_RH_PLATFORM')
+ u_boot_console.run_command('echo --- end of init ---')
+
+@pytest.mark.buildconfigspec('cmd_tpm_v2')
+def test_tpm2_init(u_boot_console):
+ """Init the software stack to use TPMv2 commands."""
+
+ u_boot_console.run_command('tpm2 init')
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+
+@pytest.mark.buildconfigspec('cmd_tpm_v2')
+def test_tpm2_startup(u_boot_console):
+ """Execute a TPM2_Startup command.
+
+ Initiate the TPM internal state machine.
+ """
+
+ u_boot_console.run_command('tpm2 startup TPM2_SU_CLEAR')
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+
+@pytest.mark.buildconfigspec('cmd_tpm_v2')
+def test_tpm2_self_test_full(u_boot_console):
+ """Execute a TPM2_SelfTest (full) command.
+
+ Ask the TPM to perform all self tests to also enable full capabilities.
+ """
+
+ u_boot_console.run_command('tpm2 self_test full')
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+
+@pytest.mark.buildconfigspec('cmd_tpm_v2')
+def test_tpm2_continue_self_test(u_boot_console):
+ """Execute a TPM2_SelfTest (continued) command.
+
+ Ask the TPM to finish its self tests (alternative to the full test) in order
+ to enter a fully operational state.
+ """
+
+ u_boot_console.run_command('tpm2 self_test continue')
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+
+@pytest.mark.buildconfigspec('cmd_tpm_v2')
+def test_tpm2_clear(u_boot_console):
+ """Execute a TPM2_Clear command.
+
+ Ask the TPM to reset entirely its internal state (including internal
+ configuration, passwords, counters and DAM parameters). This is half of the
+ TAKE_OWNERSHIP command from TPMv1.
+
+ Use the LOCKOUT hierarchy for this. The LOCKOUT/PLATFORM hierarchies must
+ not have a password set, otherwise this test will fail. ENDORSEMENT and
+ PLATFORM hierarchies are also available.
+ """
+
+ u_boot_console.run_command('tpm2 clear TPM2_RH_LOCKOUT')
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+
+ u_boot_console.run_command('tpm2 clear TPM2_RH_PLATFORM')
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+
+@pytest.mark.buildconfigspec('cmd_tpm_v2')
+def test_tpm2_change_auth(u_boot_console):
+ """Execute a TPM2_HierarchyChangeAuth command.
+
+ Ask the TPM to change the owner, ie. set a new password: 'unicorn'
+
+ Use the LOCKOUT hierarchy for this. ENDORSEMENT and PLATFORM hierarchies are
+ also available.
+ """
+
+ force_init(u_boot_console)
+
+ u_boot_console.run_command('tpm2 change_auth TPM2_RH_LOCKOUT unicorn')
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+
+ u_boot_console.run_command('tpm2 clear TPM2_RH_LOCKOUT unicorn')
+ output = u_boot_console.run_command('echo $?')
+ u_boot_console.run_command('tpm2 clear TPM2_RH_PLATFORM')
+ assert output.endswith('0')
+
+@pytest.mark.buildconfigspec('cmd_tpm_v2')
+def test_tpm2_get_capability(u_boot_console):
+ """Execute a TPM_GetCapability command.
+
+ Display one capability. In our test case, let's display the default DAM
+ lockout counter that should be 0 since the CLEAR:
+ - TPM_CAP_TPM_PROPERTIES = 0x6
+ - TPM_PT_LOCKOUT_COUNTER (1st parameter) = PTR_VAR + 14
+
+ There is no expected default values because it would depend on the chip
+ used. We can still save them in order to check they have changed later.
+ """
+
+ force_init(u_boot_console)
+ ram = u_boot_utils.find_ram_base(u_boot_console)
+
+ read_cap = u_boot_console.run_command('tpm2 get_capability 0x6 0x20e 0x200 1') #0x%x 1' % ram)
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+ assert 'Property 0x0000020e: 0x00000000' in read_cap
+
+@pytest.mark.buildconfigspec('cmd_tpm_v2')
+def test_tpm2_dam_parameters(u_boot_console):
+ """Execute a TPM2_DictionaryAttackParameters command.
+
+ Change Dictionary Attack Mitigation (DAM) parameters. Ask the TPM to change:
+ - Max number of failed authentication before lockout: 3
+ - Time before the failure counter is automatically decremented: 10 sec
+ - Time after a lockout failure before it can be attempted again: 0 sec
+
+ For an unknown reason, the DAM parameters must be changed before changing
+ the authentication, otherwise the lockout will be engaged after the first
+ failed authentication attempt.
+ """
+
+ force_init(u_boot_console)
+ ram = u_boot_utils.find_ram_base(u_boot_console)
+
+ # Set the DAM parameters to known values
+ u_boot_console.run_command('tpm2 dam_parameters 3 10 0')
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+
+ # Check the values have been saved
+ read_cap = u_boot_console.run_command('tpm2 get_capability 0x6 0x20f 0x%x 3' % ram)
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+ assert 'Property 0x0000020f: 0x00000003' in read_cap
+ assert 'Property 0x00000210: 0x0000000a' in read_cap
+ assert 'Property 0x00000211: 0x00000000' in read_cap
+
+@pytest.mark.buildconfigspec('cmd_tpm_v2')
+def test_tpm2_pcr_read(u_boot_console):
+ """Execute a TPM2_PCR_Read command.
+
+ Perform a PCR read of the 0th PCR. Must be zero.
+ """
+
+ force_init(u_boot_console)
+ ram = u_boot_utils.find_ram_base(u_boot_console)
+
+ read_pcr = u_boot_console.run_command('tpm2 pcr_read 0 0x%x' % ram)
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+
+ # Save the number of PCR updates
+ str = re.findall(r'\d+ known updates', read_pcr)[0]
+ global updates
+ updates = int(re.findall(r'\d+', str)[0])
+
+ # Check the output value
+ assert 'PCR #0 content' in read_pcr
+ assert '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' in read_pcr
+
+@pytest.mark.buildconfigspec('cmd_tpm_v2')
+def test_tpm2_pcr_extend(u_boot_console):
+ """Execute a TPM2_PCR_Extend command.
+
+ Perform a PCR extension with a known hash in memory (zeroed since the board
+ must have been rebooted).
+
+ No authentication mechanism is used here, not protecting against packet
+ replay, yet.
+ """
+
+ force_init(u_boot_console)
+ ram = u_boot_utils.find_ram_base(u_boot_console)
+
+ u_boot_console.run_command('tpm2 pcr_extend 0 0x%x' % ram)
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+
+ read_pcr = u_boot_console.run_command('tpm2 pcr_read 0 0x%x' % ram)
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+ assert 'f5 a5 fd 42 d1 6a 20 30 27 98 ef 6e d3 09 97 9b' in read_pcr
+ assert '43 00 3d 23 20 d9 f0 e8 ea 98 31 a9 27 59 fb 4b' in read_pcr
+
+ str = re.findall(r'\d+ known updates', read_pcr)[0]
+ new_updates = int(re.findall(r'\d+', str)[0])
+ assert (updates + 1) == new_updates
+
+@pytest.mark.buildconfigspec('cmd_tpm_v2')
+def test_tpm2_cleanup(u_boot_console):
+ """Ensure the TPM is cleared from password or test related configuration."""
+
+ force_init(u_boot_console, True)
diff --git a/roms/u-boot/test/py/tests/test_ums.py b/roms/u-boot/test/py/tests/test_ums.py
new file mode 100644
index 000000000..749b16062
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_ums.py
@@ -0,0 +1,236 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved.
+
+# Test U-Boot's "ums" command. The test starts UMS in U-Boot, waits for USB
+# device enumeration on the host, reads a small block of data from the UMS
+# block device, optionally mounts a partition and performs filesystem-based
+# read/write tests, and finally aborts the "ums" command in U-Boot.
+
+import os
+import os.path
+import pytest
+import re
+import time
+import u_boot_utils
+
+"""
+Note: This test relies on:
+
+a) boardenv_* to contain configuration values to define which USB ports are
+available for testing. Without this, this test will be automatically skipped.
+For example:
+
+# Leave this list empty if you have no block_devs below with writable
+# partitions defined.
+env__mount_points = (
+ '/mnt/ubtest-mnt-p2371-2180-na',
+)
+
+env__usb_dev_ports = (
+ {
+ 'fixture_id': 'micro_b',
+ 'tgt_usb_ctlr': '0',
+ 'host_ums_dev_node': '/dev/disk/by-path/pci-0000:00:14.0-usb-0:13:1.0-scsi-0:0:0:0',
+ },
+)
+
+env__block_devs = (
+ # eMMC; always present
+ {
+ 'fixture_id': 'emmc',
+ 'type': 'mmc',
+ 'id': '0',
+ # The following two properties are optional.
+ # If present, the partition will be mounted and a file written-to and
+ # read-from it. If missing, only a simple block read test will be
+ # performed.
+ 'writable_fs_partition': 1,
+ 'writable_fs_subdir': 'tmp/',
+ },
+ # SD card; present since I plugged one in
+ {
+ 'fixture_id': 'sd',
+ 'type': 'mmc',
+ 'id': '1'
+ },
+)
+
+b) udev rules to set permissions on devices nodes, so that sudo is not
+required. For example:
+
+ACTION=="add", SUBSYSTEM=="block", SUBSYSTEMS=="usb", KERNELS=="3-13", MODE:="666"
+
+(You may wish to change the group ID instead of setting the permissions wide
+open. All that matters is that the user ID running the test can access the
+device.)
+
+c) /etc/fstab entries to allow the block device to be mounted without requiring
+root permissions. For example:
+
+/dev/disk/by-path/pci-0000:00:14.0-usb-0:13:1.0-scsi-0:0:0:0-part1 /mnt/ubtest-mnt-p2371-2180-na ext4 noauto,user,nosuid,nodev
+
+This entry is only needed if any block_devs above contain a
+writable_fs_partition value.
+"""
+
+@pytest.mark.buildconfigspec('cmd_usb_mass_storage')
+def test_ums(u_boot_console, env__usb_dev_port, env__block_devs):
+ """Test the "ums" command; the host system must be able to enumerate a UMS
+ device when "ums" is running, block and optionally file I/O are tested,
+ and this device must disappear when "ums" is aborted.
+
+ Args:
+ u_boot_console: A U-Boot console connection.
+ env__usb_dev_port: The single USB device-mode port specification on
+ which to run the test. See the file-level comment above for
+ details of the format.
+ env__block_devs: The list of block devices that the target U-Boot
+ device has attached. See the file-level comment above for details
+ of the format.
+
+ Returns:
+ Nothing.
+ """
+
+ have_writable_fs_partition = 'writable_fs_partition' in env__block_devs[0]
+ if not have_writable_fs_partition:
+ # If 'writable_fs_subdir' is missing, we'll skip all parts of the
+ # testing which mount filesystems.
+ u_boot_console.log.warning(
+ 'boardenv missing "writable_fs_partition"; ' +
+ 'UMS testing will be limited.')
+
+ tgt_usb_ctlr = env__usb_dev_port['tgt_usb_ctlr']
+ host_ums_dev_node = env__usb_dev_port['host_ums_dev_node']
+
+ # We're interested in testing USB device mode on each port, not the cross-
+ # product of that with each device. So, just pick the first entry in the
+ # device list here. We'll test each block device somewhere else.
+ tgt_dev_type = env__block_devs[0]['type']
+ tgt_dev_id = env__block_devs[0]['id']
+ if have_writable_fs_partition:
+ mount_point = u_boot_console.config.env['env__mount_points'][0]
+ mount_subdir = env__block_devs[0]['writable_fs_subdir']
+ part_num = env__block_devs[0]['writable_fs_partition']
+ host_ums_part_node = '%s-part%d' % (host_ums_dev_node, part_num)
+ else:
+ host_ums_part_node = host_ums_dev_node
+
+ test_f = u_boot_utils.PersistentRandomFile(u_boot_console, 'ums.bin',
+ 1024 * 1024);
+ if have_writable_fs_partition:
+ mounted_test_fn = mount_point + '/' + mount_subdir + test_f.fn
+
+ def start_ums():
+ """Start U-Boot's ums shell command.
+
+ This also waits for the host-side USB enumeration process to complete.
+
+ Args:
+ None.
+
+ Returns:
+ Nothing.
+ """
+
+ u_boot_console.log.action(
+ 'Starting long-running U-Boot ums shell command')
+ cmd = 'ums %s %s %s' % (tgt_usb_ctlr, tgt_dev_type, tgt_dev_id)
+ u_boot_console.run_command(cmd, wait_for_prompt=False)
+ u_boot_console.wait_for(re.compile('UMS: LUN.*[\r\n]'))
+ fh = u_boot_utils.wait_until_open_succeeds(host_ums_part_node)
+ u_boot_console.log.action('Reading raw data from UMS device')
+ fh.read(4096)
+ fh.close()
+
+ def mount():
+ """Mount the block device that U-Boot exports.
+
+ Args:
+ None.
+
+ Returns:
+ Nothing.
+ """
+
+ u_boot_console.log.action('Mounting exported UMS device')
+ cmd = ('/bin/mount', host_ums_part_node)
+ u_boot_utils.run_and_log(u_boot_console, cmd)
+
+ def umount(ignore_errors):
+ """Unmount the block device that U-Boot exports.
+
+ Args:
+ ignore_errors: Ignore any errors. This is useful if an error has
+ already been detected, and the code is performing best-effort
+ cleanup. In this case, we do not want to mask the original
+ error by "honoring" any new errors.
+
+ Returns:
+ Nothing.
+ """
+
+ u_boot_console.log.action('Unmounting UMS device')
+ cmd = ('/bin/umount', host_ums_part_node)
+ u_boot_utils.run_and_log(u_boot_console, cmd, ignore_errors)
+
+ def stop_ums(ignore_errors):
+ """Stop U-Boot's ums shell command from executing.
+
+ This also waits for the host-side USB de-enumeration process to
+ complete.
+
+ Args:
+ ignore_errors: Ignore any errors. This is useful if an error has
+ already been detected, and the code is performing best-effort
+ cleanup. In this case, we do not want to mask the original
+ error by "honoring" any new errors.
+
+ Returns:
+ Nothing.
+ """
+
+ u_boot_console.log.action(
+ 'Stopping long-running U-Boot ums shell command')
+ u_boot_console.ctrlc()
+ u_boot_utils.wait_until_file_open_fails(host_ums_part_node,
+ ignore_errors)
+
+ ignore_cleanup_errors = True
+ try:
+ start_ums()
+ if not have_writable_fs_partition:
+ # Skip filesystem-based testing if not configured
+ return
+ try:
+ mount()
+ u_boot_console.log.action('Writing test file via UMS')
+ cmd = ('rm', '-f', mounted_test_fn)
+ u_boot_utils.run_and_log(u_boot_console, cmd)
+ if os.path.exists(mounted_test_fn):
+ raise Exception('Could not rm target UMS test file')
+ cmd = ('cp', test_f.abs_fn, mounted_test_fn)
+ u_boot_utils.run_and_log(u_boot_console, cmd)
+ ignore_cleanup_errors = False
+ finally:
+ umount(ignore_errors=ignore_cleanup_errors)
+ finally:
+ stop_ums(ignore_errors=ignore_cleanup_errors)
+
+ ignore_cleanup_errors = True
+ try:
+ start_ums()
+ try:
+ mount()
+ u_boot_console.log.action('Reading test file back via UMS')
+ read_back_hash = u_boot_utils.md5sum_file(mounted_test_fn)
+ cmd = ('rm', '-f', mounted_test_fn)
+ u_boot_utils.run_and_log(u_boot_console, cmd)
+ ignore_cleanup_errors = False
+ finally:
+ umount(ignore_errors=ignore_cleanup_errors)
+ finally:
+ stop_ums(ignore_errors=ignore_cleanup_errors)
+
+ written_hash = test_f.content_hash
+ assert(written_hash == read_back_hash)
diff --git a/roms/u-boot/test/py/tests/test_unknown_cmd.py b/roms/u-boot/test/py/tests/test_unknown_cmd.py
new file mode 100644
index 000000000..8fc284a92
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_unknown_cmd.py
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2015 Stephen Warren
+# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
+
+def test_unknown_command(u_boot_console):
+ """Test that executing an unknown command causes U-Boot to print an
+ error."""
+
+ # The "unknown command" error is actively expected here,
+ # so error detection for it is disabled.
+ with u_boot_console.disable_check('unknown_command'):
+ response = u_boot_console.run_command('non_existent_cmd')
+ assert('Unknown command \'non_existent_cmd\' - try \'help\'' in response)
diff --git a/roms/u-boot/test/py/tests/test_ut.py b/roms/u-boot/test/py/tests/test_ut.py
new file mode 100644
index 000000000..01c2b3ffa
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_ut.py
@@ -0,0 +1,43 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
+
+import os.path
+import pytest
+
+@pytest.mark.buildconfigspec('ut_dm')
+def test_ut_dm_init(u_boot_console):
+ """Initialize data for ut dm tests."""
+
+ fn = u_boot_console.config.source_dir + '/testflash.bin'
+ if not os.path.exists(fn):
+ data = b'this is a test'
+ data += b'\x00' * ((4 * 1024 * 1024) - len(data))
+ with open(fn, 'wb') as fh:
+ fh.write(data)
+
+ fn = u_boot_console.config.source_dir + '/spi.bin'
+ if not os.path.exists(fn):
+ data = b'\x00' * (2 * 1024 * 1024)
+ with open(fn, 'wb') as fh:
+ fh.write(data)
+
+def test_ut(u_boot_console, ut_subtest):
+ """Execute a "ut" subtest.
+
+ The subtests are collected in function generate_ut_subtest() from linker
+ generated lists by applying a regular expression to the lines of file
+ u-boot.sym. The list entries are created using the C macro UNIT_TEST().
+
+ Strict naming conventions have to be followed to match the regular
+ expression. Use UNIT_TEST(foo_test_bar, _flags, foo_test) for a test bar in
+ test suite foo that can be executed via command 'ut foo bar' and is
+ implemented in C function foo_test_bar().
+
+ Args:
+ u_boot_console (ConsoleBase): U-Boot console
+ ut_subtest (str): test to be executed via command ut, e.g 'foo bar' to
+ execute command 'ut foo bar'
+ """
+
+ output = u_boot_console.run_command('ut ' + ut_subtest)
+ assert output.endswith('Failures: 0')
diff --git a/roms/u-boot/test/py/tests/test_vboot.py b/roms/u-boot/test/py/tests/test_vboot.py
new file mode 100644
index 000000000..6dff6779d
--- /dev/null
+++ b/roms/u-boot/test/py/tests/test_vboot.py
@@ -0,0 +1,401 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2016, Google Inc.
+#
+# U-Boot Verified Boot Test
+
+"""
+This tests verified boot in the following ways:
+
+For image verification:
+- Create FIT (unsigned) with mkimage
+- Check that verification shows that no keys are verified
+- Sign image
+- Check that verification shows that a key is now verified
+
+For configuration verification:
+- Corrupt signature and check for failure
+- Create FIT (with unsigned configuration) with mkimage
+- Check that image verification works
+- Sign the FIT and mark the key as 'required' for verification
+- Check that image verification works
+- Corrupt the signature
+- Check that image verification no-longer works
+
+Tests run with both SHA1 and SHA256 hashing.
+"""
+
+import shutil
+import struct
+import pytest
+import u_boot_utils as util
+import vboot_forge
+import vboot_evil
+
+# Only run the full suite on a few combinations, since it doesn't add any more
+# test coverage.
+TESTDATA = [
+ ['sha1', '', None, False, True],
+ ['sha1', '', '-E -p 0x10000', False, False],
+ ['sha1', '-pss', None, False, False],
+ ['sha1', '-pss', '-E -p 0x10000', False, False],
+ ['sha256', '', None, False, False],
+ ['sha256', '', '-E -p 0x10000', False, False],
+ ['sha256', '-pss', None, False, False],
+ ['sha256', '-pss', '-E -p 0x10000', False, False],
+ ['sha256', '-pss', None, True, False],
+ ['sha256', '-pss', '-E -p 0x10000', True, True],
+]
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('fit_signature')
+@pytest.mark.requiredtool('dtc')
+@pytest.mark.requiredtool('fdtget')
+@pytest.mark.requiredtool('fdtput')
+@pytest.mark.requiredtool('openssl')
+@pytest.mark.parametrize("sha_algo,padding,sign_options,required,full_test",
+ TESTDATA)
+def test_vboot(u_boot_console, sha_algo, padding, sign_options, required,
+ full_test):
+ """Test verified boot signing with mkimage and verification with 'bootm'.
+
+ This works using sandbox only as it needs to update the device tree used
+ by U-Boot to hold public keys from the signing process.
+
+ The SHA1 and SHA256 tests are combined into a single test since the
+ key-generation process is quite slow and we want to avoid doing it twice.
+ """
+ def dtc(dts):
+ """Run the device tree compiler to compile a .dts file
+
+ The output file will be the same as the input file but with a .dtb
+ extension.
+
+ Args:
+ dts: Device tree file to compile.
+ """
+ dtb = dts.replace('.dts', '.dtb')
+ util.run_and_log(cons, 'dtc %s %s%s -O dtb '
+ '-o %s%s' % (dtc_args, datadir, dts, tmpdir, dtb))
+
+ def run_bootm(sha_algo, test_type, expect_string, boots, fit=None):
+ """Run a 'bootm' command U-Boot.
+
+ This always starts a fresh U-Boot instance since the device tree may
+ contain a new public key.
+
+ Args:
+ test_type: A string identifying the test type.
+ expect_string: A string which is expected in the output.
+ sha_algo: Either 'sha1' or 'sha256', to select the algorithm to
+ use.
+ boots: A boolean that is True if Linux should boot and False if
+ we are expected to not boot
+ fit: FIT filename to load and verify
+ """
+ if not fit:
+ fit = '%stest.fit' % tmpdir
+ cons.restart_uboot()
+ with cons.log.section('Verified boot %s %s' % (sha_algo, test_type)):
+ output = cons.run_command_list(
+ ['host load hostfs - 100 %s' % fit,
+ 'fdt addr 100',
+ 'bootm 100'])
+ assert expect_string in ''.join(output)
+ if boots:
+ assert 'sandbox: continuing, as we cannot run' in ''.join(output)
+ else:
+ assert('sandbox: continuing, as we cannot run'
+ not in ''.join(output))
+
+ def make_fit(its):
+ """Make a new FIT from the .its source file.
+
+ This runs 'mkimage -f' to create a new FIT.
+
+ Args:
+ its: Filename containing .its source.
+ """
+ util.run_and_log(cons, [mkimage, '-D', dtc_args, '-f',
+ '%s%s' % (datadir, its), fit])
+
+ def sign_fit(sha_algo, options):
+ """Sign the FIT
+
+ Signs the FIT and writes the signature into it. It also writes the
+ public key into the dtb.
+
+ Args:
+ sha_algo: Either 'sha1' or 'sha256', to select the algorithm to
+ use.
+ options: Options to provide to mkimage.
+ """
+ args = [mkimage, '-F', '-k', tmpdir, '-K', dtb, '-r', fit]
+ if options:
+ args += options.split(' ')
+ cons.log.action('%s: Sign images' % sha_algo)
+ util.run_and_log(cons, args)
+
+ def sign_fit_norequire(sha_algo, options):
+ """Sign the FIT
+
+ Signs the FIT and writes the signature into it. It also writes the
+ public key into the dtb. It does not mark key as 'required' in dtb.
+
+ Args:
+ sha_algo: Either 'sha1' or 'sha256', to select the algorithm to
+ use.
+ options: Options to provide to mkimage.
+ """
+ args = [mkimage, '-F', '-k', tmpdir, '-K', dtb, fit]
+ if options:
+ args += options.split(' ')
+ cons.log.action('%s: Sign images' % sha_algo)
+ util.run_and_log(cons, args)
+
+ def replace_fit_totalsize(size):
+ """Replace FIT header's totalsize with something greater.
+
+ The totalsize must be less than or equal to FIT_SIGNATURE_MAX_SIZE.
+ If the size is greater, the signature verification should return false.
+
+ Args:
+ size: The new totalsize of the header
+
+ Returns:
+ prev_size: The previous totalsize read from the header
+ """
+ total_size = 0
+ with open(fit, 'r+b') as handle:
+ handle.seek(4)
+ total_size = handle.read(4)
+ handle.seek(4)
+ handle.write(struct.pack(">I", size))
+ return struct.unpack(">I", total_size)[0]
+
+ def create_rsa_pair(name):
+ """Generate a new RSA key paid and certificate
+
+ Args:
+ name: Name of of the key (e.g. 'dev')
+ """
+ public_exponent = 65537
+ util.run_and_log(cons, 'openssl genpkey -algorithm RSA -out %s%s.key '
+ '-pkeyopt rsa_keygen_bits:2048 '
+ '-pkeyopt rsa_keygen_pubexp:%d' %
+ (tmpdir, name, public_exponent))
+
+ # Create a certificate containing the public key
+ util.run_and_log(cons, 'openssl req -batch -new -x509 -key %s%s.key '
+ '-out %s%s.crt' % (tmpdir, name, tmpdir, name))
+
+ def test_with_algo(sha_algo, padding, sign_options):
+ """Test verified boot with the given hash algorithm.
+
+ This is the main part of the test code. The same procedure is followed
+ for both hashing algorithms.
+
+ Args:
+ sha_algo: Either 'sha1' or 'sha256', to select the algorithm to
+ use.
+ padding: Either '' or '-pss', to select the padding to use for the
+ rsa signature algorithm.
+ sign_options: Options to mkimage when signing a fit image.
+ """
+ # Compile our device tree files for kernel and U-Boot. These are
+ # regenerated here since mkimage will modify them (by adding a
+ # public key) below.
+ dtc('sandbox-kernel.dts')
+ dtc('sandbox-u-boot.dts')
+
+ # Build the FIT, but don't sign anything yet
+ cons.log.action('%s: Test FIT with signed images' % sha_algo)
+ make_fit('sign-images-%s%s.its' % (sha_algo, padding))
+ run_bootm(sha_algo, 'unsigned images', 'dev-', True)
+
+ # Sign images with our dev keys
+ sign_fit(sha_algo, sign_options)
+ run_bootm(sha_algo, 'signed images', 'dev+', True)
+
+ # Create a fresh .dtb without the public keys
+ dtc('sandbox-u-boot.dts')
+
+ cons.log.action('%s: Test FIT with signed configuration' % sha_algo)
+ make_fit('sign-configs-%s%s.its' % (sha_algo, padding))
+ run_bootm(sha_algo, 'unsigned config', '%s+ OK' % sha_algo, True)
+
+ # Sign images with our dev keys
+ sign_fit(sha_algo, sign_options)
+ run_bootm(sha_algo, 'signed config', 'dev+', True)
+
+ cons.log.action('%s: Check signed config on the host' % sha_algo)
+
+ util.run_and_log(cons, [fit_check_sign, '-f', fit, '-k', dtb])
+
+ if full_test:
+ # Make sure that U-Boot checks that the config is in the list of
+ # hashed nodes. If it isn't, a security bypass is possible.
+ ffit = '%stest.forged.fit' % tmpdir
+ shutil.copyfile(fit, ffit)
+ with open(ffit, 'rb') as fd:
+ root, strblock = vboot_forge.read_fdt(fd)
+ root, strblock = vboot_forge.manipulate(root, strblock)
+ with open(ffit, 'w+b') as fd:
+ vboot_forge.write_fdt(root, strblock, fd)
+ util.run_and_log_expect_exception(
+ cons, [fit_check_sign, '-f', ffit, '-k', dtb],
+ 1, 'Failed to verify required signature')
+
+ run_bootm(sha_algo, 'forged config', 'Bad Data Hash', False, ffit)
+
+ # Try adding an evil root node. This should be detected.
+ efit = '%stest.evilf.fit' % tmpdir
+ shutil.copyfile(fit, efit)
+ vboot_evil.add_evil_node(fit, efit, evil_kernel, 'fakeroot')
+
+ util.run_and_log_expect_exception(
+ cons, [fit_check_sign, '-f', efit, '-k', dtb],
+ 1, 'Failed to verify required signature')
+ run_bootm(sha_algo, 'evil fakeroot', 'Bad FIT kernel image format',
+ False, efit)
+
+ # Try adding an @ to the kernel node name. This should be detected.
+ efit = '%stest.evilk.fit' % tmpdir
+ shutil.copyfile(fit, efit)
+ vboot_evil.add_evil_node(fit, efit, evil_kernel, 'kernel@')
+
+ msg = 'Signature checking prevents use of unit addresses (@) in nodes'
+ util.run_and_log_expect_exception(
+ cons, [fit_check_sign, '-f', efit, '-k', dtb],
+ 1, msg)
+ run_bootm(sha_algo, 'evil kernel@', msg, False, efit)
+
+ # Create a new properly signed fit and replace header bytes
+ make_fit('sign-configs-%s%s.its' % (sha_algo, padding))
+ sign_fit(sha_algo, sign_options)
+ bcfg = u_boot_console.config.buildconfig
+ max_size = int(bcfg.get('config_fit_signature_max_size', 0x10000000), 0)
+ existing_size = replace_fit_totalsize(max_size + 1)
+ run_bootm(sha_algo, 'Signed config with bad hash', 'Bad Data Hash',
+ False)
+ cons.log.action('%s: Check overflowed FIT header totalsize' % sha_algo)
+
+ # Replace with existing header bytes
+ replace_fit_totalsize(existing_size)
+ run_bootm(sha_algo, 'signed config', 'dev+', True)
+ cons.log.action('%s: Check default FIT header totalsize' % sha_algo)
+
+ # Increment the first byte of the signature, which should cause failure
+ sig = util.run_and_log(cons, 'fdtget -t bx %s %s value' %
+ (fit, sig_node))
+ byte_list = sig.split()
+ byte = int(byte_list[0], 16)
+ byte_list[0] = '%x' % (byte + 1)
+ sig = ' '.join(byte_list)
+ util.run_and_log(cons, 'fdtput -t bx %s %s value %s' %
+ (fit, sig_node, sig))
+
+ run_bootm(sha_algo, 'Signed config with bad hash', 'Bad Data Hash',
+ False)
+
+ cons.log.action('%s: Check bad config on the host' % sha_algo)
+ util.run_and_log_expect_exception(
+ cons, [fit_check_sign, '-f', fit, '-k', dtb],
+ 1, 'Failed to verify required signature')
+
+ def test_required_key(sha_algo, padding, sign_options):
+ """Test verified boot with the given hash algorithm.
+
+ This function tests if U-Boot rejects an image when a required key isn't
+ used to sign a FIT.
+
+ Args:
+ sha_algo: Either 'sha1' or 'sha256', to select the algorithm to use
+ padding: Either '' or '-pss', to select the padding to use for the
+ rsa signature algorithm.
+ sign_options: Options to mkimage when signing a fit image.
+ """
+ # Compile our device tree files for kernel and U-Boot. These are
+ # regenerated here since mkimage will modify them (by adding a
+ # public key) below.
+ dtc('sandbox-kernel.dts')
+ dtc('sandbox-u-boot.dts')
+
+ cons.log.action('%s: Test FIT with configs images' % sha_algo)
+
+ # Build the FIT with prod key (keys required) and sign it. This puts the
+ # signature into sandbox-u-boot.dtb, marked 'required'
+ make_fit('sign-configs-%s%s-prod.its' % (sha_algo, padding))
+ sign_fit(sha_algo, sign_options)
+
+ # Build the FIT with dev key (keys NOT required). This adds the
+ # signature into sandbox-u-boot.dtb, NOT marked 'required'.
+ make_fit('sign-configs-%s%s.its' % (sha_algo, padding))
+ sign_fit_norequire(sha_algo, sign_options)
+
+ # So now sandbox-u-boot.dtb two signatures, for the prod and dev keys.
+ # Only the prod key is set as 'required'. But FIT we just built has
+ # a dev signature only (sign_fit_norequire() overwrites the FIT).
+ # Try to boot the FIT with dev key. This FIT should not be accepted by
+ # U-Boot because the prod key is required.
+ run_bootm(sha_algo, 'required key', '', False)
+
+ # Build the FIT with dev key (keys required) and sign it. This puts the
+ # signature into sandbox-u-boot.dtb, marked 'required'.
+ make_fit('sign-configs-%s%s.its' % (sha_algo, padding))
+ sign_fit(sha_algo, sign_options)
+
+ # Set the required-mode policy to "any".
+ # So now sandbox-u-boot.dtb two signatures, for the prod and dev keys.
+ # Both the dev and prod key are set as 'required'. But FIT we just built has
+ # a dev signature only (sign_fit() overwrites the FIT).
+ # Try to boot the FIT with dev key. This FIT should be accepted by
+ # U-Boot because the dev key is required and policy is "any" required key.
+ util.run_and_log(cons, 'fdtput -t s %s /signature required-mode any' %
+ (dtb))
+ run_bootm(sha_algo, 'multi required key', 'dev+', True)
+
+ # Set the required-mode policy to "all".
+ # So now sandbox-u-boot.dtb two signatures, for the prod and dev keys.
+ # Both the dev and prod key are set as 'required'. But FIT we just built has
+ # a dev signature only (sign_fit() overwrites the FIT).
+ # Try to boot the FIT with dev key. This FIT should not be accepted by
+ # U-Boot because the prod key is required and policy is "all" required key
+ util.run_and_log(cons, 'fdtput -t s %s /signature required-mode all' %
+ (dtb))
+ run_bootm(sha_algo, 'multi required key', '', False)
+
+ cons = u_boot_console
+ tmpdir = cons.config.result_dir + '/'
+ datadir = cons.config.source_dir + '/test/py/tests/vboot/'
+ fit = '%stest.fit' % tmpdir
+ mkimage = cons.config.build_dir + '/tools/mkimage'
+ fit_check_sign = cons.config.build_dir + '/tools/fit_check_sign'
+ dtc_args = '-I dts -O dtb -i %s' % tmpdir
+ dtb = '%ssandbox-u-boot.dtb' % tmpdir
+ sig_node = '/configurations/conf-1/signature'
+
+ create_rsa_pair('dev')
+ create_rsa_pair('prod')
+
+ # Create a number kernel image with zeroes
+ with open('%stest-kernel.bin' % tmpdir, 'wb') as fd:
+ fd.write(500 * b'\0')
+
+ # Create a second kernel image with ones
+ evil_kernel = '%stest-kernel1.bin' % tmpdir
+ with open(evil_kernel, 'wb') as fd:
+ fd.write(500 * b'\x01')
+
+ try:
+ # We need to use our own device tree file. Remember to restore it
+ # afterwards.
+ old_dtb = cons.config.dtb
+ cons.config.dtb = dtb
+ if required:
+ test_required_key(sha_algo, padding, sign_options)
+ else:
+ test_with_algo(sha_algo, padding, sign_options)
+ finally:
+ # Go back to the original U-Boot with the correct dtb.
+ cons.config.dtb = old_dtb
+ cons.restart_uboot()
diff --git a/roms/u-boot/test/py/tests/vboot/sandbox-kernel.dts b/roms/u-boot/test/py/tests/vboot/sandbox-kernel.dts
new file mode 100644
index 000000000..a1e853c9c
--- /dev/null
+++ b/roms/u-boot/test/py/tests/vboot/sandbox-kernel.dts
@@ -0,0 +1,7 @@
+/dts-v1/;
+
+/ {
+ model = "Sandbox Verified Boot Test";
+ compatible = "sandbox";
+
+};
diff --git a/roms/u-boot/test/py/tests/vboot/sandbox-u-boot.dts b/roms/u-boot/test/py/tests/vboot/sandbox-u-boot.dts
new file mode 100644
index 000000000..63f8f401d
--- /dev/null
+++ b/roms/u-boot/test/py/tests/vboot/sandbox-u-boot.dts
@@ -0,0 +1,10 @@
+/dts-v1/;
+
+/ {
+ model = "Sandbox Verified Boot Test";
+ compatible = "sandbox";
+
+ reset@0 {
+ compatible = "sandbox,reset";
+ };
+};
diff --git a/roms/u-boot/test/py/tests/vboot/sign-configs-sha1-pss.its b/roms/u-boot/test/py/tests/vboot/sign-configs-sha1-pss.its
new file mode 100644
index 000000000..72a5637e3
--- /dev/null
+++ b/roms/u-boot/test/py/tests/vboot/sign-configs-sha1-pss.its
@@ -0,0 +1,46 @@
+/dts-v1/;
+
+/ {
+ description = "Chrome OS kernel image with one or more FDT blobs";
+ #address-cells = <1>;
+
+ images {
+ kernel {
+ data = /incbin/("test-kernel.bin");
+ type = "kernel_noload";
+ arch = "sandbox";
+ os = "linux";
+ compression = "none";
+ load = <0x4>;
+ entry = <0x8>;
+ kernel-version = <1>;
+ hash-1 {
+ algo = "sha1";
+ };
+ };
+ fdt-1 {
+ description = "snow";
+ data = /incbin/("sandbox-kernel.dtb");
+ type = "flat_dt";
+ arch = "sandbox";
+ compression = "none";
+ fdt-version = <1>;
+ hash-1 {
+ algo = "sha1";
+ };
+ };
+ };
+ configurations {
+ default = "conf-1";
+ conf-1 {
+ kernel = "kernel";
+ fdt = "fdt-1";
+ signature {
+ algo = "sha1,rsa2048";
+ padding = "pss";
+ key-name-hint = "dev";
+ sign-images = "fdt", "kernel";
+ };
+ };
+ };
+};
diff --git a/roms/u-boot/test/py/tests/vboot/sign-configs-sha1.its b/roms/u-boot/test/py/tests/vboot/sign-configs-sha1.its
new file mode 100644
index 000000000..d8bc1fa09
--- /dev/null
+++ b/roms/u-boot/test/py/tests/vboot/sign-configs-sha1.its
@@ -0,0 +1,45 @@
+/dts-v1/;
+
+/ {
+ description = "Chrome OS kernel image with one or more FDT blobs";
+ #address-cells = <1>;
+
+ images {
+ kernel {
+ data = /incbin/("test-kernel.bin");
+ type = "kernel_noload";
+ arch = "sandbox";
+ os = "linux";
+ compression = "none";
+ load = <0x4>;
+ entry = <0x8>;
+ kernel-version = <1>;
+ hash-1 {
+ algo = "sha1";
+ };
+ };
+ fdt-1 {
+ description = "snow";
+ data = /incbin/("sandbox-kernel.dtb");
+ type = "flat_dt";
+ arch = "sandbox";
+ compression = "none";
+ fdt-version = <1>;
+ hash-1 {
+ algo = "sha1";
+ };
+ };
+ };
+ configurations {
+ default = "conf-1";
+ conf-1 {
+ kernel = "kernel";
+ fdt = "fdt-1";
+ signature {
+ algo = "sha1,rsa2048";
+ key-name-hint = "dev";
+ sign-images = "fdt", "kernel";
+ };
+ };
+ };
+};
diff --git a/roms/u-boot/test/py/tests/vboot/sign-configs-sha256-pss-prod.its b/roms/u-boot/test/py/tests/vboot/sign-configs-sha256-pss-prod.its
new file mode 100644
index 000000000..aac732e30
--- /dev/null
+++ b/roms/u-boot/test/py/tests/vboot/sign-configs-sha256-pss-prod.its
@@ -0,0 +1,46 @@
+/dts-v1/;
+
+/ {
+ description = "Chrome OS kernel image with one or more FDT blobs";
+ #address-cells = <1>;
+
+ images {
+ kernel {
+ data = /incbin/("test-kernel.bin");
+ type = "kernel_noload";
+ arch = "sandbox";
+ os = "linux";
+ compression = "none";
+ load = <0x4>;
+ entry = <0x8>;
+ kernel-version = <1>;
+ hash-1 {
+ algo = "sha256";
+ };
+ };
+ fdt-1 {
+ description = "snow";
+ data = /incbin/("sandbox-kernel.dtb");
+ type = "flat_dt";
+ arch = "sandbox";
+ compression = "none";
+ fdt-version = <1>;
+ hash-1 {
+ algo = "sha256";
+ };
+ };
+ };
+ configurations {
+ default = "conf-1";
+ conf-1 {
+ kernel = "kernel";
+ fdt = "fdt-1";
+ signature {
+ algo = "sha256,rsa2048";
+ padding = "pss";
+ key-name-hint = "prod";
+ sign-images = "fdt", "kernel";
+ };
+ };
+ };
+};
diff --git a/roms/u-boot/test/py/tests/vboot/sign-configs-sha256-pss.its b/roms/u-boot/test/py/tests/vboot/sign-configs-sha256-pss.its
new file mode 100644
index 000000000..7bdcc7e28
--- /dev/null
+++ b/roms/u-boot/test/py/tests/vboot/sign-configs-sha256-pss.its
@@ -0,0 +1,46 @@
+/dts-v1/;
+
+/ {
+ description = "Chrome OS kernel image with one or more FDT blobs";
+ #address-cells = <1>;
+
+ images {
+ kernel {
+ data = /incbin/("test-kernel.bin");
+ type = "kernel_noload";
+ arch = "sandbox";
+ os = "linux";
+ compression = "none";
+ load = <0x4>;
+ entry = <0x8>;
+ kernel-version = <1>;
+ hash-1 {
+ algo = "sha256";
+ };
+ };
+ fdt-1 {
+ description = "snow";
+ data = /incbin/("sandbox-kernel.dtb");
+ type = "flat_dt";
+ arch = "sandbox";
+ compression = "none";
+ fdt-version = <1>;
+ hash-1 {
+ algo = "sha256";
+ };
+ };
+ };
+ configurations {
+ default = "conf-1";
+ conf-1 {
+ kernel = "kernel";
+ fdt = "fdt-1";
+ signature {
+ algo = "sha256,rsa2048";
+ padding = "pss";
+ key-name-hint = "dev";
+ sign-images = "fdt", "kernel";
+ };
+ };
+ };
+};
diff --git a/roms/u-boot/test/py/tests/vboot/sign-configs-sha256.its b/roms/u-boot/test/py/tests/vboot/sign-configs-sha256.its
new file mode 100644
index 000000000..f5591aad3
--- /dev/null
+++ b/roms/u-boot/test/py/tests/vboot/sign-configs-sha256.its
@@ -0,0 +1,45 @@
+/dts-v1/;
+
+/ {
+ description = "Chrome OS kernel image with one or more FDT blobs";
+ #address-cells = <1>;
+
+ images {
+ kernel {
+ data = /incbin/("test-kernel.bin");
+ type = "kernel_noload";
+ arch = "sandbox";
+ os = "linux";
+ compression = "none";
+ load = <0x4>;
+ entry = <0x8>;
+ kernel-version = <1>;
+ hash-1 {
+ algo = "sha256";
+ };
+ };
+ fdt-1 {
+ description = "snow";
+ data = /incbin/("sandbox-kernel.dtb");
+ type = "flat_dt";
+ arch = "sandbox";
+ compression = "none";
+ fdt-version = <1>;
+ hash-1 {
+ algo = "sha256";
+ };
+ };
+ };
+ configurations {
+ default = "conf-1";
+ conf-1 {
+ kernel = "kernel";
+ fdt = "fdt-1";
+ signature {
+ algo = "sha256,rsa2048";
+ key-name-hint = "dev";
+ sign-images = "fdt", "kernel";
+ };
+ };
+ };
+};
diff --git a/roms/u-boot/test/py/tests/vboot/sign-images-sha1-pss.its b/roms/u-boot/test/py/tests/vboot/sign-images-sha1-pss.its
new file mode 100644
index 000000000..ded7ae4f5
--- /dev/null
+++ b/roms/u-boot/test/py/tests/vboot/sign-images-sha1-pss.its
@@ -0,0 +1,44 @@
+/dts-v1/;
+
+/ {
+ description = "Chrome OS kernel image with one or more FDT blobs";
+ #address-cells = <1>;
+
+ images {
+ kernel {
+ data = /incbin/("test-kernel.bin");
+ type = "kernel_noload";
+ arch = "sandbox";
+ os = "linux";
+ compression = "none";
+ load = <0x4>;
+ entry = <0x8>;
+ kernel-version = <1>;
+ signature {
+ algo = "sha1,rsa2048";
+ padding = "pss";
+ key-name-hint = "dev";
+ };
+ };
+ fdt-1 {
+ description = "snow";
+ data = /incbin/("sandbox-kernel.dtb");
+ type = "flat_dt";
+ arch = "sandbox";
+ compression = "none";
+ fdt-version = <1>;
+ signature {
+ algo = "sha1,rsa2048";
+ padding = "pss";
+ key-name-hint = "dev";
+ };
+ };
+ };
+ configurations {
+ default = "conf-1";
+ conf-1 {
+ kernel = "kernel";
+ fdt = "fdt-1";
+ };
+ };
+};
diff --git a/roms/u-boot/test/py/tests/vboot/sign-images-sha1.its b/roms/u-boot/test/py/tests/vboot/sign-images-sha1.its
new file mode 100644
index 000000000..18c759e9e
--- /dev/null
+++ b/roms/u-boot/test/py/tests/vboot/sign-images-sha1.its
@@ -0,0 +1,42 @@
+/dts-v1/;
+
+/ {
+ description = "Chrome OS kernel image with one or more FDT blobs";
+ #address-cells = <1>;
+
+ images {
+ kernel {
+ data = /incbin/("test-kernel.bin");
+ type = "kernel_noload";
+ arch = "sandbox";
+ os = "linux";
+ compression = "none";
+ load = <0x4>;
+ entry = <0x8>;
+ kernel-version = <1>;
+ signature {
+ algo = "sha1,rsa2048";
+ key-name-hint = "dev";
+ };
+ };
+ fdt-1 {
+ description = "snow";
+ data = /incbin/("sandbox-kernel.dtb");
+ type = "flat_dt";
+ arch = "sandbox";
+ compression = "none";
+ fdt-version = <1>;
+ signature {
+ algo = "sha1,rsa2048";
+ key-name-hint = "dev";
+ };
+ };
+ };
+ configurations {
+ default = "conf-1";
+ conf-1 {
+ kernel = "kernel";
+ fdt = "fdt-1";
+ };
+ };
+};
diff --git a/roms/u-boot/test/py/tests/vboot/sign-images-sha256-pss.its b/roms/u-boot/test/py/tests/vboot/sign-images-sha256-pss.its
new file mode 100644
index 000000000..34850cc6c
--- /dev/null
+++ b/roms/u-boot/test/py/tests/vboot/sign-images-sha256-pss.its
@@ -0,0 +1,44 @@
+/dts-v1/;
+
+/ {
+ description = "Chrome OS kernel image with one or more FDT blobs";
+ #address-cells = <1>;
+
+ images {
+ kernel {
+ data = /incbin/("test-kernel.bin");
+ type = "kernel_noload";
+ arch = "sandbox";
+ os = "linux";
+ compression = "none";
+ load = <0x4>;
+ entry = <0x8>;
+ kernel-version = <1>;
+ signature {
+ algo = "sha256,rsa2048";
+ padding = "pss";
+ key-name-hint = "dev";
+ };
+ };
+ fdt-1 {
+ description = "snow";
+ data = /incbin/("sandbox-kernel.dtb");
+ type = "flat_dt";
+ arch = "sandbox";
+ compression = "none";
+ fdt-version = <1>;
+ signature {
+ algo = "sha256,rsa2048";
+ padding = "pss";
+ key-name-hint = "dev";
+ };
+ };
+ };
+ configurations {
+ default = "conf-1";
+ conf-1 {
+ kernel = "kernel";
+ fdt = "fdt-1";
+ };
+ };
+};
diff --git a/roms/u-boot/test/py/tests/vboot/sign-images-sha256.its b/roms/u-boot/test/py/tests/vboot/sign-images-sha256.its
new file mode 100644
index 000000000..bb0f8ee8a
--- /dev/null
+++ b/roms/u-boot/test/py/tests/vboot/sign-images-sha256.its
@@ -0,0 +1,42 @@
+/dts-v1/;
+
+/ {
+ description = "Chrome OS kernel image with one or more FDT blobs";
+ #address-cells = <1>;
+
+ images {
+ kernel {
+ data = /incbin/("test-kernel.bin");
+ type = "kernel_noload";
+ arch = "sandbox";
+ os = "linux";
+ compression = "none";
+ load = <0x4>;
+ entry = <0x8>;
+ kernel-version = <1>;
+ signature {
+ algo = "sha256,rsa2048";
+ key-name-hint = "dev";
+ };
+ };
+ fdt-1 {
+ description = "snow";
+ data = /incbin/("sandbox-kernel.dtb");
+ type = "flat_dt";
+ arch = "sandbox";
+ compression = "none";
+ fdt-version = <1>;
+ signature {
+ algo = "sha256,rsa2048";
+ key-name-hint = "dev";
+ };
+ };
+ };
+ configurations {
+ default = "conf-1";
+ conf-1 {
+ kernel = "kernel";
+ fdt = "fdt-1";
+ };
+ };
+};
diff --git a/roms/u-boot/test/py/tests/vboot_evil.py b/roms/u-boot/test/py/tests/vboot_evil.py
new file mode 100644
index 000000000..9825c2171
--- /dev/null
+++ b/roms/u-boot/test/py/tests/vboot_evil.py
@@ -0,0 +1,485 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2020, Intel Corporation
+
+"""Modifies a devicetree to add a fake root node, for testing purposes"""
+
+import hashlib
+import struct
+import sys
+
+FDT_PROP = 0x3
+FDT_BEGIN_NODE = 0x1
+FDT_END_NODE = 0x2
+FDT_END = 0x9
+
+FAKE_ROOT_ATTACK = 0
+KERNEL_AT = 1
+
+MAGIC = 0xd00dfeed
+
+EVIL_KERNEL_NAME = b'evil_kernel'
+FAKE_ROOT_NAME = b'f@keroot'
+
+
+def getstr(dt_strings, off):
+ """Get a string from the devicetree string table
+
+ Args:
+ dt_strings (bytes): Devicetree strings section
+ off (int): Offset of string to read
+
+ Returns:
+ str: String read from the table
+ """
+ output = ''
+ while dt_strings[off]:
+ output += chr(dt_strings[off])
+ off += 1
+
+ return output
+
+
+def align(offset):
+ """Align an offset to a multiple of 4
+
+ Args:
+ offset (int): Offset to align
+
+ Returns:
+ int: Resulting aligned offset (rounds up to nearest multiple)
+ """
+ return (offset + 3) & ~3
+
+
+def determine_offset(dt_struct, dt_strings, searched_node_name):
+ """Determines the offset of an element, either a node or a property
+
+ Args:
+ dt_struct (bytes): Devicetree struct section
+ dt_strings (bytes): Devicetree strings section
+ searched_node_name (str): element path, ex: /images/kernel@1/data
+
+ Returns:
+ tuple: (node start offset, node end offset)
+ if element is not found, returns (None, None)
+ """
+ offset = 0
+ depth = -1
+
+ path = '/'
+
+ object_start_offset = None
+ object_end_offset = None
+ object_depth = None
+
+ while offset < len(dt_struct):
+ (tag,) = struct.unpack('>I', dt_struct[offset:offset + 4])
+
+ if tag == FDT_BEGIN_NODE:
+ depth += 1
+
+ begin_node_offset = offset
+ offset += 4
+
+ node_name = getstr(dt_struct, offset)
+ offset += len(node_name) + 1
+ offset = align(offset)
+
+ if path[-1] != '/':
+ path += '/'
+
+ path += str(node_name)
+
+ if path == searched_node_name:
+ object_start_offset = begin_node_offset
+ object_depth = depth
+
+ elif tag == FDT_PROP:
+ begin_prop_offset = offset
+
+ offset += 4
+ len_tag, nameoff = struct.unpack('>II',
+ dt_struct[offset:offset + 8])
+ offset += 8
+ prop_name = getstr(dt_strings, nameoff)
+
+ len_tag = align(len_tag)
+
+ offset += len_tag
+
+ node_path = path + '/' + str(prop_name)
+
+ if node_path == searched_node_name:
+ object_start_offset = begin_prop_offset
+
+ elif tag == FDT_END_NODE:
+ offset += 4
+
+ path = path[:path.rfind('/')]
+ if not path:
+ path = '/'
+
+ if depth == object_depth:
+ object_end_offset = offset
+ break
+ depth -= 1
+ elif tag == FDT_END:
+ break
+
+ else:
+ print('unknown tag=0x%x, offset=0x%x found!' % (tag, offset))
+ break
+
+ return object_start_offset, object_end_offset
+
+
+def modify_node_name(dt_struct, node_offset, replcd_name):
+ """Change the name of a node
+
+ Args:
+ dt_struct (bytes): Devicetree struct section
+ node_offset (int): Offset of node
+ replcd_name (str): New name for node
+
+ Returns:
+ bytes: New dt_struct contents
+ """
+
+ # skip 4 bytes for the FDT_BEGIN_NODE
+ node_offset += 4
+
+ node_name = getstr(dt_struct, node_offset)
+ node_name_len = len(node_name) + 1
+
+ node_name_len = align(node_name_len)
+
+ replcd_name += b'\0'
+
+ # align on 4 bytes
+ while len(replcd_name) % 4:
+ replcd_name += b'\0'
+
+ dt_struct = (dt_struct[:node_offset] + replcd_name +
+ dt_struct[node_offset + node_name_len:])
+
+ return dt_struct
+
+
+def modify_prop_content(dt_struct, prop_offset, content):
+ """Overwrite the value of a property
+
+ Args:
+ dt_struct (bytes): Devicetree struct section
+ prop_offset (int): Offset of property (FDT_PROP tag)
+ content (bytes): New content for the property
+
+ Returns:
+ bytes: New dt_struct contents
+ """
+ # skip FDT_PROP
+ prop_offset += 4
+ (len_tag, nameoff) = struct.unpack('>II',
+ dt_struct[prop_offset:prop_offset + 8])
+
+ # compute padded original node length
+ original_node_len = len_tag + 8 # content length + prop meta data len
+
+ original_node_len = align(original_node_len)
+
+ added_data = struct.pack('>II', len(content), nameoff)
+ added_data += content
+ while len(added_data) % 4:
+ added_data += b'\0'
+
+ dt_struct = (dt_struct[:prop_offset] + added_data +
+ dt_struct[prop_offset + original_node_len:])
+
+ return dt_struct
+
+
+def change_property_value(dt_struct, dt_strings, prop_path, prop_value,
+ required=True):
+ """Change a given property value
+
+ Args:
+ dt_struct (bytes): Devicetree struct section
+ dt_strings (bytes): Devicetree strings section
+ prop_path (str): full path of the target property
+ prop_value (bytes): new property name
+ required (bool): raise an exception if property not found
+
+ Returns:
+ bytes: New dt_struct contents
+
+ Raises:
+ ValueError: if the property is not found
+ """
+ (rt_node_start, _) = determine_offset(dt_struct, dt_strings, prop_path)
+ if rt_node_start is None:
+ if not required:
+ return dt_struct
+ raise ValueError('Fatal error, unable to find prop %s' % prop_path)
+
+ dt_struct = modify_prop_content(dt_struct, rt_node_start, prop_value)
+
+ return dt_struct
+
+def change_node_name(dt_struct, dt_strings, node_path, node_name):
+ """Change a given node name
+
+ Args:
+ dt_struct (bytes): Devicetree struct section
+ dt_strings (bytes): Devicetree strings section
+ node_path (str): full path of the target node
+ node_name (str): new node name, just node name not full path
+
+ Returns:
+ bytes: New dt_struct contents
+
+ Raises:
+ ValueError: if the node is not found
+ """
+ (rt_node_start, rt_node_end) = (
+ determine_offset(dt_struct, dt_strings, node_path))
+ if rt_node_start is None or rt_node_end is None:
+ raise ValueError('Fatal error, unable to find root node')
+
+ dt_struct = modify_node_name(dt_struct, rt_node_start, node_name)
+
+ return dt_struct
+
+def get_prop_value(dt_struct, dt_strings, prop_path):
+ """Get the content of a property based on its path
+
+ Args:
+ dt_struct (bytes): Devicetree struct section
+ dt_strings (bytes): Devicetree strings section
+ prop_path (str): full path of the target property
+
+ Returns:
+ bytes: Property value
+
+ Raises:
+ ValueError: if the property is not found
+ """
+ (offset, _) = determine_offset(dt_struct, dt_strings, prop_path)
+ if offset is None:
+ raise ValueError('Fatal error, unable to find prop')
+
+ offset += 4
+ (len_tag,) = struct.unpack('>I', dt_struct[offset:offset + 4])
+
+ offset += 8
+ tag_data = dt_struct[offset:offset + len_tag]
+
+ return tag_data
+
+
+def kernel_at_attack(dt_struct, dt_strings, kernel_content, kernel_hash):
+ """Conduct the kernel@ attack
+
+ It fetches from /configurations/default the name of the kernel being loaded.
+ Then, if the kernel name does not contain any @sign, duplicates the kernel
+ in /images node and appends '@evil' to its name.
+ It inserts a new kernel content and updates its images digest.
+
+ Inputs:
+ - FIT dt_struct
+ - FIT dt_strings
+ - kernel content blob
+ - kernel hash blob
+
+ Important note: it assumes the U-Boot loading method is 'kernel' and the
+ loaded kernel hash's subnode name is 'hash-1'
+ """
+
+ # retrieve the default configuration name
+ default_conf_name = get_prop_value(
+ dt_struct, dt_strings, '/configurations/default')
+ default_conf_name = str(default_conf_name[:-1], 'utf-8')
+
+ conf_path = '/configurations/' + default_conf_name
+
+ # fetch the loaded kernel name from the default configuration
+ loaded_kernel = get_prop_value(dt_struct, dt_strings, conf_path + '/kernel')
+
+ loaded_kernel = str(loaded_kernel[:-1], 'utf-8')
+
+ if loaded_kernel.find('@') != -1:
+ print('kernel@ attack does not work on nodes already containing an @ sign!')
+ sys.exit()
+
+ # determine boundaries of the loaded kernel
+ (krn_node_start, krn_node_end) = (determine_offset(
+ dt_struct, dt_strings, '/images/' + loaded_kernel))
+ if krn_node_start is None and krn_node_end is None:
+ print('Fatal error, unable to find root node')
+ sys.exit()
+
+ # copy the loaded kernel
+ loaded_kernel_copy = dt_struct[krn_node_start:krn_node_end]
+
+ # insert the copy inside the tree
+ dt_struct = dt_struct[:krn_node_start] + \
+ loaded_kernel_copy + dt_struct[krn_node_start:]
+
+ evil_kernel_name = loaded_kernel+'@evil'
+
+ # change the inserted kernel name
+ dt_struct = change_node_name(
+ dt_struct, dt_strings, '/images/' + loaded_kernel, bytes(evil_kernel_name, 'utf-8'))
+
+ # change the content of the kernel being loaded
+ dt_struct = change_property_value(
+ dt_struct, dt_strings, '/images/' + evil_kernel_name + '/data', kernel_content)
+
+ # change the content of the kernel being loaded
+ dt_struct = change_property_value(
+ dt_struct, dt_strings, '/images/' + evil_kernel_name + '/hash-1/value', kernel_hash)
+
+ return dt_struct
+
+
+def fake_root_node_attack(dt_struct, dt_strings, kernel_content, kernel_digest):
+ """Conduct the fakenode attack
+
+ It duplicates the original root node at the beginning of the tree.
+ Then it modifies within this duplicated tree:
+ - The loaded kernel name
+ - The loaded kernel data
+
+ Important note: it assumes the UBoot loading method is 'kernel' and the loaded kernel
+ hash's subnode name is hash@1
+ """
+
+ # retrieve the default configuration name
+ default_conf_name = get_prop_value(
+ dt_struct, dt_strings, '/configurations/default')
+ default_conf_name = str(default_conf_name[:-1], 'utf-8')
+
+ conf_path = '/configurations/'+default_conf_name
+
+ # fetch the loaded kernel name from the default configuration
+ loaded_kernel = get_prop_value(dt_struct, dt_strings, conf_path + '/kernel')
+
+ loaded_kernel = str(loaded_kernel[:-1], 'utf-8')
+
+ # determine root node start and end:
+ (rt_node_start, rt_node_end) = (determine_offset(dt_struct, dt_strings, '/'))
+ if (rt_node_start is None) or (rt_node_end is None):
+ print('Fatal error, unable to find root node')
+ sys.exit()
+
+ # duplicate the whole tree
+ duplicated_node = dt_struct[rt_node_start:rt_node_end]
+
+ # dchange root name (empty name) to fake root name
+ new_dup = change_node_name(duplicated_node, dt_strings, '/', FAKE_ROOT_NAME)
+
+ dt_struct = new_dup + dt_struct
+
+ # change the value of /<fake_root_name>/configs/<default_config_name>/kernel
+ # so our modified kernel will be loaded
+ base = '/' + str(FAKE_ROOT_NAME, 'utf-8')
+ value_path = base + conf_path+'/kernel'
+ dt_struct = change_property_value(dt_struct, dt_strings, value_path,
+ EVIL_KERNEL_NAME + b'\0')
+
+ # change the node of the /<fake_root_name>/images/<original_kernel_name>
+ images_path = base + '/images/'
+ node_path = images_path + loaded_kernel
+ dt_struct = change_node_name(dt_struct, dt_strings, node_path,
+ EVIL_KERNEL_NAME)
+
+ # change the content of the kernel being loaded
+ data_path = images_path + str(EVIL_KERNEL_NAME, 'utf-8') + '/data'
+ dt_struct = change_property_value(dt_struct, dt_strings, data_path,
+ kernel_content, required=False)
+
+ # update the digest value
+ hash_path = images_path + str(EVIL_KERNEL_NAME, 'utf-8') + '/hash-1/value'
+ dt_struct = change_property_value(dt_struct, dt_strings, hash_path,
+ kernel_digest)
+
+ return dt_struct
+
+def add_evil_node(in_fname, out_fname, kernel_fname, attack):
+ """Add an evil node to the devicetree
+
+ Args:
+ in_fname (str): Filename of input devicetree
+ out_fname (str): Filename to write modified devicetree to
+ kernel_fname (str): Filename of kernel data to add to evil node
+ attack (str): Attack type ('fakeroot' or 'kernel@')
+
+ Raises:
+ ValueError: Unknown attack name
+ """
+ if attack == 'fakeroot':
+ attack = FAKE_ROOT_ATTACK
+ elif attack == 'kernel@':
+ attack = KERNEL_AT
+ else:
+ raise ValueError('Unknown attack name!')
+
+ with open(in_fname, 'rb') as fin:
+ input_data = fin.read()
+
+ hdr = input_data[0:0x28]
+
+ offset = 0
+ magic = struct.unpack('>I', hdr[offset:offset + 4])[0]
+ if magic != MAGIC:
+ raise ValueError('Wrong magic!')
+
+ offset += 4
+ (totalsize, off_dt_struct, off_dt_strings, off_mem_rsvmap, version,
+ last_comp_version, boot_cpuid_phys, size_dt_strings,
+ size_dt_struct) = struct.unpack('>IIIIIIIII', hdr[offset:offset + 36])
+
+ rsv_map = input_data[off_mem_rsvmap:off_dt_struct]
+ dt_struct = input_data[off_dt_struct:off_dt_struct + size_dt_struct]
+ dt_strings = input_data[off_dt_strings:off_dt_strings + size_dt_strings]
+
+ with open(kernel_fname, 'rb') as kernel_file:
+ kernel_content = kernel_file.read()
+
+ # computing inserted kernel hash
+ val = hashlib.sha1()
+ val.update(kernel_content)
+ hash_digest = val.digest()
+
+ if attack == FAKE_ROOT_ATTACK:
+ dt_struct = fake_root_node_attack(dt_struct, dt_strings, kernel_content,
+ hash_digest)
+ elif attack == KERNEL_AT:
+ dt_struct = kernel_at_attack(dt_struct, dt_strings, kernel_content,
+ hash_digest)
+
+ # now rebuild the new file
+ size_dt_strings = len(dt_strings)
+ size_dt_struct = len(dt_struct)
+ totalsize = 0x28 + len(rsv_map) + size_dt_struct + size_dt_strings
+ off_mem_rsvmap = 0x28
+ off_dt_struct = off_mem_rsvmap + len(rsv_map)
+ off_dt_strings = off_dt_struct + len(dt_struct)
+
+ header = struct.pack('>IIIIIIIIII', MAGIC, totalsize, off_dt_struct,
+ off_dt_strings, off_mem_rsvmap, version,
+ last_comp_version, boot_cpuid_phys, size_dt_strings,
+ size_dt_struct)
+
+ with open(out_fname, 'wb') as output_file:
+ output_file.write(header)
+ output_file.write(rsv_map)
+ output_file.write(dt_struct)
+ output_file.write(dt_strings)
+
+if __name__ == '__main__':
+ if len(sys.argv) != 5:
+ print('usage: %s <input_filename> <output_filename> <kernel_binary> <attack_name>' %
+ sys.argv[0])
+ print('valid attack names: [fakeroot, kernel@]')
+ sys.exit(1)
+
+ add_evil_node(sys.argv[1:])
diff --git a/roms/u-boot/test/py/tests/vboot_forge.py b/roms/u-boot/test/py/tests/vboot_forge.py
new file mode 100644
index 000000000..b41105bd0
--- /dev/null
+++ b/roms/u-boot/test/py/tests/vboot_forge.py
@@ -0,0 +1,423 @@
+#!/usr/bin/python3
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2020, F-Secure Corporation, https://foundry.f-secure.com
+#
+# pylint: disable=E1101,W0201,C0103
+
+"""
+Verified boot image forgery tools and utilities
+
+This module provides services to both take apart and regenerate FIT images
+in a way that preserves all existing verified boot signatures, unless you
+manipulate nodes in the process.
+"""
+
+import struct
+import binascii
+from io import BytesIO
+
+#
+# struct parsing helpers
+#
+
+class BetterStructMeta(type):
+ """
+ Preprocesses field definitions and creates a struct.Struct instance from them
+ """
+ def __new__(cls, clsname, superclasses, attributedict):
+ if clsname != 'BetterStruct':
+ fields = attributedict['__fields__']
+ field_types = [_[0] for _ in fields]
+ field_names = [_[1] for _ in fields if _[1] is not None]
+ attributedict['__names__'] = field_names
+ s = struct.Struct(attributedict.get('__endian__', '') + ''.join(field_types))
+ attributedict['__struct__'] = s
+ attributedict['size'] = s.size
+ return type.__new__(cls, clsname, superclasses, attributedict)
+
+class BetterStruct(metaclass=BetterStructMeta):
+ """
+ Base class for better structures
+ """
+ def __init__(self):
+ for t, n in self.__fields__:
+ if 's' in t:
+ setattr(self, n, '')
+ elif t in ('Q', 'I', 'H', 'B'):
+ setattr(self, n, 0)
+
+ @classmethod
+ def unpack_from(cls, buffer, offset=0):
+ """
+ Unpack structure instance from a buffer
+ """
+ fields = cls.__struct__.unpack_from(buffer, offset)
+ instance = cls()
+ for n, v in zip(cls.__names__, fields):
+ setattr(instance, n, v)
+ return instance
+
+ def pack(self):
+ """
+ Pack structure instance into bytes
+ """
+ return self.__struct__.pack(*[getattr(self, n) for n in self.__names__])
+
+ def __str__(self):
+ items = ["'%s': %s" % (n, repr(getattr(self, n))) for n in self.__names__ if n is not None]
+ return '(' + ', '.join(items) + ')'
+
+#
+# some defs for flat DT data
+#
+
+class HeaderV17(BetterStruct):
+ __endian__ = '>'
+ __fields__ = [
+ ('I', 'magic'),
+ ('I', 'totalsize'),
+ ('I', 'off_dt_struct'),
+ ('I', 'off_dt_strings'),
+ ('I', 'off_mem_rsvmap'),
+ ('I', 'version'),
+ ('I', 'last_comp_version'),
+ ('I', 'boot_cpuid_phys'),
+ ('I', 'size_dt_strings'),
+ ('I', 'size_dt_struct'),
+ ]
+
+class RRHeader(BetterStruct):
+ __endian__ = '>'
+ __fields__ = [
+ ('Q', 'address'),
+ ('Q', 'size'),
+ ]
+
+class PropHeader(BetterStruct):
+ __endian__ = '>'
+ __fields__ = [
+ ('I', 'value_size'),
+ ('I', 'name_offset'),
+ ]
+
+# magical constants for DTB format
+OF_DT_HEADER = 0xd00dfeed
+OF_DT_BEGIN_NODE = 1
+OF_DT_END_NODE = 2
+OF_DT_PROP = 3
+OF_DT_END = 9
+
+class StringsBlock:
+ """
+ Represents a parsed device tree string block
+ """
+ def __init__(self, values=None):
+ if values is None:
+ self.values = []
+ else:
+ self.values = values
+
+ def __getitem__(self, at):
+ if isinstance(at, str):
+ offset = 0
+ for value in self.values:
+ if value == at:
+ break
+ offset += len(value) + 1
+ else:
+ self.values.append(at)
+ return offset
+
+ if isinstance(at, int):
+ offset = 0
+ for value in self.values:
+ if offset == at:
+ return value
+ offset += len(value) + 1
+ raise IndexError('no string found corresponding to the given offset')
+
+ raise TypeError('only strings and integers are accepted')
+
+class Prop:
+ """
+ Represents a parsed device tree property
+ """
+ def __init__(self, name=None, value=None):
+ self.name = name
+ self.value = value
+
+ def clone(self):
+ return Prop(self.name, self.value)
+
+ def __repr__(self):
+ return "<Prop(name='%s', value=%s>" % (self.name, repr(self.value))
+
+class Node:
+ """
+ Represents a parsed device tree node
+ """
+ def __init__(self, name=None):
+ self.name = name
+ self.props = []
+ self.children = []
+
+ def clone(self):
+ o = Node(self.name)
+ o.props = [x.clone() for x in self.props]
+ o.children = [x.clone() for x in self.children]
+ return o
+
+ def __getitem__(self, index):
+ return self.children[index]
+
+ def __repr__(self):
+ return "<Node('%s'), %s, %s>" % (self.name, repr(self.props), repr(self.children))
+
+#
+# flat DT to memory
+#
+
+def parse_strings(strings):
+ """
+ Converts the bytes into a StringsBlock instance so it is convenient to work with
+ """
+ strings = strings.split(b'\x00')
+ return StringsBlock(strings)
+
+def parse_struct(stream):
+ """
+ Parses DTB structure(s) into a Node or Prop instance
+ """
+ tag = bytearray(stream.read(4))[3]
+ if tag == OF_DT_BEGIN_NODE:
+ name = b''
+ while b'\x00' not in name:
+ name += stream.read(4)
+ name = name.rstrip(b'\x00')
+ node = Node(name)
+
+ item = parse_struct(stream)
+ while item is not None:
+ if isinstance(item, Node):
+ node.children.append(item)
+ elif isinstance(item, Prop):
+ node.props.append(item)
+ item = parse_struct(stream)
+
+ return node
+
+ if tag == OF_DT_PROP:
+ h = PropHeader.unpack_from(stream.read(PropHeader.size))
+ length = (h.value_size + 3) & (~3)
+ value = stream.read(length)[:h.value_size]
+ prop = Prop(h.name_offset, value)
+ return prop
+
+ if tag in (OF_DT_END_NODE, OF_DT_END):
+ return None
+
+ raise ValueError('unexpected tag value')
+
+def read_fdt(fp):
+ """
+ Reads and parses the flattened device tree (or derivatives like FIT)
+ """
+ header = HeaderV17.unpack_from(fp.read(HeaderV17.size))
+ if header.magic != OF_DT_HEADER:
+ raise ValueError('invalid magic value %08x; expected %08x' % (header.magic, OF_DT_HEADER))
+ # TODO: read/parse reserved regions
+ fp.seek(header.off_dt_struct)
+ structs = fp.read(header.size_dt_struct)
+ fp.seek(header.off_dt_strings)
+ strings = fp.read(header.size_dt_strings)
+ strblock = parse_strings(strings)
+ root = parse_struct(BytesIO(structs))
+
+ return root, strblock
+
+#
+# memory to flat DT
+#
+
+def compose_structs_r(item):
+ """
+ Recursive part of composing Nodes and Props into a bytearray
+ """
+ t = bytearray()
+
+ if isinstance(item, Node):
+ t.extend(struct.pack('>I', OF_DT_BEGIN_NODE))
+ if isinstance(item.name, str):
+ item.name = bytes(item.name, 'utf-8')
+ name = item.name + b'\x00'
+ if len(name) & 3:
+ name += b'\x00' * (4 - (len(name) & 3))
+ t.extend(name)
+ for p in item.props:
+ t.extend(compose_structs_r(p))
+ for c in item.children:
+ t.extend(compose_structs_r(c))
+ t.extend(struct.pack('>I', OF_DT_END_NODE))
+
+ elif isinstance(item, Prop):
+ t.extend(struct.pack('>I', OF_DT_PROP))
+ value = item.value
+ h = PropHeader()
+ h.name_offset = item.name
+ if value:
+ h.value_size = len(value)
+ t.extend(h.pack())
+ if len(value) & 3:
+ value += b'\x00' * (4 - (len(value) & 3))
+ t.extend(value)
+ else:
+ h.value_size = 0
+ t.extend(h.pack())
+
+ return t
+
+def compose_structs(root):
+ """
+ Composes the parsed Nodes into a flat bytearray instance
+ """
+ t = compose_structs_r(root)
+ t.extend(struct.pack('>I', OF_DT_END))
+ return t
+
+def compose_strings(strblock):
+ """
+ Composes the StringsBlock instance back into a bytearray instance
+ """
+ b = bytearray()
+ for s in strblock.values:
+ b.extend(s)
+ b.append(0)
+ return bytes(b)
+
+def write_fdt(root, strblock, fp):
+ """
+ Writes out a complete flattened device tree (or FIT)
+ """
+ header = HeaderV17()
+ header.magic = OF_DT_HEADER
+ header.version = 17
+ header.last_comp_version = 16
+ fp.write(header.pack())
+
+ header.off_mem_rsvmap = fp.tell()
+ fp.write(RRHeader().pack())
+
+ structs = compose_structs(root)
+ header.off_dt_struct = fp.tell()
+ header.size_dt_struct = len(structs)
+ fp.write(structs)
+
+ strings = compose_strings(strblock)
+ header.off_dt_strings = fp.tell()
+ header.size_dt_strings = len(strings)
+ fp.write(strings)
+
+ header.totalsize = fp.tell()
+
+ fp.seek(0)
+ fp.write(header.pack())
+
+#
+# pretty printing / converting to DT source
+#
+
+def as_bytes(value):
+ return ' '.join(["%02X" % x for x in value])
+
+def prety_print_value(value):
+ """
+ Formats a property value as appropriate depending on the guessed data type
+ """
+ if not value:
+ return '""'
+ if value[-1] == b'\x00':
+ printable = True
+ for x in value[:-1]:
+ x = ord(x)
+ if x != 0 and (x < 0x20 or x > 0x7F):
+ printable = False
+ break
+ if printable:
+ value = value[:-1]
+ return ', '.join('"' + x + '"' for x in value.split(b'\x00'))
+ if len(value) > 0x80:
+ return '[' + as_bytes(value[:0x80]) + ' ... ]'
+ return '[' + as_bytes(value) + ']'
+
+def pretty_print_r(node, strblock, indent=0):
+ """
+ Prints out a single node, recursing further for each of its children
+ """
+ spaces = ' ' * indent
+ print((spaces + '%s {' % (node.name.decode('utf-8') if node.name else '/')))
+ for p in node.props:
+ print((spaces + ' %s = %s;' % (strblock[p.name].decode('utf-8'), prety_print_value(p.value))))
+ for c in node.children:
+ pretty_print_r(c, strblock, indent+1)
+ print((spaces + '};'))
+
+def pretty_print(node, strblock):
+ """
+ Generates an almost-DTS formatted printout of the parsed device tree
+ """
+ print('/dts-v1/;')
+ pretty_print_r(node, strblock, 0)
+
+#
+# manipulating the DT structure
+#
+
+def manipulate(root, strblock):
+ """
+ Maliciously manipulates the structure to create a crafted FIT file
+ """
+ # locate /images/kernel-1 (frankly, it just expects it to be the first one)
+ kernel_node = root[0][0]
+ # clone it to save time filling all the properties
+ fake_kernel = kernel_node.clone()
+ # rename the node
+ fake_kernel.name = b'kernel-2'
+ # get rid of signatures/hashes
+ fake_kernel.children = []
+ # NOTE: this simply replaces the first prop... either description or data
+ # should be good for testing purposes
+ fake_kernel.props[0].value = b'Super 1337 kernel\x00'
+ # insert the new kernel node under /images
+ root[0].children.append(fake_kernel)
+
+ # modify the default configuration
+ root[1].props[0].value = b'conf-2\x00'
+ # clone the first (only?) configuration
+ fake_conf = root[1][0].clone()
+ # rename and change kernel and fdt properties to select the crafted kernel
+ fake_conf.name = b'conf-2'
+ fake_conf.props[0].value = b'kernel-2\x00'
+ fake_conf.props[1].value = b'fdt-1\x00'
+ # insert the new configuration under /configurations
+ root[1].children.append(fake_conf)
+
+ return root, strblock
+
+def main(argv):
+ with open(argv[1], 'rb') as fp:
+ root, strblock = read_fdt(fp)
+
+ print("Before:")
+ pretty_print(root, strblock)
+
+ root, strblock = manipulate(root, strblock)
+ print("After:")
+ pretty_print(root, strblock)
+
+ with open('blah', 'w+b') as fp:
+ write_fdt(root, strblock, fp)
+
+if __name__ == '__main__':
+ import sys
+ main(sys.argv)
+# EOF