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