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