1# SPDX-License-Identifier: GPL-2.0
2# Copyright (c) 2017 Alison Chaiken
3# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved.
4
5# Test GPT manipulation commands.
6
7import os
8import pytest
9import utils
10
11"""
12These tests rely on a 4 MB disk image, which is automatically created by
13the test.
14"""
15
16# Mark all tests here as slow
17pytestmark = pytest.mark.slow
18
19def parse_gpt_parts(disk_str):
20    """Parser a partition string into a list of partitions.
21
22    Args:
23        disk_str: The disk description string, as returned by `gpt read`
24
25    Returns:
26        A list of parsed partitions. Each partition is a dictionary with the
27        string value from each specified key in the partition description, or a
28        key with with the value True for a boolean flag
29    """
30    parts = []
31    for part_str in disk_str.split(';'):
32        part = {}
33        for option in part_str.split(","):
34            if not option:
35                continue
36
37            if "=" in option:
38                key, value = option.split("=")
39                part[key] = value
40            else:
41                part[option] = True
42
43        if part:
44            parts.append(part)
45
46    return parts
47
48class GptTestDiskImage(object):
49    """Disk Image used by the GPT tests."""
50
51    def __init__(self, ubman):
52        """Initialize a new GptTestDiskImage object.
53
54        Args:
55            ubman: A U-Boot console.
56
57        Returns:
58            Nothing.
59        """
60
61        filename = 'test_gpt_disk_image.bin'
62
63        persistent = ubman.config.persistent_data_dir + '/' + filename
64        self.path = ubman.config.result_dir  + '/' + filename
65
66        with utils.persistent_file_helper(ubman.log, persistent):
67            if os.path.exists(persistent):
68                ubman.log.action('Disk image file ' + persistent +
69                    ' already exists')
70            else:
71                ubman.log.action('Generating ' + persistent)
72                fd = os.open(persistent, os.O_RDWR | os.O_CREAT)
73                os.ftruncate(fd, 4194304)
74                os.close(fd)
75                cmd = ('sgdisk',
76                    '--disk-guid=375a56f7-d6c9-4e81-b5f0-09d41ca89efe',
77                    persistent)
78                utils.run_and_log(ubman, cmd)
79                # part1 offset 1MB size 1MB
80                cmd = ('sgdisk', '--new=1:2048:4095', '--change-name=1:part1',
81                    '--partition-guid=1:33194895-67f6-4561-8457-6fdeed4f50a3',
82                    '-A 1:set:2',
83                    persistent)
84                # part2 offset 2MB size 1.5MB
85                utils.run_and_log(ubman, cmd)
86                cmd = ('sgdisk', '--new=2:4096:7167', '--change-name=2:part2',
87                    '--partition-guid=2:cc9c6e4a-6551-4cb5-87be-3210f96c86fb',
88                    persistent)
89                utils.run_and_log(ubman, cmd)
90                cmd = ('sgdisk', '--load-backup=' + persistent)
91                utils.run_and_log(ubman, cmd)
92
93        cmd = ('cp', persistent, self.path)
94        utils.run_and_log(ubman, cmd)
95
96@pytest.fixture(scope='function')
97def state_disk_image(ubman):
98    """pytest fixture to provide a GptTestDiskImage object to tests.
99    This is function-scoped because it uses ubman, which is also
100    function-scoped. A new disk is returned each time to prevent tests from
101    interfering with each other."""
102
103    return GptTestDiskImage(ubman)
104
105@pytest.mark.boardspec('sandbox')
106@pytest.mark.buildconfigspec('cmd_gpt')
107@pytest.mark.buildconfigspec('cmd_part')
108@pytest.mark.requiredtool('sgdisk')
109def test_gpt_read(state_disk_image, ubman):
110    """Test the gpt read command."""
111
112    ubman.run_command('host bind 0 ' + state_disk_image.path)
113    output = ubman.run_command('gpt read host 0')
114    assert 'Start 1MiB, size 1MiB' in output
115    assert 'Block size 512, name part1' in output
116    assert 'Start 2MiB, size 1MiB' in output
117    assert 'Block size 512, name part2' in output
118    output = ubman.run_command('part list host 0')
119    assert '0x00000800	0x00000fff	"part1"' in output
120    assert '0x00001000	0x00001bff	"part2"' in output
121
122@pytest.mark.boardspec('sandbox')
123@pytest.mark.buildconfigspec('cmd_gpt')
124@pytest.mark.buildconfigspec('partition_type_guid')
125@pytest.mark.requiredtool('sgdisk')
126def test_gpt_read_var(state_disk_image, ubman):
127    """Test the gpt read command."""
128
129    ubman.run_command('host bind 0 ' + state_disk_image.path)
130    output = ubman.run_command('gpt read host 0 gpt_parts')
131    assert 'success!' in output
132
133    output = ubman.run_command('echo ${gpt_parts}')
134    parts = parse_gpt_parts(output.rstrip())
135
136    assert parts == [
137        {
138            "uuid_disk": "375a56f7-d6c9-4e81-b5f0-09d41ca89efe",
139        },
140        {
141            "name": "part1",
142            "start": "0x100000",
143            "size": "0x100000",
144            "type": "0fc63daf-8483-4772-8e79-3d69d8477de4",
145            "uuid": "33194895-67f6-4561-8457-6fdeed4f50a3",
146            "bootable": True,
147        },
148        {
149            "name": "part2",
150            "start": "0x200000",
151            "size": "0x180000",
152            "type": "0fc63daf-8483-4772-8e79-3d69d8477de4",
153            "uuid": "cc9c6e4a-6551-4cb5-87be-3210f96c86fb",
154        },
155    ]
156
157@pytest.mark.boardspec('sandbox')
158@pytest.mark.buildconfigspec('cmd_gpt')
159@pytest.mark.requiredtool('sgdisk')
160def test_gpt_verify(state_disk_image, ubman):
161    """Test the gpt verify command."""
162
163    ubman.run_command('host bind 0 ' + state_disk_image.path)
164    output = ubman.run_command('gpt verify host 0')
165    assert 'Verify GPT: success!' in output
166
167@pytest.mark.boardspec('sandbox')
168@pytest.mark.buildconfigspec('cmd_gpt')
169@pytest.mark.requiredtool('sgdisk')
170def test_gpt_repair(state_disk_image, ubman):
171    """Test the gpt repair command."""
172
173    ubman.run_command('host bind 0 ' + state_disk_image.path)
174    output = ubman.run_command('gpt repair host 0')
175    assert 'Repairing GPT: success!' in output
176
177@pytest.mark.boardspec('sandbox')
178@pytest.mark.buildconfigspec('cmd_gpt')
179@pytest.mark.requiredtool('sgdisk')
180def test_gpt_guid(state_disk_image, ubman):
181    """Test the gpt guid command."""
182
183    ubman.run_command('host bind 0 ' + state_disk_image.path)
184    output = ubman.run_command('gpt guid host 0')
185    assert '375a56f7-d6c9-4e81-b5f0-09d41ca89efe' in output
186
187@pytest.mark.boardspec('sandbox')
188@pytest.mark.buildconfigspec('cmd_gpt')
189@pytest.mark.requiredtool('sgdisk')
190def test_gpt_setenv(state_disk_image, ubman):
191    """Test the gpt setenv command."""
192    ubman.run_command('host bind 0 ' + state_disk_image.path)
193    output = ubman.run_command('gpt setenv host 0 part1')
194    assert 'success!' in output
195    output = ubman.run_command('echo ${gpt_partition_addr}')
196    assert output.rstrip() == '800'
197    output = ubman.run_command('echo ${gpt_partition_size}')
198    assert output.rstrip() == '800'
199    output = ubman.run_command('echo ${gpt_partition_name}')
200    assert output.rstrip() == 'part1'
201    output = ubman.run_command('echo ${gpt_partition_entry}')
202    assert output.rstrip() == '1'
203    output = ubman.run_command('echo ${gpt_partition_bootable}')
204    assert output.rstrip() == '1'
205
206    output = ubman.run_command('gpt setenv host 0 part2')
207    assert 'success!' in output
208    output = ubman.run_command('echo ${gpt_partition_addr}')
209    assert output.rstrip() == '1000'
210    output = ubman.run_command('echo ${gpt_partition_size}')
211    assert output.rstrip() == 'c00'
212    output = ubman.run_command('echo ${gpt_partition_name}')
213    assert output.rstrip() == 'part2'
214    output = ubman.run_command('echo ${gpt_partition_entry}')
215    assert output.rstrip() == '2'
216    output = ubman.run_command('echo ${gpt_partition_bootable}')
217    assert output.rstrip() == '0'
218
219@pytest.mark.boardspec('sandbox')
220@pytest.mark.buildconfigspec('cmd_gpt')
221@pytest.mark.requiredtool('sgdisk')
222def test_gpt_save_guid(state_disk_image, ubman):
223    """Test the gpt guid command to save GUID into a string."""
224
225    if ubman.config.buildconfig.get('config_cmd_gpt', 'n') != 'y':
226        pytest.skip('gpt command not supported')
227    ubman.run_command('host bind 0 ' + state_disk_image.path)
228    output = ubman.run_command('gpt guid host 0 newguid')
229    output = ubman.run_command('printenv newguid')
230    assert '375a56f7-d6c9-4e81-b5f0-09d41ca89efe' in output
231
232@pytest.mark.boardspec('sandbox')
233@pytest.mark.buildconfigspec('cmd_gpt')
234@pytest.mark.requiredtool('sgdisk')
235def test_gpt_part_type_uuid(state_disk_image, ubman):
236    """Test the gpt partittion type UUID command."""
237
238    ubman.run_command('host bind 0 ' + state_disk_image.path)
239    output = ubman.run_command('part type host 0:1')
240    assert '0fc63daf-8483-4772-8e79-3d69d8477de4' in output
241
242@pytest.mark.boardspec('sandbox')
243@pytest.mark.buildconfigspec('cmd_gpt')
244@pytest.mark.requiredtool('sgdisk')
245def test_gpt_part_type_save_uuid(state_disk_image, ubman):
246    """Test the gpt partittion type to save UUID into a string."""
247
248    if ubman.config.buildconfig.get('config_cmd_gpt', 'n') != 'y':
249        pytest.skip('gpt command not supported')
250    ubman.run_command('host bind 0 ' + state_disk_image.path)
251    output = ubman.run_command('part type host 0:1 newguid')
252    output = ubman.run_command('printenv newguid')
253    assert '0fc63daf-8483-4772-8e79-3d69d8477de4' in output
254
255@pytest.mark.boardspec('sandbox')
256@pytest.mark.buildconfigspec('cmd_gpt')
257@pytest.mark.buildconfigspec('cmd_gpt_rename')
258@pytest.mark.buildconfigspec('cmd_part')
259@pytest.mark.requiredtool('sgdisk')
260def test_gpt_rename_partition(state_disk_image, ubman):
261    """Test the gpt rename command to write partition names."""
262
263    ubman.run_command('host bind 0 ' + state_disk_image.path)
264    ubman.run_command('gpt rename host 0 1 first')
265    output = ubman.run_command('gpt read host 0')
266    assert 'name first' in output
267    ubman.run_command('gpt rename host 0 2 second')
268    output = ubman.run_command('gpt read host 0')
269    assert 'name second' in output
270    output = ubman.run_command('part list host 0')
271    assert '0x00000800	0x00000fff	"first"' in output
272    assert '0x00001000	0x00001bff	"second"' in output
273
274@pytest.mark.boardspec('sandbox')
275@pytest.mark.buildconfigspec('cmd_gpt')
276@pytest.mark.buildconfigspec('cmd_gpt_rename')
277@pytest.mark.buildconfigspec('cmd_part')
278@pytest.mark.requiredtool('sgdisk')
279def test_gpt_swap_partitions(state_disk_image, ubman):
280    """Test the gpt swap command to exchange two partition names."""
281
282    ubman.run_command('host bind 0 ' + state_disk_image.path)
283    output = ubman.run_command('part list host 0')
284    assert '0x00000800	0x00000fff	"part1"' in output
285    assert '0x00001000	0x00001bff	"part2"' in output
286    ubman.run_command('gpt swap host 0 part1 part2')
287    output = ubman.run_command('part list host 0')
288    assert '0x00000800	0x00000fff	"part2"' in output
289    assert '0x00001000	0x00001bff	"part1"' in output
290
291@pytest.mark.buildconfigspec('cmd_gpt')
292@pytest.mark.buildconfigspec('cmd_gpt_rename')
293@pytest.mark.buildconfigspec('cmd_part')
294@pytest.mark.requiredtool('sgdisk')
295def test_gpt_set_bootable(state_disk_image, ubman):
296    """Test the gpt set-bootable command."""
297
298    ubman.run_command('host bind 0 ' + state_disk_image.path)
299    parts = ('part2', 'part1')
300    for bootable in parts:
301        output = ubman.run_command(f'gpt set-bootable host 0 {bootable}')
302        assert 'success!' in output
303
304        for p in parts:
305            output = ubman.run_command(f'gpt setenv host 0 {p}')
306            assert 'success!' in output
307            output = ubman.run_command('echo ${gpt_partition_bootable}')
308            if p == bootable:
309                assert output.rstrip() == '1'
310            else:
311                assert output.rstrip() == '0'
312
313@pytest.mark.boardspec('sandbox')
314@pytest.mark.buildconfigspec('cmd_gpt')
315@pytest.mark.buildconfigspec('cmd_part')
316@pytest.mark.requiredtool('sgdisk')
317def test_gpt_write(state_disk_image, ubman):
318    """Test the gpt write command."""
319
320    ubman.run_command('host bind 0 ' + state_disk_image.path)
321    output = ubman.run_command('gpt write host 0 "name=all,size=0"')
322    assert 'Writing GPT: success!' in output
323    output = ubman.run_command('part list host 0')
324    assert '0x00000022	0x00001fde	"all"' in output
325    output = ubman.run_command('gpt write host 0 "uuid_disk=375a56f7-d6c9-4e81-b5f0-09d41ca89efe;name=first,start=1M,size=1M;name=second,start=0x200000,size=0x180000;"')
326    assert 'Writing GPT: success!' in output
327    output = ubman.run_command('part list host 0')
328    assert '0x00000800	0x00000fff	"first"' in output
329    assert '0x00001000	0x00001bff	"second"' in output
330    output = ubman.run_command('gpt guid host 0')
331    assert '375a56f7-d6c9-4e81-b5f0-09d41ca89efe' in output
332
333@pytest.mark.boardspec('sandbox')
334@pytest.mark.buildconfigspec('cmd_gpt')
335@pytest.mark.buildconfigspec('cmd_part')
336@pytest.mark.buildconfigspec('partition_type_guid')
337@pytest.mark.requiredtool('sgdisk')
338def test_gpt_write_part_type(state_disk_image, ubman):
339    """Test the gpt command with part type uuid."""
340
341    output = ubman.run_command('gpt write host 0 "name=part1,type=data,size=1M;name=part2,size=512K,type=system;name=part3,size=65536,type=u-boot-env;name=part4,size=65536,type=375a56f7-d6c9-4e81-b5f0-09d41ca89efe;name=part5,size=-,type=linux"')
342    assert 'Writing GPT: success!' in output
343    output = ubman.run_command('part list host 0')
344    assert '1\t0x00000022\t0x00000821\t"part1"' in output
345    assert 'ebd0a0a2-b9e5-4433-87c0-68b6b72699c7' in output
346    assert '(data)' in output
347    assert '2\t0x00000822\t0x00000c21\t"part2"' in output
348    assert 'c12a7328-f81f-11d2-ba4b-00a0c93ec93b' in output
349    assert '(EFI System Partition)' in output
350    assert '3\t0x00000c22\t0x00000ca1\t"part3"' in output
351    assert '3de21764-95bd-54bd-a5c3-4abe786f38a8' in output
352    assert '(u-boot-env)' in output
353    assert '4\t0x00000ca2\t0x00000d21\t"part4"' in output
354    assert 'ebd0a0a2-b9e5-4433-87c0-68b6b72699c7' in output
355    assert '(375a56f7-d6c9-4e81-b5f0-09d41ca89efe)' in output
356    assert '5\t0x00000d22\t0x00001fde\t"part5"' in output
357    assert '0fc63daf-8483-4772-8e79-3d69d8477de4' in output
358    assert '(linux)' in output
359
360@pytest.mark.buildconfigspec('cmd_gpt')
361@pytest.mark.buildconfigspec('cmd_gpt_rename')
362@pytest.mark.buildconfigspec('cmd_part')
363@pytest.mark.requiredtool('sgdisk')
364def test_gpt_transpose(state_disk_image, ubman):
365    """Test the gpt transpose command."""
366
367    ubman.run_command('host bind 0 ' + state_disk_image.path)
368    output = ubman.run_command('part list host 0')
369    assert '1\t0x00000800\t0x00000fff\t"part1"' in output
370    assert '2\t0x00001000\t0x00001bff\t"part2"' in output
371
372    output = ubman.run_command('gpt transpose host 0 1 2')
373    assert 'success!' in output
374
375    output = ubman.run_command('part list host 0')
376    assert '2\t0x00000800\t0x00000fff\t"part1"' in output
377    assert '1\t0x00001000\t0x00001bff\t"part2"' in output
378