aboutsummaryrefslogtreecommitdiff
path: root/test/py/tests/test_net_boot.py
blob: e5e8ec688df1f0c5855f5f64486082d19cbfdcde (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
# SPDX-License-Identifier: GPL-2.0
# (C) Copyright 2023, Advanced Micro Devices, Inc.

import pytest
import u_boot_utils
import test_net
import re

"""
Note: This test relies on boardenv_* containing configuration values to define
which the network environment available for testing. Without this, this test
will be automatically skipped.

For example:

# Details regarding a boot image file that may be read from a TFTP server. This
# variable may be omitted or set to None if TFTP boot testing is not possible
# or desired.
env__net_tftp_bootable_file = {
    'fn': 'image.ub',
    'addr': 0x10000000,
    'size': 5058624,
    'crc32': 'c2244b26',
    'pattern': 'Linux',
    'config': 'config@2',
    'timeout': 50000,
    'check_type': 'boot_error',
    'check_pattern': 'ERROR',
}

# False or omitted if a TFTP boot test should be tested.
# If TFTP boot testing is not possible or desired, set this variable to True.
# For example: If FIT image is not proper to boot
env__tftp_boot_test_skip = False

# Here is the example of FIT image configurations:
configurations {
    default = "config@1";
    config@1 {
        description = "Boot Linux kernel with config@1";
        kernel = "kernel@0";
        fdt = "fdt@0";
        ramdisk = "ramdisk@0";
        hash@1 {
            algo = "sha1";
        };
    };
    config@2 {
        description = "Boot Linux kernel with config@2";
        kernel = "kernel@1";
        fdt = "fdt@1";
        ramdisk = "ramdisk@1";
        hash@1 {
            algo = "sha1";
        };
    };
};

# Details regarding a file that may be read from a TFTP server. This variable
# may be omitted or set to None if PXE testing is not possible or desired.
env__net_pxe_bootable_file = {
    'fn': 'default',
    'addr': 0x10000000,
    'size': 74,
    'timeout': 50000,
    'pattern': 'Linux',
    'valid_label': '1',
    'invalid_label': '2',
    'exp_str_invalid': 'Skipping install for failure retrieving',
    'local_label': '3',
    'exp_str_local': 'missing environment variable: localcmd',
    'empty_label': '4',
    'exp_str_empty': 'No kernel given, skipping boot',
    'check_type': 'boot_error',
    'check_pattern': 'ERROR',
}

# False or omitted if a PXE boot test should be tested.
# If PXE boot testing is not possible or desired, set this variable to True.
# For example: If pxe configuration file is not proper to boot
env__pxe_boot_test_skip = False

# Here is the example of pxe configuration file ordered based on the execution
# flow:
1) /tftpboot/pxelinux.cfg/default-arm-zynqmp

    menu include pxelinux.cfg/default-arm
    timeout 50

    default Linux

2) /tftpboot/pxelinux.cfg/default-arm

    menu title Linux boot selections
    menu include pxelinux.cfg/default

    label install
        menu label Invalid boot
        kernel kernels/install.bin
        append console=ttyAMA0,38400 debug earlyprintk
        initrd initrds/uzInitrdDebInstall

    label local
        menu label Local boot
        append root=/dev/sdb1
        localboot 1

    label boot
        menu label Empty boot

3) /tftpboot/pxelinux.cfg/default

    label Linux
        menu label Boot kernel
        kernel Image
        fdt system.dtb
        initrd rootfs.cpio.gz.u-boot
"""

def setup_tftpboot_boot(u_boot_console):
    f = u_boot_console.config.env.get('env__net_tftp_bootable_file', None)
    if not f:
        pytest.skip('No TFTP bootable file to read')

    test_net.test_net_dhcp(u_boot_console)
    if not test_net.net_set_up:
        test_net.test_net_setup_static(u_boot_console)

    addr = f.get('addr', None)
    if not addr:
        addr = u_boot_utils.find_ram_base(u_boot_console)

    fn = f['fn']
    timeout = f.get('timeout', 50000)

    with u_boot_console.temporary_timeout(timeout):
        output = u_boot_console.run_command('tftpboot %x %s' % (addr, fn))

    expected_text = 'Bytes transferred = '
    sz = f.get('size', None)
    if sz:
        expected_text += '%d' % sz
    assert expected_text in output

    expected_crc = f.get('crc32', None)
    output = u_boot_console.run_command('crc32 %x $filesize' % addr)
    if expected_crc:
        assert expected_crc in output

    pattern = f.get('pattern')
    chk_type = f.get('check_type', 'boot_error')
    chk_pattern = re.compile(f.get('check_pattern', 'ERROR'))
    config = f.get('config', None)

    return addr, timeout, pattern, chk_type, chk_pattern, config

@pytest.mark.buildconfigspec('cmd_net')
def test_net_tftpboot_boot(u_boot_console):
    """Boot the loaded image

    A boot file (fit image) is downloaded from the TFTP server and booted using
    bootm command with the default fit configuration, its boot log pattern are
    validated.

    The details of the file to download are provided by the boardenv_* file;
    see the comment at the beginning of this file.
    """
    if u_boot_console.config.env.get('env__tftp_boot_test_skip', True):
        pytest.skip('TFTP boot test is not enabled!')

    addr, timeout, pattern, chk_type, chk_pattern, imcfg = setup_tftpboot_boot(
        u_boot_console
    )

    if imcfg:
        bootcmd = 'bootm %x#%s' % (addr, imcfg)
    else:
        bootcmd = 'bootm %x' % addr

    with u_boot_console.enable_check(
        chk_type, chk_pattern
    ), u_boot_console.temporary_timeout(timeout):
        try:
            # wait_for_prompt=False makes the core code not wait for the U-Boot
            # prompt code to be seen, since it won't be on a successful kernel
            # boot
            u_boot_console.run_command(bootcmd, wait_for_prompt=False)

            # Wait for boot log pattern
            u_boot_console.wait_for(pattern)
        finally:
            # This forces the console object to be shutdown, so any subsequent
            # test will reset the board back into U-Boot. We want to force this
            # no matter whether the kernel boot passed or failed.
            u_boot_console.drain_console()
            u_boot_console.cleanup_spawn()

def setup_pxe_boot(u_boot_console):
    f = u_boot_console.config.env.get('env__net_pxe_bootable_file', None)
    if not f:
        pytest.skip('No PXE bootable file to read')

    test_net.test_net_dhcp(u_boot_console)
    test_net.test_net_setup_static(u_boot_console)
    return f

@pytest.mark.buildconfigspec('cmd_net')
@pytest.mark.buildconfigspec('cmd_pxe')
def test_net_pxe_boot(u_boot_console):
    """Test the pxe boot command.

    A pxe configuration file is downloaded from the TFTP server and interpreted
    to boot the images mentioned in pxe configuration file.

    The details of the file to download are provided by the boardenv_* file;
    see the comment at the beginning of this file.
    """
    if u_boot_console.config.env.get('env__pxe_boot_test_skip', True):
        pytest.skip('PXE boot test is not enabled!')

    f = setup_pxe_boot(u_boot_console)
    addr = f.get('addr', None)
    timeout = f.get('timeout', u_boot_console.p.timeout)
    fn = f['fn']

    with u_boot_console.temporary_timeout(timeout):
        output = u_boot_console.run_command('pxe get')

    expected_text = 'Bytes transferred = '
    sz = f.get('size', None)
    if sz:
        expected_text += '%d' % sz
    assert 'TIMEOUT' not in output
    assert expected_text in output
    assert "Config file 'default.boot' found" in output

    pattern = f.get('pattern')
    chk_type = f.get('check_type', 'boot_error')
    chk_pattern = re.compile(f.get('check_pattern', 'ERROR'))

    if not addr:
        pxe_boot_cmd = 'pxe boot'
    else:
        pxe_boot_cmd = 'pxe boot %x' % addr

    with u_boot_console.enable_check(
        chk_type, chk_pattern
    ), u_boot_console.temporary_timeout(timeout):
        try:
            u_boot_console.run_command(pxe_boot_cmd, wait_for_prompt=False)
            u_boot_console.wait_for(pattern)
        finally:
            u_boot_console.drain_console()
            u_boot_console.cleanup_spawn()

@pytest.mark.buildconfigspec('cmd_net')
@pytest.mark.buildconfigspec('cmd_pxe')
def test_net_pxe_boot_config(u_boot_console):
    """Test the pxe boot command by selecting different combination of labels

    A pxe configuration file is downloaded from the TFTP server and interpreted
    to boot the images mentioned in pxe configuration file.

    The details of the file to download are provided by the boardenv_* file;
    see the comment at the beginning of this file.
    """
    if u_boot_console.config.env.get('env__pxe_boot_test_skip', True):
        pytest.skip('PXE boot test is not enabled!')

    f = setup_pxe_boot(u_boot_console)
    addr = f.get('addr', None)
    timeout = f.get('timeout', u_boot_console.p.timeout)
    fn = f['fn']
    local_label = f['local_label']
    empty_label = f['empty_label']
    exp_str_local = f['exp_str_local']
    exp_str_empty = f['exp_str_empty']

    with u_boot_console.temporary_timeout(timeout):
        output = u_boot_console.run_command('pxe get')

    expected_text = 'Bytes transferred = '
    sz = f.get('size', None)
    if sz:
        expected_text += '%d' % sz
    assert 'TIMEOUT' not in output
    assert expected_text in output
    assert "Config file 'default.boot' found" in output

    pattern = f.get('pattern')
    chk_type = f.get('check_type', 'boot_error')
    chk_pattern = re.compile(f.get('check_pattern', 'ERROR'))

    if not addr:
        pxe_boot_cmd = 'pxe boot'
    else:
        pxe_boot_cmd = 'pxe boot %x' % addr

    with u_boot_console.enable_check(
        chk_type, chk_pattern
    ), u_boot_console.temporary_timeout(timeout):
        try:
            u_boot_console.run_command(pxe_boot_cmd, wait_for_prompt=False)

            # pxe config is loaded where multiple labels are there and need to
            # select particular label to boot and check for expected string
            # In this case, local label is selected and it should look for
            # localcmd env variable and if that variable is not defined it
            # should not boot it and come out to u-boot prompt
            u_boot_console.wait_for('Enter choice:')
            u_boot_console.run_command(local_label, wait_for_prompt=False)
            expected_str = u_boot_console.p.expect([exp_str_local])
            assert (
                expected_str == 0
            ), f'Expected string: {exp_str_local} did not match!'

            # In this case, empty label is selected and it should look for
            # kernel image path and if it is not set it should fail it and load
            # default label to boot
            u_boot_console.run_command(pxe_boot_cmd, wait_for_prompt=False)
            u_boot_console.wait_for('Enter choice:')
            u_boot_console.run_command(empty_label, wait_for_prompt=False)
            expected_str = u_boot_console.p.expect([exp_str_empty])
            assert (
                expected_str == 0
            ), f'Expected string: {exp_str_empty} did not match!'

            u_boot_console.wait_for(pattern)
        finally:
            u_boot_console.drain_console()
            u_boot_console.cleanup_spawn()

@pytest.mark.buildconfigspec('cmd_net')
@pytest.mark.buildconfigspec('cmd_pxe')
def test_net_pxe_boot_config_invalid(u_boot_console):
    """Test the pxe boot command by selecting invalid label

    A pxe configuration file is downloaded from the TFTP server and interpreted
    to boot the images mentioned in pxe configuration file.

    The details of the file to download are provided by the boardenv_* file;
    see the comment at the beginning of this file.
    """
    if u_boot_console.config.env.get('env__pxe_boot_test_skip', True):
        pytest.skip('PXE boot test is not enabled!')

    f = setup_pxe_boot(u_boot_console)
    addr = f.get('addr', None)
    timeout = f.get('timeout', u_boot_console.p.timeout)
    fn = f['fn']
    invalid_label = f['invalid_label']
    exp_str_invalid = f['exp_str_invalid']

    with u_boot_console.temporary_timeout(timeout):
        output = u_boot_console.run_command('pxe get')

    expected_text = 'Bytes transferred = '
    sz = f.get('size', None)
    if sz:
        expected_text += '%d' % sz
    assert 'TIMEOUT' not in output
    assert expected_text in output
    assert "Config file 'default.boot' found" in output

    pattern = f.get('pattern')
    if not addr:
        pxe_boot_cmd = 'pxe boot'
    else:
        pxe_boot_cmd = 'pxe boot %x' % addr

    with u_boot_console.temporary_timeout(timeout):
        try:
            u_boot_console.run_command(pxe_boot_cmd, wait_for_prompt=False)

            # pxe config is loaded where multiple labels are there and need to
            # select particular label to boot and check for expected string
            # In this case invalid label is selected, it should load invalid
            # label and if it fails it should load the default label to boot
            u_boot_console.wait_for('Enter choice:')
            u_boot_console.run_command(invalid_label, wait_for_prompt=False)
            expected_str = u_boot_console.p.expect([exp_str_invalid])
            assert (
                expected_str == 0
            ), f'Expected string: {exp_str_invalid} did not match!'

            u_boot_console.wait_for(pattern)
        finally:
            u_boot_console.drain_console()
            u_boot_console.cleanup_spawn()