1# SPDX-License-Identifier: GPL-2.0
2# (C) Copyright 2023, Advanced Micro Devices, Inc.
3
4import pytest
5import random
6import re
7import u_boot_utils
8
9"""
10Note: This test doesn't rely on boardenv_* configuration values but it can
11change the test behavior. To test MMC file system cases (fat32, ext2, ext4),
12MMC device should be formatted and valid partitions should be created for
13different file system, otherwise it may leads to failure. This test will be
14skipped if the MMC device is not detected.
15
16For example:
17
18# Setup env__mmc_device_test_skip to not skipping the test. By default, its
19# value is set to True. Set it to False to run all tests for MMC device.
20env__mmc_device_test_skip = False
21"""
22
23mmc_set_up = False
24controllers = 0
25devices = {}
26
27def setup_mmc(u_boot_console):
28    if u_boot_console.config.env.get('env__mmc_device_test_skip', True):
29        pytest.skip('MMC device test is not enabled')
30
31@pytest.mark.buildconfigspec('cmd_mmc')
32def test_mmc_list(u_boot_console):
33    setup_mmc(u_boot_console)
34    output = u_boot_console.run_command('mmc list')
35    if 'No MMC device available' in output:
36        pytest.skip('No SD/MMC/eMMC controller available')
37
38    if 'Card did not respond to voltage select' in output:
39        pytest.skip('No SD/MMC card present')
40
41    array = output.split()
42    global devices
43    global controllers
44    controllers = int(len(array) / 2)
45    for x in range(0, controllers):
46        y = x * 2
47        devices[x] = {}
48        devices[x]['name'] = array[y]
49
50    global mmc_set_up
51    mmc_set_up = True
52
53@pytest.mark.buildconfigspec('cmd_mmc')
54def test_mmc_dev(u_boot_console):
55    if not mmc_set_up:
56        pytest.skip('No SD/MMC/eMMC controller available')
57
58    fail = 0
59    for x in range(0, controllers):
60        devices[x]['detected'] = 'yes'
61        output = u_boot_console.run_command('mmc dev %d' % x)
62
63        # Some sort of switch here
64        if 'Card did not respond to voltage select' in output:
65            fail = 1
66            devices[x]['detected'] = 'no'
67
68        if 'no mmc device at slot' in output:
69            devices[x]['detected'] = 'no'
70
71        if 'MMC: no card present' in output:
72            devices[x]['detected'] = 'no'
73
74    if fail:
75        pytest.fail('Card not present')
76
77@pytest.mark.buildconfigspec('cmd_mmc')
78def test_mmcinfo(u_boot_console):
79    if not mmc_set_up:
80        pytest.skip('No SD/MMC/eMMC controller available')
81
82    for x in range(0, controllers):
83        if devices[x]['detected'] == 'yes':
84            u_boot_console.run_command('mmc dev %d' % x)
85            output = u_boot_console.run_command('mmcinfo')
86            if 'busy timeout' in output:
87                pytest.skip('No SD/MMC/eMMC device present')
88
89            obj = re.search(r'Capacity: (\d+|\d+[\.]?\d)', output)
90            try:
91                capacity = float(obj.groups()[0])
92                print(capacity)
93                devices[x]['capacity'] = capacity
94                print('Capacity of dev %d is: %g GiB' % (x, capacity))
95            except ValueError:
96                pytest.fail('MMC capacity not recognized')
97
98@pytest.mark.buildconfigspec('cmd_mmc')
99def test_mmc_info(u_boot_console):
100    if not mmc_set_up:
101        pytest.skip('No SD/MMC/eMMC controller available')
102
103    for x in range(0, controllers):
104        if devices[x]['detected'] == 'yes':
105            u_boot_console.run_command('mmc dev %d' % x)
106
107            output = u_boot_console.run_command('mmc info')
108
109            obj = re.search(r'Capacity: (\d+|\d+[\.]?\d)', output)
110            try:
111                capacity = float(obj.groups()[0])
112                print(capacity)
113                if devices[x]['capacity'] != capacity:
114                    pytest.fail("MMC capacity doesn't match mmcinfo")
115
116            except ValueError:
117                pytest.fail('MMC capacity not recognized')
118
119@pytest.mark.buildconfigspec('cmd_mmc')
120def test_mmc_rescan(u_boot_console):
121    if not mmc_set_up:
122        pytest.skip('No SD/MMC/eMMC controller available')
123
124    if not devices:
125        pytest.skip('No devices detected')
126
127    for x in range(0, controllers):
128        if devices[x]['detected'] == 'yes':
129            u_boot_console.run_command('mmc dev %d' % x)
130            output = u_boot_console.run_command('mmc rescan')
131            if output:
132                pytest.fail('mmc rescan has something to check')
133            output = u_boot_console.run_command('echo $?')
134            assert output.endswith('0')
135
136@pytest.mark.buildconfigspec('cmd_mmc')
137def test_mmc_part(u_boot_console):
138    if not mmc_set_up:
139        pytest.skip('No SD/MMC/eMMC controller available')
140
141    if not devices:
142        pytest.skip('No devices detected')
143
144    for x in range(0, controllers):
145        if devices[x]['detected'] == 'yes':
146            u_boot_console.run_command('mmc dev %d' % x)
147            output = u_boot_console.run_command('mmc part')
148
149            lines = output.split('\n')
150            part_fat = []
151            part_ext = []
152            for line in lines:
153                obj = re.search(
154                        r'(\d)\s+\d+\s+\d+\s+\w+\d+\w+-\d+\s+(\d+\w+)', line)
155                if obj:
156                    part_id = int(obj.groups()[0])
157                    part_type = obj.groups()[1]
158                    print('part_id:%d, part_type:%s' % (part_id, part_type))
159
160                    if part_type in ['0c', '0b', '0e']:
161                        print('Fat detected')
162                        part_fat.append(part_id)
163                    elif part_type == '83':
164                        print('ext detected')
165                        part_ext.append(part_id)
166                    else:
167                        pytest.fail('Unsupported Filesystem on device %d' % x)
168            devices[x]['ext4'] = part_ext
169            devices[x]['ext2'] = part_ext
170            devices[x]['fat'] = part_fat
171
172            if not part_ext and not part_fat:
173                pytest.fail('No partition detected on device %d' % x)
174
175@pytest.mark.buildconfigspec('cmd_mmc')
176@pytest.mark.buildconfigspec('cmd_fat')
177def test_mmc_fatls_fatinfo(u_boot_console):
178    if not mmc_set_up:
179        pytest.skip('No SD/MMC/eMMC controller available')
180
181    if not devices:
182        pytest.skip('No devices detected')
183
184    part_detect = 0
185    fs = 'fat'
186    for x in range(0, controllers):
187        if devices[x]['detected'] == 'yes':
188            u_boot_console.run_command('mmc dev %d' % x)
189            try:
190                partitions = devices[x][fs]
191            except:
192                print('No %s table on this device' % fs.upper())
193                continue
194
195            for part in partitions:
196                output = u_boot_console.run_command(
197                        'fatls mmc %d:%s' % (x, part))
198                if 'Unrecognized filesystem type' in output:
199                    partitions.remove(part)
200                    pytest.fail('Unrecognized filesystem')
201
202                if not re.search(r'\d file\(s\), \d dir\(s\)', output):
203                    pytest.fail('%s read failed on device %d' % (fs.upper, x))
204                output = u_boot_console.run_command(
205                        'fatinfo mmc %d:%s' % (x, part))
206                string = 'Filesystem: %s' % fs.upper
207                if re.search(string, output):
208                    pytest.fail('%s FS failed on device %d' % (fs.upper(), x))
209                part_detect = 1
210
211    if not part_detect:
212        pytest.skip('No %s partition detected' % fs.upper())
213
214
215@pytest.mark.buildconfigspec('cmd_mmc')
216@pytest.mark.buildconfigspec('cmd_fat')
217@pytest.mark.buildconfigspec('cmd_memory')
218def test_mmc_fatload_fatwrite(u_boot_console):
219    if not mmc_set_up:
220        pytest.skip('No SD/MMC/eMMC controller available')
221
222    if not devices:
223        pytest.skip('No devices detected')
224
225    part_detect = 0
226    fs = 'fat'
227    for x in range(0, controllers):
228        if devices[x]['detected'] == 'yes':
229            u_boot_console.run_command('mmc dev %d' % x)
230            try:
231                partitions = devices[x][fs]
232            except:
233                print('No %s table on this device' % fs.upper())
234                continue
235
236            for part in partitions:
237                part_detect = 1
238                addr = u_boot_utils.find_ram_base(u_boot_console)
239                devices[x]['addr_%d' % part] = addr
240                size = random.randint(4, 1 * 1024 * 1024)
241                devices[x]['size_%d' % part] = size
242                # count CRC32
243                output = u_boot_console.run_command('crc32 %x %x' % (addr, size))
244                m = re.search('==> (.+?)', output)
245                if not m:
246                    pytest.fail('CRC32 failed')
247                expected_crc32 = m.group(1)
248                devices[x]['expected_crc32_%d' % part] = expected_crc32
249                # do write
250                file = '%s_%d' % ('uboot_test', size)
251                devices[x]['file_%d' % part] = file
252                output = u_boot_console.run_command(
253                    '%swrite mmc %d:%s %x %s %x' % (fs, x, part, addr, file, size)
254                )
255                assert 'Unable to write' not in output
256                assert 'Error' not in output
257                assert 'overflow' not in output
258                expected_text = '%d bytes written' % size
259                assert expected_text in output
260
261                alignment = int(
262                    u_boot_console.config.buildconfig.get(
263                        'config_sys_cacheline_size', 128
264                    )
265                )
266                offset = random.randrange(alignment, 1024, alignment)
267                output = u_boot_console.run_command(
268                    '%sload mmc %d:%s %x %s' % (fs, x, part, addr + offset, file)
269                )
270                assert 'Invalid FAT entry' not in output
271                assert 'Unable to read file' not in output
272                assert 'Misaligned buffer address' not in output
273                expected_text = '%d bytes read' % size
274                assert expected_text in output
275
276                output = u_boot_console.run_command(
277                    'crc32 %x $filesize' % (addr + offset)
278                )
279                assert expected_crc32 in output
280
281    if not part_detect:
282        pytest.skip('No %s partition detected' % fs.upper())
283
284@pytest.mark.buildconfigspec('cmd_mmc')
285@pytest.mark.buildconfigspec('cmd_ext4')
286def test_mmc_ext4ls(u_boot_console):
287    if not mmc_set_up:
288        pytest.skip('No SD/MMC/eMMC controller available')
289
290    if not devices:
291        pytest.skip('No devices detected')
292
293    part_detect = 0
294    fs = 'ext4'
295    for x in range(0, controllers):
296        if devices[x]['detected'] == 'yes':
297            try:
298                partitions = devices[x][fs]
299            except:
300                print('No %s table on this device' % fs.upper())
301                continue
302
303            u_boot_console.run_command('mmc dev %d' % x)
304            for part in partitions:
305                output = u_boot_console.run_command('%sls mmc %d:%s' % (fs, x, part))
306                if 'Unrecognized filesystem type' in output:
307                    partitions.remove(part)
308                    pytest.fail('Unrecognized filesystem')
309                part_detect = 1
310
311    if not part_detect:
312        pytest.skip('No %s partition detected' % fs.upper())
313
314@pytest.mark.buildconfigspec('cmd_mmc')
315@pytest.mark.buildconfigspec('cmd_ext4')
316@pytest.mark.buildconfigspec('ext4_write')
317@pytest.mark.buildconfigspec('cmd_memory')
318def test_mmc_ext4load_ext4write(u_boot_console):
319    if not mmc_set_up:
320        pytest.skip('No SD/MMC/eMMC controller available')
321
322    if not devices:
323        pytest.skip('No devices detected')
324
325    part_detect = 0
326    fs = 'ext4'
327    for x in range(0, controllers):
328        if devices[x]['detected'] == 'yes':
329            u_boot_console.run_command('mmc dev %d' % x)
330            try:
331                partitions = devices[x][fs]
332            except:
333                print('No %s table on this device' % fs.upper())
334                continue
335
336            for part in partitions:
337                part_detect = 1
338                addr = u_boot_utils.find_ram_base(u_boot_console)
339                devices[x]['addr_%d' % part] = addr
340                size = random.randint(4, 1 * 1024 * 1024)
341                devices[x]['size_%d' % part] = size
342                # count CRC32
343                output = u_boot_console.run_command('crc32 %x %x' % (addr, size))
344                m = re.search('==> (.+?)', output)
345                if not m:
346                    pytest.fail('CRC32 failed')
347                expected_crc32 = m.group(1)
348                devices[x]['expected_crc32_%d' % part] = expected_crc32
349                # do write
350
351                file = '%s_%d' % ('uboot_test', size)
352                devices[x]['file_%d' % part] = file
353                output = u_boot_console.run_command(
354                    '%swrite mmc %d:%s %x /%s %x' % (fs, x, part, addr, file, size)
355                )
356                assert 'Unable to write' not in output
357                assert 'Error' not in output
358                assert 'overflow' not in output
359                expected_text = '%d bytes written' % size
360                assert expected_text in output
361
362                offset = random.randrange(128, 1024, 128)
363                output = u_boot_console.run_command(
364                    '%sload mmc %d:%s %x /%s' % (fs, x, part, addr + offset, file)
365                )
366                expected_text = '%d bytes read' % size
367                assert expected_text in output
368
369                output = u_boot_console.run_command(
370                    'crc32 %x $filesize' % (addr + offset)
371                )
372                assert expected_crc32 in output
373
374    if not part_detect:
375        pytest.skip('No %s partition detected' % fs.upper())
376
377@pytest.mark.buildconfigspec('cmd_mmc')
378@pytest.mark.buildconfigspec('cmd_ext2')
379def test_mmc_ext2ls(u_boot_console):
380    if not mmc_set_up:
381        pytest.skip('No SD/MMC/eMMC controller available')
382
383    if not devices:
384        pytest.skip('No devices detected')
385
386    part_detect = 0
387    fs = 'ext2'
388    for x in range(0, controllers):
389        if devices[x]['detected'] == 'yes':
390            u_boot_console.run_command('mmc dev %d' % x)
391            try:
392                partitions = devices[x][fs]
393            except:
394                print('No %s table on this device' % fs.upper())
395                continue
396
397            for part in partitions:
398                part_detect = 1
399                output = u_boot_console.run_command('%sls mmc %d:%s' % (fs, x, part))
400                if 'Unrecognized filesystem type' in output:
401                    partitions.remove(part)
402                    pytest.fail('Unrecognized filesystem')
403                part_detect = 1
404
405    if not part_detect:
406        pytest.skip('No %s partition detected' % fs.upper())
407
408@pytest.mark.buildconfigspec('cmd_mmc')
409@pytest.mark.buildconfigspec('cmd_ext2')
410@pytest.mark.buildconfigspec('cmd_ext4')
411@pytest.mark.buildconfigspec('ext4_write')
412@pytest.mark.buildconfigspec('cmd_memory')
413def test_mmc_ext2load(u_boot_console):
414    if not mmc_set_up:
415        pytest.skip('No SD/MMC/eMMC controller available')
416
417    if not devices:
418        pytest.skip('No devices detected')
419
420    part_detect = 0
421    fs = 'ext2'
422    for x in range(0, controllers):
423        if devices[x]['detected'] == 'yes':
424            u_boot_console.run_command('mmc dev %d' % x)
425            try:
426                partitions = devices[x][fs]
427            except:
428                print('No %s table on this device' % fs.upper())
429                continue
430
431            for part in partitions:
432                part_detect = 1
433                addr = devices[x]['addr_%d' % part]
434                size = devices[x]['size_%d' % part]
435                expected_crc32 = devices[x]['expected_crc32_%d' % part]
436                file = devices[x]['file_%d' % part]
437
438                offset = random.randrange(128, 1024, 128)
439                output = u_boot_console.run_command(
440                    '%sload mmc %d:%s %x /%s' % (fs, x, part, addr + offset, file)
441                )
442                expected_text = '%d bytes read' % size
443                assert expected_text in output
444
445                output = u_boot_console.run_command(
446                    'crc32 %x $filesize' % (addr + offset)
447                )
448                assert expected_crc32 in output
449
450    if not part_detect:
451        pytest.skip('No %s partition detected' % fs.upper())
452
453@pytest.mark.buildconfigspec('cmd_mmc')
454@pytest.mark.buildconfigspec('cmd_fs_generic')
455def test_mmc_ls(u_boot_console):
456    if not mmc_set_up:
457        pytest.skip('No SD/MMC/eMMC controller available')
458
459    if not devices:
460        pytest.skip('No devices detected')
461
462    part_detect = 0
463    for x in range(0, controllers):
464        if devices[x]['detected'] == 'yes':
465            u_boot_console.run_command('mmc dev %d' % x)
466            for fs in ['fat', 'ext4']:
467                try:
468                    partitions = devices[x][fs]
469                except:
470                    print('No %s table on this device' % fs.upper())
471                    continue
472
473                for part in partitions:
474                    part_detect = 1
475                    output = u_boot_console.run_command('ls mmc %d:%s' % (x, part))
476                    if re.search(r'No \w+ table on this device', output):
477                        pytest.fail(
478                            '%s: Partition table not found %d' % (fs.upper(), x)
479                        )
480
481    if not part_detect:
482        pytest.skip('No partition detected')
483
484@pytest.mark.buildconfigspec('cmd_mmc')
485@pytest.mark.buildconfigspec('cmd_fs_generic')
486def test_mmc_load(u_boot_console):
487    if not mmc_set_up:
488        pytest.skip('No SD/MMC/eMMC controller available')
489
490    if not devices:
491        pytest.skip('No devices detected')
492
493    part_detect = 0
494    for x in range(0, controllers):
495        if devices[x]['detected'] == 'yes':
496            u_boot_console.run_command('mmc dev %d' % x)
497            for fs in ['fat', 'ext4']:
498                try:
499                    partitions = devices[x][fs]
500                except:
501                    print('No %s table on this device' % fs.upper())
502                    continue
503
504                for part in partitions:
505                    part_detect = 1
506                    addr = devices[x]['addr_%d' % part]
507                    size = devices[x]['size_%d' % part]
508                    expected_crc32 = devices[x]['expected_crc32_%d' % part]
509                    file = devices[x]['file_%d' % part]
510
511                    offset = random.randrange(128, 1024, 128)
512                    output = u_boot_console.run_command(
513                        'load mmc %d:%s %x /%s' % (x, part, addr + offset, file)
514                    )
515                    expected_text = '%d bytes read' % size
516                    assert expected_text in output
517
518                    output = u_boot_console.run_command(
519                        'crc32 %x $filesize' % (addr + offset)
520                    )
521                    assert expected_crc32 in output
522
523    if not part_detect:
524        pytest.skip('No partition detected')
525
526@pytest.mark.buildconfigspec('cmd_mmc')
527@pytest.mark.buildconfigspec('cmd_fs_generic')
528def test_mmc_save(u_boot_console):
529    if not mmc_set_up:
530        pytest.skip('No SD/MMC/eMMC controller available')
531
532    if not devices:
533        pytest.skip('No devices detected')
534
535    part_detect = 0
536    for x in range(0, controllers):
537        if devices[x]['detected'] == 'yes':
538            u_boot_console.run_command('mmc dev %d' % x)
539            for fs in ['fat', 'ext4']:
540                try:
541                    partitions = devices[x][fs]
542                except:
543                    print('No %s table on this device' % fs.upper())
544                    continue
545
546                for part in partitions:
547                    part_detect = 1
548                    addr = devices[x]['addr_%d' % part]
549                    size = 0
550                    file = devices[x]['file_%d' % part]
551
552                    offset = random.randrange(128, 1024, 128)
553                    output = u_boot_console.run_command(
554                        'save mmc %d:%s %x /%s %d'
555                        % (x, part, addr + offset, file, size)
556                    )
557                    expected_text = '%d bytes written' % size
558                    assert expected_text in output
559
560    if not part_detect:
561        pytest.skip('No partition detected')
562
563@pytest.mark.buildconfigspec('cmd_mmc')
564@pytest.mark.buildconfigspec('cmd_fat')
565@pytest.mark.buildconfigspec('cmd_memory')
566def test_mmc_fat_read_write_files(u_boot_console):
567    test_mmc_list(u_boot_console)
568    test_mmc_dev(u_boot_console)
569    test_mmcinfo(u_boot_console)
570    test_mmc_part(u_boot_console)
571    if not mmc_set_up:
572        pytest.skip('No SD/MMC/eMMC controller available')
573
574    if not devices:
575        pytest.skip('No devices detected')
576
577    part_detect = 0
578    fs = 'fat'
579
580    # Number of files to be written/read in MMC card
581    num_files = 100
582
583    for x in range(0, controllers):
584        if devices[x]['detected'] == 'yes':
585            u_boot_console.run_command('mmc dev %d' % x)
586            try:
587                partitions = devices[x][fs]
588            except:
589                print('No %s table on this device' % fs.upper())
590                continue
591
592            for part in partitions:
593                part_detect = 1
594                addr = u_boot_utils.find_ram_base(u_boot_console)
595                count_f = 0
596                addr_l = []
597                size_l = []
598                file_l = []
599                crc32_l = []
600                offset_l = []
601                addr_l.append(addr)
602
603                while count_f < num_files:
604                    size_l.append(random.randint(4, 1 * 1024 * 1024))
605
606                    # CRC32 count
607                    output = u_boot_console.run_command(
608                        'crc32 %x %x' % (addr_l[count_f], size_l[count_f])
609                    )
610                    m = re.search('==> (.+?)', output)
611                    if not m:
612                        pytest.fail('CRC32 failed')
613                    crc32_l.append(m.group(1))
614
615                    # Write operation
616                    file_l.append('%s_%d_%d' % ('uboot_test', count_f, size_l[count_f]))
617                    output = u_boot_console.run_command(
618                        '%swrite mmc %d:%s %x %s %x'
619                        % (
620                            fs,
621                            x,
622                            part,
623                            addr_l[count_f],
624                            file_l[count_f],
625                            size_l[count_f],
626                        )
627                    )
628                    assert 'Unable to write' not in output
629                    assert 'Error' not in output
630                    assert 'overflow' not in output
631                    expected_text = '%d bytes written' % size_l[count_f]
632                    assert expected_text in output
633
634                    addr_l.append(addr_l[count_f] + size_l[count_f] + 1048576)
635                    count_f += 1
636
637                count_f = 0
638                while count_f < num_files:
639                    alignment = int(
640                        u_boot_console.config.buildconfig.get(
641                            'config_sys_cacheline_size', 128
642                        )
643                    )
644                    offset_l.append(random.randrange(alignment, 1024, alignment))
645
646                    # Read operation
647                    output = u_boot_console.run_command(
648                        '%sload mmc %d:%s %x %s'
649                        % (
650                            fs,
651                            x,
652                            part,
653                            addr_l[count_f] + offset_l[count_f],
654                            file_l[count_f],
655                        )
656                    )
657                    assert 'Invalid FAT entry' not in output
658                    assert 'Unable to read file' not in output
659                    assert 'Misaligned buffer address' not in output
660                    expected_text = '%d bytes read' % size_l[count_f]
661                    assert expected_text in output
662
663                    output = u_boot_console.run_command(
664                        'crc32 %x $filesize' % (addr_l[count_f] + offset_l[count_f])
665                    )
666                    assert crc32_l[count_f] in output
667
668                    count_f += 1
669
670    if not part_detect:
671        pytest.skip('No %s partition detected' % fs.upper())
672