1146773Ssam# SPDX-License-Identifier: GPL-2.0
2146773Ssam# (C) Copyright 2023, Advanced Micro Devices, Inc.
3146773Ssam
4146773Ssamimport pytest
5146773Ssamimport random
6146773Ssamimport re
7146773Ssamimport u_boot_utils
8146773Ssam
9146773Ssam"""
10146773SsamNote: This test doesn't rely on boardenv_* configuration values but it can
11146773Ssamchange the test behavior. To test USB file system cases (fat32, ext2, ext4),
12146773SsamUSB device should be formatted and valid partitions should be created for
13146773Ssamdifferent file system, otherwise it may leads to failure. This test will be
14146773Ssamskipped if the USB device is not detected.
15146773Ssam
16146773SsamFor example:
17146773Ssam
18190207Srpaulo# Setup env__usb_device_test_skip to not skipping the test. By default, its
19146773Ssam# value is set to True. Set it to False to run all tests for USB device.
20146773Ssamenv__usb_device_test_skip = False
21146773Ssam"""
22146773Ssam
23146773Ssamdef setup_usb(u_boot_console):
24146773Ssam    if u_boot_console.config.env.get('env__usb_device_test_skip', True):
25146773Ssam        pytest.skip('USB device test is not enabled')
26146773Ssam
27146773Ssam@pytest.mark.buildconfigspec('cmd_usb')
28146773Ssamdef test_usb_start(u_boot_console):
29214478Srpaulo    setup_usb(u_boot_console)
30146773Ssam    output = u_boot_console.run_command('usb start')
31146773Ssam
32146773Ssam    # if output is empty, usb start may already run as part of preboot command
33146773Ssam    # re-start the usb, in that case
34146773Ssam    if not output:
35146773Ssam        u_boot_console.run_command('usb stop')
36146773Ssam        output = u_boot_console.run_command('usb start')
37146773Ssam
38146773Ssam    if 'No USB device found' in output:
39146773Ssam        pytest.skip('No USB controller available')
40146773Ssam
41146773Ssam    if 'Card did not respond to voltage select' in output:
42146773Ssam        pytest.skip('No USB device present')
43146773Ssam
44146773Ssam    controllers = 0
45146773Ssam    storage_device = 0
46    obj = re.search(r'\d USB Device\(s\) found', output)
47    controllers = int(obj.group()[0])
48
49    if not controllers:
50        pytest.skip('No USB device present')
51
52    obj = re.search(r'\d Storage Device\(s\) found', output)
53    storage_device = int(obj.group()[0])
54
55    if not storage_device:
56        pytest.skip('No USB storage device present')
57
58    assert 'USB init failed' not in output
59    assert 'starting USB...' in output
60
61    if 'Starting the controller' in output:
62        assert 'USB XHCI' in output
63
64    output = u_boot_console.run_command('echo $?')
65    assert output.endswith('0')
66    return controllers, storage_device
67
68@pytest.mark.buildconfigspec('cmd_usb')
69def test_usb_stop(u_boot_console):
70    setup_usb(u_boot_console)
71    output = u_boot_console.run_command('usb stop')
72    assert 'stopping USB..' in output
73
74    output = u_boot_console.run_command('echo $?')
75    assert output.endswith('0')
76
77    output = u_boot_console.run_command('usb dev')
78    assert "USB is stopped. Please issue 'usb start' first." in output
79
80@pytest.mark.buildconfigspec('cmd_usb')
81def test_usb_reset(u_boot_console):
82    setup_usb(u_boot_console)
83    output = u_boot_console.run_command('usb reset')
84
85    if 'No USB device found' in output:
86        pytest.skip('No USB controller available')
87
88    if 'Card did not respond to voltage select' in output:
89        pytest.skip('No USB device present')
90
91    obj = re.search(r'\d USB Device\(s\) found', output)
92    usb_dev_num = int(obj.group()[0])
93
94    if not usb_dev_num:
95        pytest.skip('No USB device present')
96
97    obj = re.search(r'\d Storage Device\(s\) found', output)
98    usb_stor_num = int(obj.group()[0])
99
100    if not usb_stor_num:
101        pytest.skip('No USB storage device present')
102
103    assert 'BUG' not in output
104    assert 'USB init failed' not in output
105    assert 'resetting USB...' in output
106
107    if 'Starting the controller' in output:
108        assert 'USB XHCI' in output
109
110    output = u_boot_console.run_command('echo $?')
111    assert output.endswith('0')
112
113@pytest.mark.buildconfigspec('cmd_usb')
114def test_usb_info(u_boot_console):
115    controllers, storage_device = test_usb_start(u_boot_console)
116    output = u_boot_console.run_command('usb info')
117
118    num_controller = len(re.findall(': Hub,', output))
119    num_mass_storage = len(re.findall(': Mass Storage,', output))
120
121    assert num_controller == controllers - 1
122    assert num_mass_storage == storage_device
123
124    output = u_boot_console.run_command('echo $?')
125    assert output.endswith('0')
126
127    for i in range(0, storage_device + controllers - 1):
128        output = u_boot_console.run_command('usb info %d' % i)
129        num_controller = len(re.findall(': Hub,', output))
130        num_mass_storage = len(re.findall(': Mass Storage,', output))
131        assert num_controller + num_mass_storage == 1
132        assert 'No device available' not in output
133        output = u_boot_console.run_command('echo $?')
134        assert output.endswith('0')
135
136@pytest.mark.buildconfigspec('cmd_usb')
137def test_usb_tree(u_boot_console):
138    controllers, storage_device = test_usb_start(u_boot_console)
139    output = u_boot_console.run_command('usb tree')
140
141    num_controller = len(re.findall('Hub', output))
142    num_mass_storage = len(re.findall('Mass Storage', output))
143
144    assert num_controller == controllers - 1
145    assert num_mass_storage == storage_device
146
147    output = u_boot_console.run_command('echo $?')
148    assert output.endswith('0')
149
150@pytest.mark.buildconfigspec('cmd_usb')
151@pytest.mark.buildconfigspec('usb_storage')
152def test_usb_storage(u_boot_console):
153    controllers, storage_device = test_usb_start(u_boot_console)
154    output = u_boot_console.run_command('usb storage')
155
156    obj = re.findall(r'Capacity: (\d+|\d+[\.]?\d)', output)
157    devices = {}
158
159    for key in range(int(storage_device)):
160        devices[key] = {}
161
162    for x in range(int(storage_device)):
163        try:
164            capacity = float(obj[x].split()[0])
165            devices[x]['capacity'] = capacity
166            print('USB storage device %d capacity is: %g MB' % (x, capacity))
167        except ValueError:
168            pytest.fail('USB storage device capacity not recognized')
169
170    output = u_boot_console.run_command('echo $?')
171    assert output.endswith('0')
172
173@pytest.mark.buildconfigspec('cmd_usb')
174def test_usb_dev(u_boot_console):
175    controllers, storage_device = test_usb_start(u_boot_console)
176    output = u_boot_console.run_command('usb dev')
177
178    assert 'no usb devices available' not in output
179
180    output = u_boot_console.run_command('echo $?')
181    assert output.endswith('0')
182
183    devices = {}
184
185    for key in range(int(storage_device)):
186        devices[key] = {}
187
188    fail = 0
189    for x in range(0, storage_device):
190        devices[x]['detected'] = 'yes'
191        output = u_boot_console.run_command('usb dev %d' % x)
192
193        if 'Card did not respond to voltage select' in output:
194            fail = 1
195            devices[x]['detected'] = 'no'
196
197        if 'No USB device found' in output:
198            devices[x]['detected'] = 'no'
199
200        if 'unknown device' in output:
201            devices[x]['detected'] = 'no'
202
203        assert 'is now current device' in output
204        output = u_boot_console.run_command('echo $?')
205        assert output.endswith('0')
206
207    if fail:
208        pytest.fail('USB device not present')
209
210    return devices, controllers, storage_device
211
212@pytest.mark.buildconfigspec('cmd_usb')
213def test_usb_part(u_boot_console):
214    devices, controllers, storage_device = test_usb_dev(u_boot_console)
215    if not devices:
216        pytest.skip('No devices detected')
217
218    u_boot_console.run_command('usb part')
219
220    output = u_boot_console.run_command('echo $?')
221    assert output.endswith('0')
222
223    for i in range(0, storage_device):
224        if devices[i]['detected'] == 'yes':
225            u_boot_console.run_command('usb dev %d' % i)
226            output = u_boot_console.run_command('usb part')
227
228            lines = output.split('\n')
229            part_fat = []
230            part_ext = []
231            for line in lines:
232                obj = re.search(r'(\d)\s+\d+\s+\d+\s+\w+\d+\w+-\d+\s+(\d+\w+)', line)
233                if obj:
234                    part_id = int(obj.groups()[0])
235                    part_type = obj.groups()[1]
236                    print('part_id:%d, part_type:%s' % (part_id, part_type))
237
238                    if part_type == '0c' or part_type == '0b' or part_type == '0e':
239                        print('Fat detected')
240                        part_fat.append(part_id)
241                    elif part_type == '83':
242                        print('ext detected')
243                        part_ext.append(part_id)
244                    else:
245                        pytest.fail('Unsupported Filesystem on device %d' % i)
246            devices[i]['ext4'] = part_ext
247            devices[i]['ext2'] = part_ext
248            devices[i]['fat'] = part_fat
249
250            if not part_ext and not part_fat:
251                pytest.fail('No partition detected on device %d' % i)
252
253    return devices, controllers, storage_device
254
255@pytest.mark.buildconfigspec('cmd_usb')
256@pytest.mark.buildconfigspec('cmd_fat')
257def test_usb_fatls_fatinfo(u_boot_console):
258    devices, controllers, storage_device = test_usb_part(u_boot_console)
259    if not devices:
260        pytest.skip('No devices detected')
261
262    part_detect = 0
263    fs = 'fat'
264    for x in range(0, int(storage_device)):
265        if devices[x]['detected'] == 'yes':
266            u_boot_console.run_command('usb dev %d' % x)
267            try:
268                partitions = devices[x][fs]
269            except:
270                print('No %s table on this device' % fs.upper())
271                continue
272
273            for part in partitions:
274                output = u_boot_console.run_command('fatls usb %d:%s' % (x, part))
275                if 'Unrecognized filesystem type' in output:
276                    partitions.remove(part)
277                    pytest.fail('Unrecognized filesystem')
278
279                if not re.search(r'\d file\(s\), \d dir\(s\)', output):
280                    pytest.fail('%s read failed on device %d' % (fs.upper, x))
281
282                output = u_boot_console.run_command('fatinfo usb %d:%s' % (x, part))
283                string = 'Filesystem: %s' % fs.upper
284                if re.search(string, output):
285                    pytest.fail('%s FS failed on device %d' % (fs.upper(), x))
286                part_detect = 1
287
288    if not part_detect:
289        pytest.skip('No %s partition detected' % fs.upper())
290
291@pytest.mark.buildconfigspec('cmd_usb')
292@pytest.mark.buildconfigspec('cmd_fat')
293@pytest.mark.buildconfigspec('cmd_memory')
294def test_usb_fatload_fatwrite(u_boot_console):
295    devices, controllers, storage_device = test_usb_part(u_boot_console)
296    if not devices:
297        pytest.skip('No devices detected')
298
299    part_detect = 0
300    fs = 'fat'
301    for x in range(0, int(storage_device)):
302        if devices[x]['detected'] == 'yes':
303            u_boot_console.run_command('usb dev %d' % x)
304            try:
305                partitions = devices[x][fs]
306            except:
307                print('No %s table on this device' % fs.upper())
308                continue
309
310            for part in partitions:
311                part_detect = 1
312                addr = u_boot_utils.find_ram_base(u_boot_console)
313                size = random.randint(4, 1 * 1024 * 1024)
314                output = u_boot_console.run_command('crc32 %x %x' % (addr, size))
315                m = re.search('==> (.+?)', output)
316                if not m:
317                    pytest.fail('CRC32 failed')
318                expected_crc32 = m.group(1)
319
320                file = '%s_%d' % ('uboot_test', size)
321                output = u_boot_console.run_command(
322                    '%swrite usb %d:%s %x %s %x' % (fs, x, part, addr, file, size)
323                )
324                assert 'Unable to write' not in output
325                assert 'Error' not in output
326                assert 'overflow' not in output
327                expected_text = '%d bytes written' % size
328                assert expected_text in output
329
330                alignment = int(
331                    u_boot_console.config.buildconfig.get(
332                        'config_sys_cacheline_size', 128
333                    )
334                )
335                offset = random.randrange(alignment, 1024, alignment)
336                output = u_boot_console.run_command(
337                    '%sload usb %d:%s %x %s' % (fs, x, part, addr + offset, file)
338                )
339                assert 'Invalid FAT entry' not in output
340                assert 'Unable to read file' not in output
341                assert 'Misaligned buffer address' not in output
342                expected_text = '%d bytes read' % size
343                assert expected_text in output
344
345                output = u_boot_console.run_command(
346                    'crc32 %x $filesize' % (addr + offset)
347                )
348                assert expected_crc32 in output
349
350    if not part_detect:
351        pytest.skip('No %s partition detected' % fs.upper())
352
353    return file, size
354
355@pytest.mark.buildconfigspec('cmd_usb')
356@pytest.mark.buildconfigspec('cmd_ext4')
357def test_usb_ext4ls(u_boot_console):
358    devices, controllers, storage_device = test_usb_part(u_boot_console)
359    if not devices:
360        pytest.skip('No devices detected')
361
362    part_detect = 0
363    fs = 'ext4'
364    for x in range(0, int(storage_device)):
365        if devices[x]['detected'] == 'yes':
366            try:
367                partitions = devices[x][fs]
368            except:
369                print('No %s table on this device' % fs.upper())
370                continue
371
372            u_boot_console.run_command('usb dev %d' % x)
373            for part in partitions:
374                output = u_boot_console.run_command('%sls usb %d:%s' % (fs, x, part))
375                if 'Unrecognized filesystem type' in output:
376                    partitions.remove(part)
377                    pytest.fail('Unrecognized filesystem')
378                part_detect = 1
379
380    if not part_detect:
381        pytest.skip('No %s partition detected' % fs.upper())
382
383@pytest.mark.buildconfigspec('cmd_usb')
384@pytest.mark.buildconfigspec('cmd_ext4')
385@pytest.mark.buildconfigspec('ext4_write')
386@pytest.mark.buildconfigspec('cmd_memory')
387def test_usb_ext4load_ext4write(u_boot_console):
388    devices, controllers, storage_device = test_usb_part(u_boot_console)
389    if not devices:
390        pytest.skip('No devices detected')
391
392    part_detect = 0
393    fs = 'ext4'
394    for x in range(0, int(storage_device)):
395        if devices[x]['detected'] == 'yes':
396            u_boot_console.run_command('usb dev %d' % x)
397            try:
398                partitions = devices[x][fs]
399            except:
400                print('No %s table on this device' % fs.upper())
401                continue
402
403            for part in partitions:
404                part_detect = 1
405                addr = u_boot_utils.find_ram_base(u_boot_console)
406                size = random.randint(4, 1 * 1024 * 1024)
407                output = u_boot_console.run_command('crc32 %x %x' % (addr, size))
408                m = re.search('==> (.+?)', output)
409                if not m:
410                    pytest.fail('CRC32 failed')
411                expected_crc32 = m.group(1)
412                file = '%s_%d' % ('uboot_test', size)
413
414                output = u_boot_console.run_command(
415                    '%swrite usb %d:%s %x /%s %x' % (fs, x, part, addr, file, size)
416                )
417                assert 'Unable to write' not in output
418                assert 'Error' not in output
419                assert 'overflow' not in output
420                expected_text = '%d bytes written' % size
421                assert expected_text in output
422
423                offset = random.randrange(128, 1024, 128)
424                output = u_boot_console.run_command(
425                    '%sload usb %d:%s %x /%s' % (fs, x, part, addr + offset, file)
426                )
427                expected_text = '%d bytes read' % size
428                assert expected_text in output
429
430                output = u_boot_console.run_command(
431                    'crc32 %x $filesize' % (addr + offset)
432                )
433                assert expected_crc32 in output
434
435    if not part_detect:
436        pytest.skip('No %s partition detected' % fs.upper())
437
438    return file, size
439
440@pytest.mark.buildconfigspec('cmd_usb')
441@pytest.mark.buildconfigspec('cmd_ext2')
442def test_usb_ext2ls(u_boot_console):
443    devices, controllers, storage_device = test_usb_part(u_boot_console)
444    if not devices:
445        pytest.skip('No devices detected')
446
447    part_detect = 0
448    fs = 'ext2'
449    for x in range(0, int(storage_device)):
450        if devices[x]['detected'] == 'yes':
451            u_boot_console.run_command('usb dev %d' % x)
452            try:
453                partitions = devices[x][fs]
454            except:
455                print('No %s table on this device' % fs.upper())
456                continue
457
458            for part in partitions:
459                part_detect = 1
460                output = u_boot_console.run_command('%sls usb %d:%s' % (fs, x, part))
461                if 'Unrecognized filesystem type' in output:
462                    partitions.remove(part)
463                    pytest.fail('Unrecognized filesystem')
464                part_detect = 1
465
466    if not part_detect:
467        pytest.skip('No %s partition detected' % fs.upper())
468
469@pytest.mark.buildconfigspec('cmd_usb')
470@pytest.mark.buildconfigspec('cmd_ext2')
471@pytest.mark.buildconfigspec('cmd_ext4')
472@pytest.mark.buildconfigspec('ext4_write')
473@pytest.mark.buildconfigspec('cmd_memory')
474def test_usb_ext2load(u_boot_console):
475    devices, controllers, storage_device = test_usb_part(u_boot_console)
476    file, size = test_usb_ext4load_ext4write(u_boot_console)
477
478    if not devices:
479        pytest.skip('No devices detected')
480
481    part_detect = 0
482    fs = 'ext2'
483    for x in range(0, int(storage_device)):
484        if devices[x]['detected'] == 'yes':
485            u_boot_console.run_command('usb dev %d' % x)
486            try:
487                partitions = devices[x][fs]
488            except:
489                print('No %s table on this device' % fs.upper())
490                continue
491
492            for part in partitions:
493                part_detect = 1
494                addr = u_boot_utils.find_ram_base(u_boot_console)
495                output = u_boot_console.run_command('crc32 %x %x' % (addr, size))
496                m = re.search('==> (.+?)', output)
497                if not m:
498                    pytest.fail('CRC32 failed')
499                expected_crc32 = m.group(1)
500
501                offset = random.randrange(128, 1024, 128)
502                output = u_boot_console.run_command(
503                    '%sload usb %d:%s %x /%s' % (fs, x, part, addr + offset, file)
504                )
505                expected_text = '%d bytes read' % size
506                assert expected_text in output
507
508                output = u_boot_console.run_command(
509                    'crc32 %x $filesize' % (addr + offset)
510                )
511                assert expected_crc32 in output
512
513    if not part_detect:
514        pytest.skip('No %s partition detected' % fs.upper())
515
516@pytest.mark.buildconfigspec('cmd_usb')
517@pytest.mark.buildconfigspec('cmd_fs_generic')
518def test_usb_ls(u_boot_console):
519    devices, controllers, storage_device = test_usb_part(u_boot_console)
520    if not devices:
521        pytest.skip('No devices detected')
522
523    part_detect = 0
524    for x in range(0, int(storage_device)):
525        if devices[x]['detected'] == 'yes':
526            u_boot_console.run_command('usb dev %d' % x)
527            for fs in ['fat', 'ext4']:
528                try:
529                    partitions = devices[x][fs]
530                except:
531                    print('No %s table on this device' % fs.upper())
532                    continue
533
534                for part in partitions:
535                    part_detect = 1
536                    output = u_boot_console.run_command('ls usb %d:%s' % (x, part))
537                    if re.search(r'No \w+ table on this device', output):
538                        pytest.fail(
539                            '%s: Partition table not found %d' % (fs.upper(), x)
540                        )
541
542    if not part_detect:
543        pytest.skip('No partition detected')
544
545@pytest.mark.buildconfigspec('cmd_usb')
546@pytest.mark.buildconfigspec('cmd_fs_generic')
547def test_usb_load(u_boot_console):
548    devices, controllers, storage_device = test_usb_part(u_boot_console)
549    if not devices:
550        pytest.skip('No devices detected')
551
552    part_detect = 0
553    for x in range(0, int(storage_device)):
554        if devices[x]['detected'] == 'yes':
555            u_boot_console.run_command('usb dev %d' % x)
556            for fs in ['fat', 'ext4']:
557                try:
558                    partitions = devices[x][fs]
559                except:
560                    print('No %s table on this device' % fs.upper())
561                    continue
562
563                for part in partitions:
564                    part_detect = 1
565                    addr = u_boot_utils.find_ram_base(u_boot_console)
566
567                    if fs == 'fat':
568                        file, size = test_usb_fatload_fatwrite(u_boot_console)
569                    elif fs == 'ext4':
570                        file, size = test_usb_ext4load_ext4write(u_boot_console)
571
572                    output = u_boot_console.run_command('crc32 %x %x' % (addr, size))
573                    m = re.search('==> (.+?)', output)
574                    if not m:
575                        pytest.fail('CRC32 failed')
576                    expected_crc32 = m.group(1)
577
578                    offset = random.randrange(128, 1024, 128)
579                    output = u_boot_console.run_command(
580                        'load usb %d:%s %x /%s' % (x, part, addr + offset, file)
581                    )
582                    expected_text = '%d bytes read' % size
583                    assert expected_text in output
584
585                    output = u_boot_console.run_command(
586                        'crc32 %x $filesize' % (addr + offset)
587                    )
588                    assert expected_crc32 in output
589
590    if not part_detect:
591        pytest.skip('No partition detected')
592
593@pytest.mark.buildconfigspec('cmd_usb')
594@pytest.mark.buildconfigspec('cmd_fs_generic')
595def test_usb_save(u_boot_console):
596    devices, controllers, storage_device = test_usb_part(u_boot_console)
597    if not devices:
598        pytest.skip('No devices detected')
599
600    part_detect = 0
601    for x in range(0, int(storage_device)):
602        if devices[x]['detected'] == 'yes':
603            u_boot_console.run_command('usb dev %d' % x)
604            for fs in ['fat', 'ext4']:
605                try:
606                    partitions = devices[x][fs]
607                except:
608                    print('No %s table on this device' % fs.upper())
609                    continue
610
611                for part in partitions:
612                    part_detect = 1
613                    addr = u_boot_utils.find_ram_base(u_boot_console)
614                    size = random.randint(4, 1 * 1024 * 1024)
615                    file = '%s_%d' % ('uboot_test', size)
616
617                    offset = random.randrange(128, 1024, 128)
618                    output = u_boot_console.run_command(
619                        'save usb %d:%s %x /%s %x'
620                        % (x, part, addr + offset, file, size)
621                    )
622                    expected_text = '%d bytes written' % size
623                    assert expected_text in output
624
625    if not part_detect:
626        pytest.skip('No partition detected')
627