13045d7f0SStephen Warren# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved. 23045d7f0SStephen Warren# 33045d7f0SStephen Warren# SPDX-License-Identifier: GPL-2.0 43045d7f0SStephen Warren 5d054f4c2SStephen Warren# Test U-Boot's "ums" command. The test starts UMS in U-Boot, waits for USB 6d054f4c2SStephen Warren# device enumeration on the host, reads a small block of data from the UMS 7d054f4c2SStephen Warren# block device, optionally mounts a partition and performs filesystem-based 8d054f4c2SStephen Warren# read/write tests, and finally aborts the "ums" command in U-Boot. 93045d7f0SStephen Warren 103045d7f0SStephen Warrenimport os 11d054f4c2SStephen Warrenimport os.path 123045d7f0SStephen Warrenimport pytest 13d054f4c2SStephen Warrenimport re 143045d7f0SStephen Warrenimport time 15d054f4c2SStephen Warrenimport u_boot_utils 163045d7f0SStephen Warren 17*e8debf39SStephen Warren""" 183045d7f0SStephen WarrenNote: This test relies on: 193045d7f0SStephen Warren 203045d7f0SStephen Warrena) boardenv_* to contain configuration values to define which USB ports are 213045d7f0SStephen Warrenavailable for testing. Without this, this test will be automatically skipped. 223045d7f0SStephen WarrenFor example: 233045d7f0SStephen Warren 24d054f4c2SStephen Warren# Leave this list empty if you have no block_devs below with writable 25d054f4c2SStephen Warren# partitions defined. 26d054f4c2SStephen Warrenenv__mount_points = ( 27d054f4c2SStephen Warren "/mnt/ubtest-mnt-p2371-2180-na", 28d054f4c2SStephen Warren) 29d054f4c2SStephen Warren 303045d7f0SStephen Warrenenv__usb_dev_ports = ( 31d054f4c2SStephen Warren { 32d054f4c2SStephen Warren "tgt_usb_ctlr": "0", 33d054f4c2SStephen Warren "host_ums_dev_node": "/dev/disk/by-path/pci-0000:00:14.0-usb-0:13:1.0-scsi-0:0:0:0", 34d054f4c2SStephen Warren }, 353045d7f0SStephen Warren) 363045d7f0SStephen Warren 373045d7f0SStephen Warrenenv__block_devs = ( 38d054f4c2SStephen Warren # eMMC; always present 39d054f4c2SStephen Warren { 40d054f4c2SStephen Warren "type": "mmc", 41d054f4c2SStephen Warren "id": "0", 42d054f4c2SStephen Warren # The following two properties are optional. 43d054f4c2SStephen Warren # If present, the partition will be mounted and a file written-to and 44d054f4c2SStephen Warren # read-from it. If missing, only a simple block read test will be 45d054f4c2SStephen Warren # performed. 46d054f4c2SStephen Warren "writable_fs_partition": 1, 47d054f4c2SStephen Warren "writable_fs_subdir": "tmp/", 48d054f4c2SStephen Warren }, 49d054f4c2SStephen Warren # SD card; present since I plugged one in 50d054f4c2SStephen Warren { 51d054f4c2SStephen Warren "type": "mmc", 52d054f4c2SStephen Warren "id": "1" 53d054f4c2SStephen Warren }, 543045d7f0SStephen Warren) 553045d7f0SStephen Warren 563045d7f0SStephen Warrenb) udev rules to set permissions on devices nodes, so that sudo is not 573045d7f0SStephen Warrenrequired. For example: 583045d7f0SStephen Warren 593045d7f0SStephen WarrenACTION=="add", SUBSYSTEM=="block", SUBSYSTEMS=="usb", KERNELS=="3-13", MODE:="666" 603045d7f0SStephen Warren 613045d7f0SStephen Warren(You may wish to change the group ID instead of setting the permissions wide 623045d7f0SStephen Warrenopen. All that matters is that the user ID running the test can access the 633045d7f0SStephen Warrendevice.) 64d054f4c2SStephen Warren 65d054f4c2SStephen Warrenc) /etc/fstab entries to allow the block device to be mounted without requiring 66d054f4c2SStephen Warrenroot permissions. For example: 67d054f4c2SStephen Warren 68d054f4c2SStephen Warren/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 69d054f4c2SStephen Warren 70d054f4c2SStephen WarrenThis entry is only needed if any block_devs above contain a 71d054f4c2SStephen Warrenwritable_fs_partition value. 72*e8debf39SStephen Warren""" 733045d7f0SStephen Warren 743045d7f0SStephen Warren@pytest.mark.buildconfigspec('cmd_usb_mass_storage') 753045d7f0SStephen Warrendef test_ums(u_boot_console, env__usb_dev_port, env__block_devs): 76*e8debf39SStephen Warren """Test the "ums" command; the host system must be able to enumerate a UMS 77d054f4c2SStephen Warren device when "ums" is running, block and optionally file I/O are tested, 78d054f4c2SStephen Warren and this device must disappear when "ums" is aborted. 79d054f4c2SStephen Warren 80d054f4c2SStephen Warren Args: 81d054f4c2SStephen Warren u_boot_console: A U-Boot console connection. 82d054f4c2SStephen Warren env__usb_dev_port: The single USB device-mode port specification on 83d054f4c2SStephen Warren which to run the test. See the file-level comment above for 84d054f4c2SStephen Warren details of the format. 85d054f4c2SStephen Warren env__block_devs: The list of block devices that the target U-Boot 86d054f4c2SStephen Warren device has attached. See the file-level comment above for details 87d054f4c2SStephen Warren of the format. 88d054f4c2SStephen Warren 89d054f4c2SStephen Warren Returns: 90d054f4c2SStephen Warren Nothing. 91*e8debf39SStephen Warren """ 92d054f4c2SStephen Warren 93d054f4c2SStephen Warren have_writable_fs_partition = 'writable_fs_partition' in env__block_devs[0] 94d054f4c2SStephen Warren if not have_writable_fs_partition: 95d054f4c2SStephen Warren # If 'writable_fs_subdir' is missing, we'll skip all parts of the 96d054f4c2SStephen Warren # testing which mount filesystems. 97d054f4c2SStephen Warren u_boot_console.log.warning( 98d054f4c2SStephen Warren 'boardenv missing "writable_fs_partition"; ' + 99d054f4c2SStephen Warren 'UMS testing will be limited.') 1003045d7f0SStephen Warren 1013045d7f0SStephen Warren tgt_usb_ctlr = env__usb_dev_port['tgt_usb_ctlr'] 1023045d7f0SStephen Warren host_ums_dev_node = env__usb_dev_port['host_ums_dev_node'] 1033045d7f0SStephen Warren 1043045d7f0SStephen Warren # We're interested in testing USB device mode on each port, not the cross- 1053045d7f0SStephen Warren # product of that with each device. So, just pick the first entry in the 1063045d7f0SStephen Warren # device list here. We'll test each block device somewhere else. 1073045d7f0SStephen Warren tgt_dev_type = env__block_devs[0]['type'] 1083045d7f0SStephen Warren tgt_dev_id = env__block_devs[0]['id'] 109d054f4c2SStephen Warren if have_writable_fs_partition: 110d054f4c2SStephen Warren mount_point = u_boot_console.config.env['env__mount_points'][0] 111d054f4c2SStephen Warren mount_subdir = env__block_devs[0]['writable_fs_subdir'] 112d054f4c2SStephen Warren part_num = env__block_devs[0]['writable_fs_partition'] 113d054f4c2SStephen Warren host_ums_part_node = '%s-part%d' % (host_ums_dev_node, part_num) 114d054f4c2SStephen Warren else: 115d054f4c2SStephen Warren host_ums_part_node = host_ums_dev_node 1163045d7f0SStephen Warren 117d054f4c2SStephen Warren test_f = u_boot_utils.PersistentRandomFile(u_boot_console, 'ums.bin', 118d054f4c2SStephen Warren 1024 * 1024); 119d054f4c2SStephen Warren if have_writable_fs_partition: 120d054f4c2SStephen Warren mounted_test_fn = mount_point + '/' + mount_subdir + test_f.fn 121d054f4c2SStephen Warren 122d054f4c2SStephen Warren def start_ums(): 123*e8debf39SStephen Warren """Start U-Boot's ums shell command. 124d054f4c2SStephen Warren 125d054f4c2SStephen Warren This also waits for the host-side USB enumeration process to complete. 126d054f4c2SStephen Warren 127d054f4c2SStephen Warren Args: 128d054f4c2SStephen Warren None. 129d054f4c2SStephen Warren 130d054f4c2SStephen Warren Returns: 131d054f4c2SStephen Warren Nothing. 132*e8debf39SStephen Warren """ 133d054f4c2SStephen Warren 134d054f4c2SStephen Warren u_boot_console.log.action( 135d054f4c2SStephen Warren 'Starting long-running U-Boot ums shell command') 1363045d7f0SStephen Warren cmd = 'ums %s %s %s' % (tgt_usb_ctlr, tgt_dev_type, tgt_dev_id) 137d054f4c2SStephen Warren u_boot_console.run_command(cmd, wait_for_prompt=False) 138d054f4c2SStephen Warren u_boot_console.wait_for(re.compile('UMS: LUN.*[\r\n]')) 139d054f4c2SStephen Warren fh = u_boot_utils.wait_until_open_succeeds(host_ums_part_node) 140d054f4c2SStephen Warren u_boot_console.log.action('Reading raw data from UMS device') 1413045d7f0SStephen Warren fh.read(4096) 1423045d7f0SStephen Warren fh.close() 143d054f4c2SStephen Warren 144d054f4c2SStephen Warren def mount(): 145*e8debf39SStephen Warren """Mount the block device that U-Boot exports. 146d054f4c2SStephen Warren 147d054f4c2SStephen Warren Args: 148d054f4c2SStephen Warren None. 149d054f4c2SStephen Warren 150d054f4c2SStephen Warren Returns: 151d054f4c2SStephen Warren Nothing. 152*e8debf39SStephen Warren """ 153d054f4c2SStephen Warren 154d054f4c2SStephen Warren u_boot_console.log.action('Mounting exported UMS device') 155d054f4c2SStephen Warren cmd = ('/bin/mount', host_ums_part_node) 156d054f4c2SStephen Warren u_boot_utils.run_and_log(u_boot_console, cmd) 157d054f4c2SStephen Warren 158d054f4c2SStephen Warren def umount(ignore_errors): 159*e8debf39SStephen Warren """Unmount the block device that U-Boot exports. 160d054f4c2SStephen Warren 161d054f4c2SStephen Warren Args: 162d054f4c2SStephen Warren ignore_errors: Ignore any errors. This is useful if an error has 163d054f4c2SStephen Warren already been detected, and the code is performing best-effort 164d054f4c2SStephen Warren cleanup. In this case, we do not want to mask the original 165d054f4c2SStephen Warren error by "honoring" any new errors. 166d054f4c2SStephen Warren 167d054f4c2SStephen Warren Returns: 168d054f4c2SStephen Warren Nothing. 169*e8debf39SStephen Warren """ 170d054f4c2SStephen Warren 171d054f4c2SStephen Warren u_boot_console.log.action('Unmounting UMS device') 172d054f4c2SStephen Warren cmd = ('/bin/umount', host_ums_part_node) 173d054f4c2SStephen Warren u_boot_utils.run_and_log(u_boot_console, cmd, ignore_errors) 174d054f4c2SStephen Warren 175d054f4c2SStephen Warren def stop_ums(ignore_errors): 176*e8debf39SStephen Warren """Stop U-Boot's ums shell command from executing. 177d054f4c2SStephen Warren 178d054f4c2SStephen Warren This also waits for the host-side USB de-enumeration process to 179d054f4c2SStephen Warren complete. 180d054f4c2SStephen Warren 181d054f4c2SStephen Warren Args: 182d054f4c2SStephen Warren ignore_errors: Ignore any errors. This is useful if an error has 183d054f4c2SStephen Warren already been detected, and the code is performing best-effort 184d054f4c2SStephen Warren cleanup. In this case, we do not want to mask the original 185d054f4c2SStephen Warren error by "honoring" any new errors. 186d054f4c2SStephen Warren 187d054f4c2SStephen Warren Returns: 188d054f4c2SStephen Warren Nothing. 189*e8debf39SStephen Warren """ 190d054f4c2SStephen Warren 191d054f4c2SStephen Warren u_boot_console.log.action( 192d054f4c2SStephen Warren 'Stopping long-running U-Boot ums shell command') 1933045d7f0SStephen Warren u_boot_console.ctrlc() 194d054f4c2SStephen Warren u_boot_utils.wait_until_file_open_fails(host_ums_part_node, 195d054f4c2SStephen Warren ignore_errors) 196d054f4c2SStephen Warren 197d054f4c2SStephen Warren ignore_cleanup_errors = True 198d054f4c2SStephen Warren try: 199d054f4c2SStephen Warren start_ums() 200d054f4c2SStephen Warren if not have_writable_fs_partition: 201d054f4c2SStephen Warren # Skip filesystem-based testing if not configured 202d054f4c2SStephen Warren return 203d054f4c2SStephen Warren try: 204d054f4c2SStephen Warren mount() 205d054f4c2SStephen Warren u_boot_console.log.action('Writing test file via UMS') 206d054f4c2SStephen Warren cmd = ('rm', '-f', mounted_test_fn) 207d054f4c2SStephen Warren u_boot_utils.run_and_log(u_boot_console, cmd) 208d054f4c2SStephen Warren if os.path.exists(mounted_test_fn): 209d054f4c2SStephen Warren raise Exception('Could not rm target UMS test file') 210d054f4c2SStephen Warren cmd = ('cp', test_f.abs_fn, mounted_test_fn) 211d054f4c2SStephen Warren u_boot_utils.run_and_log(u_boot_console, cmd) 212d054f4c2SStephen Warren ignore_cleanup_errors = False 213d054f4c2SStephen Warren finally: 214d054f4c2SStephen Warren umount(ignore_errors=ignore_cleanup_errors) 215d054f4c2SStephen Warren finally: 216d054f4c2SStephen Warren stop_ums(ignore_errors=ignore_cleanup_errors) 217d054f4c2SStephen Warren 218d054f4c2SStephen Warren ignore_cleanup_errors = True 219d054f4c2SStephen Warren try: 220d054f4c2SStephen Warren start_ums() 221d054f4c2SStephen Warren try: 222d054f4c2SStephen Warren mount() 223d054f4c2SStephen Warren u_boot_console.log.action('Reading test file back via UMS') 224d054f4c2SStephen Warren read_back_hash = u_boot_utils.md5sum_file(mounted_test_fn) 225d054f4c2SStephen Warren cmd = ('rm', '-f', mounted_test_fn) 226d054f4c2SStephen Warren u_boot_utils.run_and_log(u_boot_console, cmd) 227d054f4c2SStephen Warren ignore_cleanup_errors = False 228d054f4c2SStephen Warren finally: 229d054f4c2SStephen Warren umount(ignore_errors=ignore_cleanup_errors) 230d054f4c2SStephen Warren finally: 231d054f4c2SStephen Warren stop_ums(ignore_errors=ignore_cleanup_errors) 232d054f4c2SStephen Warren 233d054f4c2SStephen Warren written_hash = test_f.content_hash 234d054f4c2SStephen Warren assert(written_hash == read_back_hash) 235