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