diff options
Diffstat (limited to 'test/py')
| -rw-r--r-- | test/py/tests/fs_helper.py | 95 | ||||
| -rw-r--r-- | test/py/tests/test_cat.py | 27 | ||||
| -rw-r--r-- | test/py/tests/test_efi_bootmgr.py | 38 | ||||
| -rw-r--r-- | test/py/tests/test_efi_fit.py | 2 | ||||
| -rw-r--r-- | test/py/tests/test_efi_loader.py | 2 | ||||
| -rw-r--r-- | test/py/tests/test_fpga.py | 4 | ||||
| -rw-r--r-- | test/py/tests/test_fs/conftest.py | 198 | ||||
| -rw-r--r-- | test/py/tests/test_net.py | 2 | ||||
| -rw-r--r-- | test/py/tests/test_scsi.py | 10 | ||||
| -rw-r--r-- | test/py/tests/test_ut.py | 190 | ||||
| -rw-r--r-- | test/py/tests/test_vboot.py | 10 | ||||
| -rw-r--r-- | test/py/tests/test_xxd.py | 28 | ||||
| -rw-r--r-- | test/py/tests/vboot_evil.py | 65 |
13 files changed, 368 insertions, 303 deletions
diff --git a/test/py/tests/fs_helper.py b/test/py/tests/fs_helper.py index 800376b1e7d..ee779474ce6 100644 --- a/test/py/tests/fs_helper.py +++ b/test/py/tests/fs_helper.py @@ -66,8 +66,9 @@ class FsHelper: """Make a new filesystem and copy in the files""" self.setup() self._do_cleanup = True + src_dir = self.srcdir if os.listdir(self.srcdir) else None self.fs_img = mk_fs(self.config, self.fs_type, self.size_mb << 20, - self.prefix, self.srcdir, quiet=self.quiet) + self.prefix, src_dir, quiet=self.quiet) def setup(self): """Set up the srcdir ready to receive files""" @@ -97,6 +98,98 @@ class FsHelper: self.cleanup() +class DiskHelper: + """Helper class for creating disk images containing filesytems + + Usage: + with DiskHelper(ubman.config, 0, 'mmc') as img, \ + FsHelper(ubman.config, 'ext1', 1, 'mmc') as fsh: + # Write files to fsh.srcdir + ... + + # Create the filesystem + fsh.mk_fs() + + # Add this filesystem to the disk + img.add_fs(fsh, DiskHelper.VFAT) + + # Add more filesystems as needed (add another 'with' clause) + ... + + # Get the final disk image + data = img.create() + """ + + # Partition-type codes + VFAT = 0xc + EXT4 = 0x83 + + def __init__(self, config, devnum, prefix, cur_dir=False): + """Set up a new disk image + + Args: + config (u_boot_config): U-Boot configuration + devnum (int): Device number (for filename) + prefix (str): Prefix string of volume's file name + cur_dir (bool): True to put the file in the current directory, + instead of the persistent-data directory + """ + self.fs_list = [] + self.fname = os.path.join('' if cur_dir else config.persistent_data_dir, + f'{prefix}{devnum}.img') + + def add_fs(self, fs_img, part_type, bootable=False): + """Add a new filesystem + + Args: + fs_img (FsHelper): Filesystem to add + part_type (DiskHelper.FAT or DiskHelper.EXT4): Partition type + bootable (bool): True to set the 'bootable' flat + """ + self.fs_list.append([fs_img, part_type, bootable]) + + def create(self): + """Create the disk image + + Create an image with a partition table and the filesystems + """ + spec = '' + pos = 1 # Reserve 1MB for the partition table itself + for fsi, part_type, bootable in self.fs_list: + if spec: + spec += '\n' + spec += f'type={part_type:x}, size={fsi.size_mb}M, start={pos}M' + if bootable: + spec += ', bootable' + pos += fsi.size_mb + + img_size = pos + try: + check_call(f'qemu-img create {self.fname} {img_size}M', shell=True) + check_call(f'printf "{spec}" | sfdisk {self.fname}', shell=True) + except CalledProcessError: + os.remove(self.fname) + raise + + pos = 1 # Reserve 1MB for the partition table itself + for fsi, part_type, bootable in self.fs_list: + check_call( + f'dd if={fsi.fs_img} of={self.fname} bs=1M seek={pos} conv=notrunc', + shell=True) + pos += fsi.size_mb + return self.fname + + def cleanup(self, remove_full_img=False): + """Remove created file""" + os.remove(self.fname) + + def __enter__(self): + return self + + def __exit__(self, extype, value, traceback): + self.cleanup() + + def mk_fs(config, fs_type, size, prefix, src_dir=None, fs_img=None, quiet=False): """Create a file system volume diff --git a/test/py/tests/test_cat.py b/test/py/tests/test_cat.py index 252c3d50a02..f793b9fe0a1 100644 --- a/test/py/tests/test_cat.py +++ b/test/py/tests/test_cat.py @@ -4,8 +4,7 @@ """ import pytest -from subprocess import call, check_call, CalledProcessError -from tests import fs_helper +from tests.fs_helper import FsHelper @pytest.mark.boardspec('sandbox') @pytest.mark.buildconfigspec('cmd_cat') @@ -15,23 +14,11 @@ def test_cat(ubman): Args: ubman -- U-Boot console """ - try: - scratch_dir = ubman.config.persistent_data_dir + '/scratch' + with FsHelper(ubman.config, 'vfat', 1, 'test_cat') as fsh: + with open(f'{fsh.srcdir}/hello', 'w', encoding = 'ascii') as outf: + outf.write('hello world\n') + fsh.mk_fs() - check_call('mkdir -p %s' % scratch_dir, shell=True) - - with open(scratch_dir + '/hello', 'w', encoding = 'ascii') as file: - file.write('hello world\n') - - cat_data = fs_helper.mk_fs(ubman.config, 'vfat', 0x100000, - 'test_cat', scratch_dir) - response = ubman.run_command_list([ f'host bind 0 {cat_data}', - 'cat host 0 hello']) + response = ubman.run_command_list([f'host bind 0 {fsh.fs_img}', + 'cat host 0 hello']) assert 'hello world' in response - except CalledProcessError as err: - pytest.skip('Preparing test_cat image failed') - call('rm -f %s' % cat_data, shell=True) - return - finally: - call('rm -rf %s' % scratch_dir, shell=True) - call('rm -f %s' % cat_data, shell=True) diff --git a/test/py/tests/test_efi_bootmgr.py b/test/py/tests/test_efi_bootmgr.py index 4c10cbdf17d..7d1f3f16d00 100644 --- a/test/py/tests/test_efi_bootmgr.py +++ b/test/py/tests/test_efi_bootmgr.py @@ -5,7 +5,7 @@ import shutil import pytest from subprocess import call, check_call, CalledProcessError -from tests import fs_helper +from tests.fs_helper import DiskHelper, FsHelper @pytest.mark.boardspec('sandbox') @pytest.mark.buildconfigspec('cmd_efidebug') @@ -20,22 +20,19 @@ def test_efi_bootmgr(ubman): Args: ubman -- U-Boot console """ - try: - efi_bootmgr_data, mnt = fs_helper.setup_image(ubman, 0, 0xc, - basename='test_efi_bootmgr') - - with open(mnt + '/initrd-1.img', 'w', encoding = 'ascii') as file: - file.write("initrd 1") - - with open(mnt + '/initrd-2.img', 'w', encoding = 'ascii') as file: - file.write("initrd 2") - - shutil.copyfile(ubman.config.build_dir + '/lib/efi_loader/initrddump.efi', - mnt + '/initrddump.efi') - - fsfile = fs_helper.mk_fs(ubman.config, 'vfat', 0x100000, - 'test_efi_bootmgr', mnt) - check_call(f'dd if={fsfile} of={efi_bootmgr_data} bs=1M seek=1', shell=True) + with DiskHelper(ubman.config, 0, 'test_efi_bootmgr') as img, \ + FsHelper(ubman.config, 'vfat', 1, 'test_efi_bootmgr') as fsh: + with open(f'{fsh.srcdir}/initrd-1.img', 'w', encoding = 'ascii') as outf: + outf.write("initrd 1") + with open(f'{fsh.srcdir}/initrd-2.img', 'w', encoding = 'ascii') as outf: + outf.write("initrd 2") + shutil.copyfile( + ubman.config.build_dir + '/lib/efi_loader/initrddump.efi', + f'{fsh.srcdir}/initrddump.efi') + fsh.mk_fs() + + img.add_fs(fsh, DiskHelper.VFAT) + efi_bootmgr_data = img.create() ubman.run_command(cmd = f'host bind 0 {efi_bootmgr_data}') @@ -61,10 +58,3 @@ def test_efi_bootmgr(ubman): ubman.run_command(cmd = 'efidebug boot rm 0001') ubman.run_command(cmd = 'efidebug boot rm 0002') - except CalledProcessError as err: - pytest.skip('Preparing test_efi_bootmgr image failed') - call('rm -f %s' % efi_bootmgr_data, shell=True) - return - finally: - call('rm -rf %s' % mnt, shell=True) - call('rm -f %s' % efi_bootmgr_data, shell=True) diff --git a/test/py/tests/test_efi_fit.py b/test/py/tests/test_efi_fit.py index 63ee8e6cef2..409cfdfd56f 100644 --- a/test/py/tests/test_efi_fit.py +++ b/test/py/tests/test_efi_fit.py @@ -225,7 +225,7 @@ def test_efi_fit_launch(ubman): has_dhcp = ubman.config.buildconfig.get('config_cmd_dhcp', 'n') == 'y' if not has_dhcp: - ubman.log.warning('CONFIG_NET != y: Skipping static network setup') + ubman.log.warning('CONFIG_NET_LEGACY != y: Skipping static network setup') return False env_vars = ubman.config.env.get('env__net_static_env_vars', None) diff --git a/test/py/tests/test_efi_loader.py b/test/py/tests/test_efi_loader.py index dc58c0d4dbd..91f151d09cd 100644 --- a/test/py/tests/test_efi_loader.py +++ b/test/py/tests/test_efi_loader.py @@ -98,7 +98,7 @@ def test_efi_setup_dhcp(ubman): global net_set_up net_set_up = True [email protected]('net', 'net_lwip') [email protected]('net') def test_efi_setup_static(ubman): """Set up the network using a static IP configuration. diff --git a/test/py/tests/test_fpga.py b/test/py/tests/test_fpga.py index 299a8653f74..74cd42b910e 100644 --- a/test/py/tests/test_fpga.py +++ b/test/py/tests/test_fpga.py @@ -506,7 +506,7 @@ def test_fpga_loadfs(ubman): @pytest.mark.buildconfigspec('cmd_fpga_load_secure') @pytest.mark.buildconfigspec('cmd_net') @pytest.mark.buildconfigspec('cmd_dhcp') [email protected]('net', 'net_lwip') [email protected]('net') def test_fpga_secure_bit_auth(ubman): test_net.test_net_dhcp(ubman) @@ -534,7 +534,7 @@ def test_fpga_secure_bit_auth(ubman): @pytest.mark.buildconfigspec('cmd_fpga_load_secure') @pytest.mark.buildconfigspec('cmd_net') @pytest.mark.buildconfigspec('cmd_dhcp') [email protected]('net', 'net_lwip') [email protected]('net') def test_fpga_secure_bit_img_auth_kup(ubman): test_net.test_net_dhcp(ubman) diff --git a/test/py/tests/test_fs/conftest.py b/test/py/tests/test_fs/conftest.py index 0205048e73a..ba125cc7073 100644 --- a/test/py/tests/test_fs/conftest.py +++ b/test/py/tests/test_fs/conftest.py @@ -9,7 +9,7 @@ import re from subprocess import call, check_call, check_output, CalledProcessError from fstest_defs import * # pylint: disable=E0611 -from tests import fs_helper +from tests.fs_helper import FsHelper supported_fs_basic = ['fat16', 'fat32', 'exfat', 'ext4', 'fs_generic'] supported_fs_ext = ['fat12', 'fat16', 'fat32', 'exfat', 'fs_generic'] @@ -200,33 +200,26 @@ def fs_obj_basic(request, u_boot_config): fs_type = request.param fs_cmd_prefix = fstype_to_prefix(fs_type) fs_cmd_write = 'save' if fs_type == 'fs_generic' or fs_type == 'exfat' else 'write' - fs_img = '' fs_ubtype = fstype_to_ubname(fs_type) check_ubconfig(u_boot_config, fs_ubtype) - scratch_dir = u_boot_config.persistent_data_dir + '/scratch' - - small_file = scratch_dir + '/' + SMALL_FILE - big_file = scratch_dir + '/' + BIG_FILE - + fsh = FsHelper(u_boot_config, fs_type, 3072, '3GB') try: - check_call('mkdir -p %s' % scratch_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 + fsh.setup() + + small_file = fsh.srcdir + '/' + SMALL_FILE + big_file = fsh.srcdir + '/' + BIG_FILE - try: # Create a subdirectory. - check_call('mkdir %s/SUBDIR' % scratch_dir, shell=True) + check_call('mkdir %s/SUBDIR' % fsh.srcdir, 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) + % 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' @@ -234,65 +227,54 @@ def fs_obj_basic(request, u_boot_config): # 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) + % small_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() + % 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() + % 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() + % 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() + % 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() + % 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() + % big_file, shell=True).decode() md5val.append(out.split()[0]) - try: - # 3GiB volume - fs_img = fs_helper.mk_fs(u_boot_config, fs_type, 0xc0000000, '3GB', scratch_dir) - except CalledProcessError as err: - pytest.skip('Creating failed for filesystem: ' + fs_type + '. {}'.format(err)) - return + fsh.mk_fs() except CalledProcessError as err: pytest.skip('Setup failed for filesystem: ' + fs_type + '. {}'.format(err)) return else: - yield [fs_ubtype, fs_cmd_prefix, fs_cmd_write, fs_img, md5val] + yield [fs_ubtype, fs_cmd_prefix, fs_cmd_write, fsh.fs_img, md5val] finally: - call('rm -rf %s' % scratch_dir, shell=True) - call('rm -f %s' % fs_img, shell=True) + fsh.cleanup() # # Fixture for extended fs test @@ -312,26 +294,19 @@ def fs_obj_ext(request, u_boot_config): fs_type = request.param fs_cmd_prefix = fstype_to_prefix(fs_type) fs_cmd_write = 'save' if fs_type == 'fs_generic' or fs_type == 'exfat' else 'write' - fs_img = '' fs_ubtype = fstype_to_ubname(fs_type) check_ubconfig(u_boot_config, fs_ubtype) - scratch_dir = u_boot_config.persistent_data_dir + '/scratch' - - min_file = scratch_dir + '/' + MIN_FILE - tmp_file = scratch_dir + '/tmpfile' - + fsh = FsHelper(u_boot_config, fs_type, 128, '128MB') try: - check_call('mkdir -p %s' % scratch_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 + fsh.setup() + + min_file = fsh.srcdir + '/' + MIN_FILE + tmp_file = fsh.srcdir + '/tmpfile' - try: # Create a test directory - check_call('mkdir %s/dir1' % scratch_dir, shell=True) + check_call('mkdir %s/dir1' % fsh.srcdir, shell=True) # Create a small file and calculate md5 check_call('dd if=/dev/urandom of=%s bs=1K count=20' @@ -370,21 +345,15 @@ def fs_obj_ext(request, u_boot_config): check_call('rm %s' % tmp_file, shell=True) - try: - # 128MiB volume - fs_img = fs_helper.mk_fs(u_boot_config, fs_type, 0x8000000, '128MB', scratch_dir) - except CalledProcessError as err: - pytest.skip('Creating failed for filesystem: ' + fs_type + '. {}'.format(err)) - return + fsh.mk_fs() except CalledProcessError: pytest.skip('Setup failed for filesystem: ' + fs_type) return else: - yield [fs_ubtype, fs_cmd_prefix, fs_cmd_write, fs_img, md5val] + yield [fs_ubtype, fs_cmd_prefix, fs_cmd_write, fsh.fs_img, md5val] finally: - call('rm -rf %s' % scratch_dir, shell=True) - call('rm -f %s' % fs_img, shell=True) + fsh.cleanup() # # Fixture for mkdir test @@ -403,20 +372,19 @@ def fs_obj_mkdir(request, u_boot_config): """ fs_type = request.param fs_cmd_prefix = fstype_to_prefix(fs_type) - fs_img = '' fs_ubtype = fstype_to_ubname(fs_type) check_ubconfig(u_boot_config, fs_ubtype) + fsh = FsHelper(u_boot_config, fs_type, 128, '128MB') try: - # 128MiB volume - fs_img = fs_helper.mk_fs(u_boot_config, fs_type, 0x8000000, '128MB', None) + fsh.mk_fs() except: pytest.skip('Setup failed for filesystem: ' + fs_type) return else: - yield [fs_ubtype, fs_cmd_prefix, fs_img] - call('rm -f %s' % fs_img, shell=True) + yield [fs_ubtype, fs_cmd_prefix, fsh.fs_img] + fsh.cleanup() # # Fixture for unlink test @@ -435,57 +403,44 @@ def fs_obj_unlink(request, u_boot_config): """ fs_type = request.param fs_cmd_prefix = fstype_to_prefix(fs_type) - fs_img = '' fs_ubtype = fstype_to_ubname(fs_type) check_ubconfig(u_boot_config, fs_ubtype) - scratch_dir = u_boot_config.persistent_data_dir + '/scratch' - + fsh = FsHelper(u_boot_config, fs_type, 128, '128MB') try: - check_call('mkdir -p %s' % scratch_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 + fsh.setup() - try: # Test Case 1 & 3 - check_call('mkdir %s/dir1' % scratch_dir, shell=True) + check_call('mkdir %s/dir1' % fsh.srcdir, shell=True) check_call('dd if=/dev/urandom of=%s/dir1/file1 bs=1K count=1' - % scratch_dir, shell=True) + % fsh.srcdir, shell=True) check_call('dd if=/dev/urandom of=%s/dir1/file2 bs=1K count=1' - % scratch_dir, shell=True) + % fsh.srcdir, shell=True) # Test Case 2 - check_call('mkdir %s/dir2' % scratch_dir, shell=True) + check_call('mkdir %s/dir2' % fsh.srcdir, shell=True) for i in range(0, 20): check_call('mkdir %s/dir2/0123456789abcdef%02x' - % (scratch_dir, i), shell=True) + % (fsh.srcdir, i), shell=True) # Test Case 4 - check_call('mkdir %s/dir4' % scratch_dir, shell=True) + check_call('mkdir %s/dir4' % fsh.srcdir, shell=True) # Test Case 5, 6 & 7 - check_call('mkdir %s/dir5' % scratch_dir, shell=True) + check_call('mkdir %s/dir5' % fsh.srcdir, shell=True) check_call('dd if=/dev/urandom of=%s/dir5/file1 bs=1K count=1' - % scratch_dir, shell=True) + % fsh.srcdir, shell=True) - try: - # 128MiB volume - fs_img = fs_helper.mk_fs(u_boot_config, fs_type, 0x8000000, '128MB', scratch_dir) - except CalledProcessError as err: - pytest.skip('Creating failed for filesystem: ' + fs_type + '. {}'.format(err)) - return + fsh.mk_fs() except CalledProcessError: pytest.skip('Setup failed for filesystem: ' + fs_type) return else: - yield [fs_ubtype, fs_cmd_prefix, fs_img] + yield [fs_ubtype, fs_cmd_prefix, fsh.fs_img] finally: - call('rm -rf %s' % scratch_dir, shell=True) - call('rm -f %s' % fs_img, shell=True) + fsh.cleanup() # # Fixture for symlink fs test @@ -503,26 +458,19 @@ def fs_obj_symlink(request, u_boot_config): 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) - scratch_dir = u_boot_config.persistent_data_dir + '/scratch' - - small_file = scratch_dir + '/' + SMALL_FILE - medium_file = scratch_dir + '/' + MEDIUM_FILE - + fsh = FsHelper(u_boot_config, fs_type, 1024, '1GB') try: - check_call('mkdir -p %s' % scratch_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 + fsh.setup() + + small_file = fsh.srcdir + '/' + SMALL_FILE + medium_file = fsh.srcdir + '/' + MEDIUM_FILE - try: # Create a subdirectory. - check_call('mkdir %s/SUBDIR' % scratch_dir, shell=True) + check_call('mkdir %s/SUBDIR' % fsh.srcdir, shell=True) # Create a small file in this image. check_call('dd if=/dev/urandom of=%s bs=1M count=1' @@ -542,21 +490,15 @@ def fs_obj_symlink(request, u_boot_config): % medium_file, shell=True).decode() md5val.extend([out.split()[0]]) - try: - # 1GiB volume - fs_img = fs_helper.mk_fs(u_boot_config, fs_type, 0x40000000, '1GB', scratch_dir) - except CalledProcessError as err: - pytest.skip('Creating failed for filesystem: ' + fs_type + '. {}'.format(err)) - return + fsh.mk_fs() except CalledProcessError: pytest.skip('Setup failed for filesystem: ' + fs_type) return else: - yield [fs_ubtype, fs_img, md5val] + yield [fs_ubtype, fsh.fs_img, md5val] finally: - call('rm -rf %s' % scratch_dir, shell=True) - call('rm -f %s' % fs_img, shell=True) + fsh.cleanup() # # Fixture for rename test @@ -584,21 +526,15 @@ def fs_obj_rename(request, u_boot_config): return out.decode().split()[0] 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 + '/scratch' - + fsh = FsHelper(u_boot_config, fs_type, 128, '128MB') 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 + fsh.setup() + mount_dir = fsh.srcdir - try: md5val = {} # Test Case 1 check_call('mkdir %s/test1' % mount_dir, shell=True) @@ -657,21 +593,15 @@ def fs_obj_rename(request, u_boot_config): new_rand_file('%s/test11/dir1/file1' % mount_dir) md5val['test11'] = file_hash('%s/test11/dir1/file1' % mount_dir) - try: - # 128MiB volume - fs_img = fs_helper.mk_fs(u_boot_config, fs_type, 0x8000000, '128MB', mount_dir) - except CalledProcessError as err: - pytest.skip('Creating failed for filesystem: ' + fs_type + '. {}'.format(err)) - return + fsh.mk_fs() except CalledProcessError: pytest.skip('Setup failed for filesystem: ' + fs_type) return else: - yield [fs_ubtype, fs_img, md5val] + yield [fs_ubtype, fsh.fs_img, md5val] finally: - call('rm -rf %s' % mount_dir, shell=True) - call('rm -f %s' % fs_img, shell=True) + fsh.cleanup() # # Fixture for fat test @@ -697,19 +627,19 @@ def fs_obj_fat(request, u_boot_config): MIN_FAT16_SIZE = 8208 * 1024 fs_type = request.param - fs_img = '' fs_ubtype = fstype_to_ubname(fs_type) check_ubconfig(u_boot_config, fs_ubtype) fs_size = MAX_FAT12_SIZE if fs_type == 'fat12' else MIN_FAT16_SIZE + size_mb = (fs_size + (1 << 20) - 1) >> 20 + fsh = FsHelper(u_boot_config, fs_type, size_mb, f'{fs_size}') try: - # the volume size depends on the filesystem - fs_img = fs_helper.mk_fs(u_boot_config, fs_type, fs_size, f'{fs_size}', None, 1024) + fsh.mk_fs() except: pytest.skip('Setup failed for filesystem: ' + fs_type) return else: - yield [fs_ubtype, fs_img] - call('rm -f %s' % fs_img, shell=True) + yield [fs_ubtype, fsh.fs_img] + fsh.cleanup() diff --git a/test/py/tests/test_net.py b/test/py/tests/test_net.py index 6ef02e53389..27cdd73fd49 100644 --- a/test/py/tests/test_net.py +++ b/test/py/tests/test_net.py @@ -201,7 +201,7 @@ def test_net_dhcp6(ubman): global net6_set_up net6_set_up = True [email protected]('net', 'net_lwip') [email protected]('net') def test_net_setup_static(ubman): """Set up a static IP configuration. diff --git a/test/py/tests/test_scsi.py b/test/py/tests/test_scsi.py index 2a35e47e558..4390ff65845 100644 --- a/test/py/tests/test_scsi.py +++ b/test/py/tests/test_scsi.py @@ -39,16 +39,6 @@ def scsi_setup(ubman): return dev_num, dev_type, dev_size @pytest.mark.buildconfigspec('cmd_scsi') -def test_scsi_reset(ubman): - dev_num, dev_type, dev_size = scsi_setup(ubman) - output = ubman.run_command('scsi reset') - assert f'Device {dev_num}:' in output - assert f'Type: {dev_type}' in output - assert f'Capacity: {dev_size}' in output - output = ubman.run_command('echo $?') - assert output.endswith('0') - [email protected]('cmd_scsi') def test_scsi_info(ubman): dev_num, dev_type, dev_size = scsi_setup(ubman) output = ubman.run_command('scsi info') diff --git a/test/py/tests/test_ut.py b/test/py/tests/test_ut.py index 98641a46c1d..dce5a37dd35 100644 --- a/test/py/tests/test_ut.py +++ b/test/py/tests/test_ut.py @@ -17,6 +17,7 @@ import pytest import utils # pylint: disable=E0611 from tests import fs_helper +from fs_helper import DiskHelper, FsHelper from test_android import test_abootimg def mkdir_cond(dirname): @@ -45,7 +46,6 @@ def setup_bootmenu_image(ubman): This is modelled on Armbian 22.08 Jammy """ mmc_dev = 4 - fname, mnt = fs_helper.setup_image(ubman, mmc_dev, 0x83) script = '''# DO NOT EDIT THIS FILE # @@ -121,7 +121,9 @@ booti ${kernel_addr_r} ${ramdisk_addr_r} ${fdt_addr_r} # Recompile with: # mkimage -C none -A arm -T script -d /boot/boot.cmd /boot/boot.scr ''' - bootdir = os.path.join(mnt, 'boot') + fsh = FsHelper(ubman.config, 'ext4', 18, 'mmc') + fsh.setup() + bootdir = os.path.join(fsh.srcdir, 'boot') mkdir_cond(bootdir) cmd_fname = os.path.join(bootdir, 'boot.cmd') scr_fname = os.path.join(bootdir, 'boot.scr') @@ -150,36 +152,30 @@ booti ${kernel_addr_r} ${ramdisk_addr_r} ${fdt_addr_r} utils.run_and_log( ubman, f'echo here {kernel} {symlink}') os.symlink(kernel, symlink) + fsh.mk_fs() + img = DiskHelper(ubman.config, mmc_dev, 'mmc', True) + img.add_fs(fsh, DiskHelper.EXT4) + img.create() + fsh.cleanup() - fsfile = 'ext18M.img' - utils.run_and_log(ubman, f'fallocate -l 18M {fsfile}') - utils.run_and_log(ubman, f'mkfs.ext4 {fsfile} -d {mnt}') - copy_partition(ubman, fsfile, fname) - utils.run_and_log(ubman, f'rm -rf {mnt}') - utils.run_and_log(ubman, f'rm -f {fsfile}') -def setup_bootflow_image(ubman): - """Create a 20MB disk image with a single FAT partition""" - mmc_dev = 1 - fname, mnt = fs_helper.setup_image(ubman, mmc_dev, 0xc, second_part=True) +def setup_extlinux_image(ubman, devnum, basename, vmlinux, initrd, dtbdir, + script): + """Create a 20MB disk image with a single FAT partition - vmlinux = 'vmlinuz-5.3.7-301.fc31.armv7hl' - initrd = 'initramfs-5.3.7-301.fc31.armv7hl.img' - dtbdir = 'dtb-5.3.7-301.fc31.armv7hl' - script = '''# extlinux.conf generated by appliance-creator -ui menu.c32 -menu autoboot Welcome to Fedora-Workstation-armhfp-31-1.9. Automatic boot in # second{,s}. Press a key for options. -menu title Fedora-Workstation-armhfp-31-1.9 Boot Options. -menu hidden -timeout 20 -totaltimeout 600 + Args: + ubman (ConsoleBase): Console to use + devnum (int): Device number to use, e.g. 1 + basename (str): Base name to use in the filename, e.g. 'mmc' + vmlinux (str): Kernel filename + initrd (str): Ramdisk filename + dtbdir (str or None): Devicetree filename + script (str): Script to place in the extlinux.conf file + """ + fsh = FsHelper(ubman.config, 'vfat', 18, prefix=basename) + fsh.setup() -label Fedora-Workstation-armhfp-31-1.9 (5.3.7-301.fc31.armv7hl) - kernel /%s - append ro root=UUID=9732b35b-4cd5-458b-9b91-80f7047e0b8a rhgb quiet LANG=en_US.UTF-8 cma=192MB cma=256MB - fdtdir /%s/ - initrd /%s''' % (vmlinux, dtbdir, initrd) - ext = os.path.join(mnt, 'extlinux') + ext = os.path.join(fsh.srcdir, 'extlinux') mkdir_cond(ext) conf = os.path.join(ext, 'extlinux.conf') @@ -191,24 +187,57 @@ label Fedora-Workstation-armhfp-31-1.9 (5.3.7-301.fc31.armv7hl) fd.write(gzip.compress(b'vmlinux')) mkimage = ubman.config.build_dir + '/tools/mkimage' utils.run_and_log( - ubman, f'{mkimage} -f auto -d {inf} {os.path.join(mnt, vmlinux)}') + ubman, f'{mkimage} -f auto -d {inf} {os.path.join(fsh.srcdir, vmlinux)}') - with open(os.path.join(mnt, initrd), 'w', encoding='ascii') as fd: + with open(os.path.join(fsh.srcdir, initrd), 'w', encoding='ascii') as fd: print('initrd', file=fd) - mkdir_cond(os.path.join(mnt, dtbdir)) + if dtbdir: + mkdir_cond(os.path.join(fsh.srcdir, dtbdir)) - dtb_file = os.path.join(mnt, f'{dtbdir}/sandbox.dtb') - utils.run_and_log( - ubman, f'dtc -o {dtb_file}', stdin=b'/dts-v1/; / {};') + dtb_file = os.path.join(fsh.srcdir, f'{dtbdir}/sandbox.dtb') + utils.run_and_log( + ubman, f'dtc -o {dtb_file}', stdin=b'/dts-v1/; / {};') + + fsh.mk_fs() + + img = DiskHelper(ubman.config, devnum, basename, True) + img.add_fs(fsh, DiskHelper.VFAT, bootable=True) + + ext4 = FsHelper(ubman.config, 'ext4', 1, prefix=basename) + ext4.setup() + ext4.mk_fs() + + img.add_fs(ext4, DiskHelper.EXT4) + img.create() + fsh.cleanup() + +def setup_fedora_image(ubman, devnum, basename): + """Create a 20MB Fedora disk image with a single FAT partition + + Args: + ubman (ConsoleBase): Console to use + devnum (int): Device number to use, e.g. 1 + basename (str): Base name to use in the filename, e.g. 'mmc' + """ + vmlinux = 'vmlinuz-5.3.7-301.fc31.armv7hl' + initrd = 'initramfs-5.3.7-301.fc31.armv7hl.img' + dtbdir = 'dtb-5.3.7-301.fc31.armv7hl' + script = '''# extlinux.conf generated by appliance-creator +ui menu.c32 +menu autoboot Welcome to Fedora-Workstation-armhfp-31-1.9. Automatic boot in # second{,s}. Press a key for options. +menu title Fedora-Workstation-armhfp-31-1.9 Boot Options. +menu hidden +timeout 20 +totaltimeout 600 - fsfile = 'vfat18M.img' - utils.run_and_log(ubman, f'fallocate -l 18M {fsfile}') - utils.run_and_log(ubman, f'mkfs.vfat {fsfile}') - utils.run_and_log(ubman, ['sh', '-c', f'mcopy -i {fsfile} {mnt}/* ::/']) - copy_partition(ubman, fsfile, fname) - utils.run_and_log(ubman, f'rm -rf {mnt}') - utils.run_and_log(ubman, f'rm -f {fsfile}') +label Fedora-Workstation-armhfp-31-1.9 (5.3.7-301.fc31.armv7hl) + kernel /%s + append ro root=UUID=9732b35b-4cd5-458b-9b91-80f7047e0b8a rhgb quiet LANG=en_US.UTF-8 cma=192MB cma=256MB + fdtdir /%s/ + initrd /%s''' % (vmlinux, dtbdir, initrd) + setup_extlinux_image(ubman, devnum, basename, vmlinux, initrd, dtbdir, + script) def setup_cros_image(ubman): """Create a 20MB disk image with ChromiumOS partitions""" @@ -513,8 +542,8 @@ def test_ut_dm_init(ubman): utils.run_and_log( ubman, f'sfdisk {fn}', stdin=b'type=83') - fs_helper.mk_fs(ubman.config, 'ext2', 0x200000, '2MB', None) - fs_helper.mk_fs(ubman.config, 'fat32', 0x100000, '1MB', None) + FsHelper(ubman.config, 'ext2', 2, '2MB').mk_fs() + FsHelper(ubman.config, 'fat32', 1, '1MB').mk_fs() mmc_dev = 6 fn = os.path.join(ubman.config.source_dir, f'mmc{mmc_dev}.img') @@ -532,11 +561,9 @@ def test_ut_dm_init(ubman): def setup_efi_image(ubman): """Create a 20MB disk image with an EFI app on it""" devnum = 1 - basename = 'flash' - fname, mnt = fs_helper.setup_image(ubman, devnum, 0xc, second_part=True, - basename=basename) - - efi_dir = os.path.join(mnt, 'EFI') + fsh = FsHelper(ubman.config, 'vfat', 18, 'flash') + fsh.setup() + efi_dir = os.path.join(fsh.srcdir, 'EFI') mkdir_cond(efi_dir) bootdir = os.path.join(efi_dir, 'BOOT') mkdir_cond(bootdir) @@ -546,66 +573,53 @@ def setup_efi_image(ubman): with open(efi_src, 'rb') as inf: with open(efi_dst, 'wb') as outf: outf.write(inf.read()) - fsfile = 'vfat18M.img' - utils.run_and_log(ubman, f'fallocate -l 18M {fsfile}') - utils.run_and_log(ubman, f'mkfs.vfat {fsfile}') - utils.run_and_log(ubman, ['sh', '-c', f'mcopy -vs -i {fsfile} {mnt}/* ::/']) - copy_partition(ubman, fsfile, fname) - utils.run_and_log(ubman, f'rm -rf {mnt}') - utils.run_and_log(ubman, f'rm -f {fsfile}') + + fsh.mk_fs() + + img = DiskHelper(ubman.config, devnum, 'flash', True) + img.add_fs(fsh, DiskHelper.VFAT) + img.create() + fsh.cleanup() + def setup_rauc_image(ubman): """Create a 40MB disk image with an A/B RAUC system on it""" mmc_dev = 10 - fname = os.path.join(ubman.config.source_dir, f'mmc{mmc_dev}.img') - mnt = ubman.config.persistent_data_dir - - spec = 'type=c, size=8M, start=1M, bootable\n' \ - 'type=83, size=10M\n' \ - 'type=c, size=8M, bootable\n' \ - 'type=83, size=10M' - utils.run_and_log(ubman, f'qemu-img create {fname} 40M') - utils.run_and_log(ubman, ['sh', '-c', f'printf "{spec}" | sfdisk {fname}']) + boot = FsHelper(ubman.config, 'fat32', 8, 'rauc_boot') + boot.setup() # Generate boot script script = '# dummy boot script' - bootdir = os.path.join(mnt, 'boot') - utils.run_and_log(ubman, f'mkdir -p {bootdir}') - cmd_fname = os.path.join(bootdir, 'boot.cmd') - scr_fname = os.path.join(bootdir, 'boot.scr') + cmd_fname = os.path.join(boot.srcdir, 'boot.cmd') + scr_fname = os.path.join(boot.srcdir, 'boot.scr') with open(cmd_fname, 'w', encoding='ascii') as outf: print(script, file=outf) mkimage = os.path.join(ubman.config.build_dir, 'tools/mkimage') utils.run_and_log( ubman, f'{mkimage} -C none -A arm -T script -d {cmd_fname} {scr_fname}') - utils.run_and_log(ubman, f'rm -f {cmd_fname}') - - # Generate empty rootfs - rootdir = os.path.join(mnt, 'root') - utils.run_and_log(ubman, f'mkdir -p {rootdir}') - - # Create boot filesystem image with boot script in it and copy to disk image - fsfile = f'rauc_boot.fat32.img' - fs_helper.mk_fs(ubman.config, 'fat32', 0x800000, fsfile.split('.')[0], bootdir) - utils.run_and_log(ubman, f'dd if={mnt}/{fsfile} of=mmc{mmc_dev}.img bs=1M seek=1 conv=notrunc') - utils.run_and_log(ubman, f'dd if={mnt}/{fsfile} of=mmc{mmc_dev}.img bs=1M seek=19 conv=notrunc') - utils.run_and_log(ubman, f'rm -f {scr_fname}') - - # Create empty root filesystem image and copy to disk image - fsfile = f'rauc_root.ext4.img' - fs_helper.mk_fs(ubman.config, 'ext4', 0xa00000, fsfile.split('.')[0], rootdir) - utils.run_and_log(ubman, f'dd if={mnt}/{fsfile} of=mmc{mmc_dev}.img bs=1M seek=9 conv=notrunc') - utils.run_and_log(ubman, f'dd if={mnt}/{fsfile} of=mmc{mmc_dev}.img bs=1M seek=27 conv=notrunc') - utils.run_and_log(ubman, f'rm -f {fsfile}') + os.remove(cmd_fname) + boot.mk_fs() + + root = FsHelper(ubman.config, 'ext4', 10, 'rauc_root') + root.mk_fs() + + img = DiskHelper(ubman.config, mmc_dev, 'mmc', True) + img.add_fs(boot, DiskHelper.VFAT, bootable=True) + img.add_fs(root, DiskHelper.EXT4) + img.add_fs(boot, DiskHelper.VFAT, bootable=True) + img.add_fs(root, DiskHelper.EXT4) + img.create() + boot.cleanup() + root.cleanup() @pytest.mark.buildconfigspec('cmd_bootflow') @pytest.mark.buildconfigspec('sandbox') def test_ut_dm_init_bootstd(ubman): """Initialise data for bootflow tests""" - setup_bootflow_image(ubman) + setup_fedora_image(ubman, 1, 'mmc') setup_bootmenu_image(ubman) setup_cedit_file(ubman) setup_cros_image(ubman) diff --git a/test/py/tests/test_vboot.py b/test/py/tests/test_vboot.py index 19f3f981379..55518bed07e 100644 --- a/test/py/tests/test_vboot.py +++ b/test/py/tests/test_vboot.py @@ -372,6 +372,16 @@ def test_vboot(ubman, name, sha_algo, padding, sign_options, required, msg = 'Signature checking prevents use of unit addresses (@) in nodes' run_bootm(sha_algo, 'evil kernel@', msg, False, efit) + # Try doing a clone of the images + efit = '%stest.evilclone.fit' % tmpdir + shutil.copyfile(fit, efit) + vboot_evil.add_evil_node(fit, efit, evil_kernel, 'clone') + + utils.run_and_log_expect_exception( + ubman, [fit_check_sign, '-f', efit, '-k', dtb], + 1, 'Failed to verify required signature') + run_bootm(sha_algo, 'evil clone', 'Bad Data Hash', False, efit) + # Create a new properly signed fit and replace header bytes make_fit('sign-configs-%s%s.its' % (sha_algo, padding), ubman, mkimage, dtc_args, datadir, fit) sign_fit(sha_algo, sign_options) diff --git a/test/py/tests/test_xxd.py b/test/py/tests/test_xxd.py index c457c54146c..a5f63f006fe 100644 --- a/test/py/tests/test_xxd.py +++ b/test/py/tests/test_xxd.py @@ -4,8 +4,7 @@ """ import pytest -from subprocess import call, check_call, CalledProcessError -from tests import fs_helper +from tests.fs_helper import FsHelper @pytest.mark.boardspec('sandbox') @pytest.mark.buildconfigspec('cmd_xxd') @@ -15,26 +14,13 @@ def test_xxd(ubman): Args: ubman -- U-Boot console """ - try: - scratch_dir = ubman.config.persistent_data_dir + '/scratch' - - check_call('mkdir -p %s' % scratch_dir, shell=True) - - with open(scratch_dir + '/hello', 'w', encoding = 'ascii') as file: - file.write('hello world\n\x00\x01\x02\x03\x04\x05') - - xxd_data = fs_helper.mk_fs(ubman.config, 'vfat', 0x100000, - 'test_xxd', scratch_dir) - response = ubman.run_command_list([ f'host bind 0 {xxd_data}', - 'xxd host 0 hello']) + with FsHelper(ubman.config, 'vfat', 1, 'test_xxd') as fsh: + with open(f'{fsh.srcdir}/hello', 'w', encoding = 'ascii') as outf: + outf.write('hello world\n\x00\x01\x02\x03\x04\x05') + fsh.mk_fs() + response = ubman.run_command_list([f'host bind 0 {fsh.fs_img}', + 'xxd host 0 hello']) assert '00000000: 68 65 6c 6c 6f 20 77 6f 72 6c 64 0a 00 01 02 03 hello world.....\r\r\n' + \ '00000010: 04 05 ..' \ in response - except CalledProcessError as err: - pytest.skip('Preparing test_xxd image failed') - call('rm -f %s' % xxd_data, shell=True) - return - finally: - call('rm -rf %s' % scratch_dir, shell=True) - call('rm -f %s' % xxd_data, shell=True) diff --git a/test/py/tests/vboot_evil.py b/test/py/tests/vboot_evil.py index e2b0cd65468..5720631ae52 100644 --- a/test/py/tests/vboot_evil.py +++ b/test/py/tests/vboot_evil.py @@ -14,6 +14,7 @@ FDT_END = 0x9 FAKE_ROOT_ATTACK = 0 KERNEL_AT = 1 +IMAGE_CLONE = 2 MAGIC = 0xd00dfeed @@ -274,6 +275,66 @@ def get_prop_value(dt_struct, dt_strings, prop_path): return tag_data +def image_clone_attack(dt_struct, dt_strings, kernel_content, kernel_hash): + # 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') + + # since this is the last child in images! + loaded_fdt_name = get_prop_value(dt_struct, dt_strings, conf_path + '/fdt') + + loaded_fdt_name = str(loaded_fdt_name[:-1], 'utf-8') + + # determine boundaries of the images + (img_node_start, img_node_end) = (determine_offset( + dt_struct, dt_strings, '/images')) + if img_node_start is None and img_node_end is None: + print('Fatal error, unable to find images node') + sys.exit() + + # copy the images node + img_node_copy = dt_struct[img_node_start:img_node_end] + + # create an additional empty node + empty_node = struct.pack('>I', FDT_BEGIN_NODE) + b"EMPTYNO\0" + struct.pack('>I', FDT_END_NODE) + # right before the end, we add it! + img_node_copy = img_node_copy[:-4] + empty_node + img_node_copy[-4:] + + # insert the copy inside the tree + dt_struct = dt_struct[:img_node_end-4] + \ + img_node_copy + empty_node + dt_struct[img_node_end-4:] + + # change the content of the kernel being loaded + dt_struct = change_property_value( + dt_struct, dt_strings, '/images/' + loaded_kernel + '/data', kernel_content) + + # change the content of the kernel being loaded + dt_struct = change_property_value( + dt_struct, dt_strings, '/images/' + loaded_kernel + '/hash-1/value', kernel_hash) + + # finally, the main bug: change the hashed nodes to use the images clone instead! + hashed_nodes: bytes = get_prop_value(dt_struct, dt_strings, conf_path + '/signature/hashed-nodes') + print(f"got hashed nodes: {hashed_nodes}") + nodes = hashed_nodes.split(b"\0") + patched_nodes = [] + for node in nodes: + new_node = node + if node.startswith(b"/images/"): + # reparent the node + new_node = b"/images" + node + patched_nodes.append(new_node) + hashed_nodes = b"\0".join(patched_nodes) + dt_struct = change_property_value( + dt_struct, dt_strings, conf_path + '/signature/hashed-nodes', hashed_nodes) + return dt_struct def kernel_at_attack(dt_struct, dt_strings, kernel_content, kernel_hash): """Conduct the kernel@ attack @@ -419,6 +480,8 @@ def add_evil_node(in_fname, out_fname, kernel_fname, attack): attack = FAKE_ROOT_ATTACK elif attack == 'kernel@': attack = KERNEL_AT + elif attack == 'clone': + attack = IMAGE_CLONE else: raise ValueError('Unknown attack name!') @@ -455,6 +518,8 @@ def add_evil_node(in_fname, out_fname, kernel_fname, attack): elif attack == KERNEL_AT: dt_struct = kernel_at_attack(dt_struct, dt_strings, kernel_content, hash_digest) + elif attack == IMAGE_CLONE: + dt_struct = image_clone_attack(dt_struct, dt_strings, kernel_content, hash_digest) # now rebuild the new file size_dt_strings = len(dt_strings) |
