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