Stephen Warren | 3045d7f | 2016-01-15 11:15:30 -0700 | [diff] [blame] | 1 | # SPDX-License-Identifier: GPL-2.0 |
Tom Rini | 83d290c | 2018-05-06 17:58:06 -0400 | [diff] [blame] | 2 | # Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved. |
Stephen Warren | 3045d7f | 2016-01-15 11:15:30 -0700 | [diff] [blame] | 3 | |
Stephen Warren | d054f4c | 2016-01-22 12:30:13 -0700 | [diff] [blame] | 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. |
Stephen Warren | 3045d7f | 2016-01-15 11:15:30 -0700 | [diff] [blame] | 8 | |
| 9 | import os |
Stephen Warren | d054f4c | 2016-01-22 12:30:13 -0700 | [diff] [blame] | 10 | import os.path |
Stephen Warren | 3045d7f | 2016-01-15 11:15:30 -0700 | [diff] [blame] | 11 | import pytest |
Stephen Warren | d054f4c | 2016-01-22 12:30:13 -0700 | [diff] [blame] | 12 | import re |
Stephen Warren | 3045d7f | 2016-01-15 11:15:30 -0700 | [diff] [blame] | 13 | import time |
Stephen Warren | d054f4c | 2016-01-22 12:30:13 -0700 | [diff] [blame] | 14 | import u_boot_utils |
Stephen Warren | 3045d7f | 2016-01-15 11:15:30 -0700 | [diff] [blame] | 15 | |
Stephen Warren | e8debf3 | 2016-01-26 13:41:30 -0700 | [diff] [blame] | 16 | """ |
Stephen Warren | 3045d7f | 2016-01-15 11:15:30 -0700 | [diff] [blame] | 17 | Note: This test relies on: |
| 18 | |
| 19 | a) boardenv_* to contain configuration values to define which USB ports are |
| 20 | available for testing. Without this, this test will be automatically skipped. |
| 21 | For example: |
| 22 | |
Stephen Warren | d054f4c | 2016-01-22 12:30:13 -0700 | [diff] [blame] | 23 | # Leave this list empty if you have no block_devs below with writable |
| 24 | # partitions defined. |
| 25 | env__mount_points = ( |
Simon Glass | 871bf7d | 2018-12-27 08:11:13 -0700 | [diff] [blame] | 26 | '/mnt/ubtest-mnt-p2371-2180-na', |
Stephen Warren | d054f4c | 2016-01-22 12:30:13 -0700 | [diff] [blame] | 27 | ) |
| 28 | |
Stephen Warren | 3045d7f | 2016-01-15 11:15:30 -0700 | [diff] [blame] | 29 | env__usb_dev_ports = ( |
Stephen Warren | d054f4c | 2016-01-22 12:30:13 -0700 | [diff] [blame] | 30 | { |
Simon Glass | 871bf7d | 2018-12-27 08:11:13 -0700 | [diff] [blame] | 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', |
Stephen Warren | d054f4c | 2016-01-22 12:30:13 -0700 | [diff] [blame] | 34 | }, |
Stephen Warren | 3045d7f | 2016-01-15 11:15:30 -0700 | [diff] [blame] | 35 | ) |
| 36 | |
| 37 | env__block_devs = ( |
Stephen Warren | d054f4c | 2016-01-22 12:30:13 -0700 | [diff] [blame] | 38 | # eMMC; always present |
| 39 | { |
Simon Glass | 871bf7d | 2018-12-27 08:11:13 -0700 | [diff] [blame] | 40 | 'fixture_id': 'emmc', |
| 41 | 'type': 'mmc', |
| 42 | 'id': '0', |
Stephen Warren | d054f4c | 2016-01-22 12:30:13 -0700 | [diff] [blame] | 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. |
Simon Glass | 871bf7d | 2018-12-27 08:11:13 -0700 | [diff] [blame] | 47 | 'writable_fs_partition': 1, |
| 48 | 'writable_fs_subdir': 'tmp/', |
Stephen Warren | d054f4c | 2016-01-22 12:30:13 -0700 | [diff] [blame] | 49 | }, |
| 50 | # SD card; present since I plugged one in |
| 51 | { |
Simon Glass | 871bf7d | 2018-12-27 08:11:13 -0700 | [diff] [blame] | 52 | 'fixture_id': 'sd', |
| 53 | 'type': 'mmc', |
| 54 | 'id': '1' |
Stephen Warren | d054f4c | 2016-01-22 12:30:13 -0700 | [diff] [blame] | 55 | }, |
Stephen Warren | 3045d7f | 2016-01-15 11:15:30 -0700 | [diff] [blame] | 56 | ) |
| 57 | |
| 58 | b) udev rules to set permissions on devices nodes, so that sudo is not |
| 59 | required. For example: |
| 60 | |
| 61 | ACTION=="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 |
| 64 | open. All that matters is that the user ID running the test can access the |
| 65 | device.) |
Stephen Warren | d054f4c | 2016-01-22 12:30:13 -0700 | [diff] [blame] | 66 | |
| 67 | c) /etc/fstab entries to allow the block device to be mounted without requiring |
| 68 | root 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 | |
| 72 | This entry is only needed if any block_devs above contain a |
| 73 | writable_fs_partition value. |
Stephen Warren | e8debf3 | 2016-01-26 13:41:30 -0700 | [diff] [blame] | 74 | """ |
Stephen Warren | 3045d7f | 2016-01-15 11:15:30 -0700 | [diff] [blame] | 75 | |
Stephen Warren | 3045d7f | 2016-01-15 11:15:30 -0700 | [diff] [blame] | 76 | @pytest.mark.buildconfigspec('cmd_usb_mass_storage') |
| 77 | def test_ums(u_boot_console, env__usb_dev_port, env__block_devs): |
Stephen Warren | e8debf3 | 2016-01-26 13:41:30 -0700 | [diff] [blame] | 78 | """Test the "ums" command; the host system must be able to enumerate a UMS |
Stephen Warren | d054f4c | 2016-01-22 12:30:13 -0700 | [diff] [blame] | 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 | u_boot_console: 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. |
Stephen Warren | e8debf3 | 2016-01-26 13:41:30 -0700 | [diff] [blame] | 93 | """ |
Stephen Warren | d054f4c | 2016-01-22 12:30:13 -0700 | [diff] [blame] | 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 | u_boot_console.log.warning( |
| 100 | 'boardenv missing "writable_fs_partition"; ' + |
| 101 | 'UMS testing will be limited.') |
Stephen Warren | 3045d7f | 2016-01-15 11:15:30 -0700 | [diff] [blame] | 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'] |
Stephen Warren | d054f4c | 2016-01-22 12:30:13 -0700 | [diff] [blame] | 111 | if have_writable_fs_partition: |
| 112 | mount_point = u_boot_console.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 | else: |
| 117 | host_ums_part_node = host_ums_dev_node |
Stephen Warren | 3045d7f | 2016-01-15 11:15:30 -0700 | [diff] [blame] | 118 | |
Stephen Warren | d054f4c | 2016-01-22 12:30:13 -0700 | [diff] [blame] | 119 | test_f = u_boot_utils.PersistentRandomFile(u_boot_console, 'ums.bin', |
| 120 | 1024 * 1024); |
| 121 | if have_writable_fs_partition: |
| 122 | mounted_test_fn = mount_point + '/' + mount_subdir + test_f.fn |
| 123 | |
| 124 | def start_ums(): |
Stephen Warren | e8debf3 | 2016-01-26 13:41:30 -0700 | [diff] [blame] | 125 | """Start U-Boot's ums shell command. |
Stephen Warren | d054f4c | 2016-01-22 12:30:13 -0700 | [diff] [blame] | 126 | |
| 127 | This also waits for the host-side USB enumeration process to complete. |
| 128 | |
| 129 | Args: |
| 130 | None. |
| 131 | |
| 132 | Returns: |
| 133 | Nothing. |
Stephen Warren | e8debf3 | 2016-01-26 13:41:30 -0700 | [diff] [blame] | 134 | """ |
Stephen Warren | d054f4c | 2016-01-22 12:30:13 -0700 | [diff] [blame] | 135 | |
| 136 | u_boot_console.log.action( |
| 137 | 'Starting long-running U-Boot ums shell command') |
| 138 | cmd = 'ums %s %s %s' % (tgt_usb_ctlr, tgt_dev_type, tgt_dev_id) |
| 139 | u_boot_console.run_command(cmd, wait_for_prompt=False) |
| 140 | u_boot_console.wait_for(re.compile('UMS: LUN.*[\r\n]')) |
| 141 | fh = u_boot_utils.wait_until_open_succeeds(host_ums_part_node) |
| 142 | u_boot_console.log.action('Reading raw data from UMS device') |
| 143 | fh.read(4096) |
| 144 | fh.close() |
| 145 | |
| 146 | def mount(): |
Stephen Warren | e8debf3 | 2016-01-26 13:41:30 -0700 | [diff] [blame] | 147 | """Mount the block device that U-Boot exports. |
Stephen Warren | d054f4c | 2016-01-22 12:30:13 -0700 | [diff] [blame] | 148 | |
| 149 | Args: |
| 150 | None. |
| 151 | |
| 152 | Returns: |
| 153 | Nothing. |
Stephen Warren | e8debf3 | 2016-01-26 13:41:30 -0700 | [diff] [blame] | 154 | """ |
Stephen Warren | d054f4c | 2016-01-22 12:30:13 -0700 | [diff] [blame] | 155 | |
| 156 | u_boot_console.log.action('Mounting exported UMS device') |
| 157 | cmd = ('/bin/mount', host_ums_part_node) |
| 158 | u_boot_utils.run_and_log(u_boot_console, cmd) |
| 159 | |
| 160 | def umount(ignore_errors): |
Stephen Warren | e8debf3 | 2016-01-26 13:41:30 -0700 | [diff] [blame] | 161 | """Unmount the block device that U-Boot exports. |
Stephen Warren | d054f4c | 2016-01-22 12:30:13 -0700 | [diff] [blame] | 162 | |
| 163 | Args: |
| 164 | ignore_errors: Ignore any errors. This is useful if an error has |
| 165 | already been detected, and the code is performing best-effort |
| 166 | cleanup. In this case, we do not want to mask the original |
| 167 | error by "honoring" any new errors. |
| 168 | |
| 169 | Returns: |
| 170 | Nothing. |
Stephen Warren | e8debf3 | 2016-01-26 13:41:30 -0700 | [diff] [blame] | 171 | """ |
Stephen Warren | d054f4c | 2016-01-22 12:30:13 -0700 | [diff] [blame] | 172 | |
| 173 | u_boot_console.log.action('Unmounting UMS device') |
| 174 | cmd = ('/bin/umount', host_ums_part_node) |
| 175 | u_boot_utils.run_and_log(u_boot_console, cmd, ignore_errors) |
| 176 | |
| 177 | def stop_ums(ignore_errors): |
Stephen Warren | e8debf3 | 2016-01-26 13:41:30 -0700 | [diff] [blame] | 178 | """Stop U-Boot's ums shell command from executing. |
Stephen Warren | d054f4c | 2016-01-22 12:30:13 -0700 | [diff] [blame] | 179 | |
| 180 | This also waits for the host-side USB de-enumeration process to |
| 181 | complete. |
| 182 | |
| 183 | Args: |
| 184 | ignore_errors: Ignore any errors. This is useful if an error has |
| 185 | already been detected, and the code is performing best-effort |
| 186 | cleanup. In this case, we do not want to mask the original |
| 187 | error by "honoring" any new errors. |
| 188 | |
| 189 | Returns: |
| 190 | Nothing. |
Stephen Warren | e8debf3 | 2016-01-26 13:41:30 -0700 | [diff] [blame] | 191 | """ |
Stephen Warren | d054f4c | 2016-01-22 12:30:13 -0700 | [diff] [blame] | 192 | |
| 193 | u_boot_console.log.action( |
| 194 | 'Stopping long-running U-Boot ums shell command') |
| 195 | u_boot_console.ctrlc() |
| 196 | u_boot_utils.wait_until_file_open_fails(host_ums_part_node, |
| 197 | ignore_errors) |
| 198 | |
| 199 | ignore_cleanup_errors = True |
| 200 | try: |
| 201 | start_ums() |
| 202 | if not have_writable_fs_partition: |
| 203 | # Skip filesystem-based testing if not configured |
| 204 | return |
| 205 | try: |
| 206 | mount() |
| 207 | u_boot_console.log.action('Writing test file via UMS') |
| 208 | cmd = ('rm', '-f', mounted_test_fn) |
| 209 | u_boot_utils.run_and_log(u_boot_console, cmd) |
| 210 | if os.path.exists(mounted_test_fn): |
| 211 | raise Exception('Could not rm target UMS test file') |
| 212 | cmd = ('cp', test_f.abs_fn, mounted_test_fn) |
| 213 | u_boot_utils.run_and_log(u_boot_console, cmd) |
| 214 | ignore_cleanup_errors = False |
| 215 | finally: |
| 216 | umount(ignore_errors=ignore_cleanup_errors) |
| 217 | finally: |
| 218 | stop_ums(ignore_errors=ignore_cleanup_errors) |
| 219 | |
| 220 | ignore_cleanup_errors = True |
| 221 | try: |
| 222 | start_ums() |
| 223 | try: |
| 224 | mount() |
| 225 | u_boot_console.log.action('Reading test file back via UMS') |
| 226 | read_back_hash = u_boot_utils.md5sum_file(mounted_test_fn) |
| 227 | cmd = ('rm', '-f', mounted_test_fn) |
| 228 | u_boot_utils.run_and_log(u_boot_console, cmd) |
| 229 | ignore_cleanup_errors = False |
| 230 | finally: |
| 231 | umount(ignore_errors=ignore_cleanup_errors) |
| 232 | finally: |
| 233 | stop_ums(ignore_errors=ignore_cleanup_errors) |
| 234 | |
| 235 | written_hash = test_f.content_hash |
| 236 | assert(written_hash == read_back_hash) |