Love Kumar | b2835c5 | 2024-06-05 15:19:35 +0530 | [diff] [blame] | 1 | # SPDX-License-Identifier: GPL-2.0 |
| 2 | # (C) Copyright 2023, Advanced Micro Devices, Inc. |
| 3 | |
| 4 | import pytest |
| 5 | import u_boot_utils |
| 6 | import test_net |
| 7 | import re |
| 8 | |
| 9 | """ |
| 10 | Note: This test relies on boardenv_* containing configuration values to define |
| 11 | which the network environment available for testing. Without this, this test |
| 12 | will be automatically skipped. |
| 13 | |
| 14 | For example: |
| 15 | |
| 16 | # Details regarding a boot image file that may be read from a TFTP server. This |
| 17 | # variable may be omitted or set to None if TFTP boot testing is not possible |
| 18 | # or desired. |
| 19 | env__net_tftp_bootable_file = { |
| 20 | 'fn': 'image.ub', |
| 21 | 'addr': 0x10000000, |
| 22 | 'size': 5058624, |
| 23 | 'crc32': 'c2244b26', |
| 24 | 'pattern': 'Linux', |
| 25 | 'config': 'config@2', |
| 26 | 'timeout': 50000, |
| 27 | 'check_type': 'boot_error', |
| 28 | 'check_pattern': 'ERROR', |
| 29 | } |
| 30 | |
| 31 | # False or omitted if a TFTP boot test should be tested. |
| 32 | # If TFTP boot testing is not possible or desired, set this variable to True. |
| 33 | # For example: If FIT image is not proper to boot |
| 34 | env__tftp_boot_test_skip = False |
| 35 | |
| 36 | # Here is the example of FIT image configurations: |
| 37 | configurations { |
| 38 | default = "config@1"; |
| 39 | config@1 { |
| 40 | description = "Boot Linux kernel with config@1"; |
| 41 | kernel = "kernel@0"; |
| 42 | fdt = "fdt@0"; |
| 43 | ramdisk = "ramdisk@0"; |
| 44 | hash@1 { |
| 45 | algo = "sha1"; |
| 46 | }; |
| 47 | }; |
| 48 | config@2 { |
| 49 | description = "Boot Linux kernel with config@2"; |
| 50 | kernel = "kernel@1"; |
| 51 | fdt = "fdt@1"; |
| 52 | ramdisk = "ramdisk@1"; |
| 53 | hash@1 { |
| 54 | algo = "sha1"; |
| 55 | }; |
| 56 | }; |
| 57 | }; |
| 58 | |
| 59 | # Details regarding a file that may be read from a TFTP server. This variable |
| 60 | # may be omitted or set to None if PXE testing is not possible or desired. |
| 61 | env__net_pxe_bootable_file = { |
| 62 | 'fn': 'default', |
| 63 | 'addr': 0x10000000, |
| 64 | 'size': 74, |
| 65 | 'timeout': 50000, |
| 66 | 'pattern': 'Linux', |
| 67 | 'valid_label': '1', |
| 68 | 'invalid_label': '2', |
| 69 | 'exp_str_invalid': 'Skipping install for failure retrieving', |
| 70 | 'local_label': '3', |
| 71 | 'exp_str_local': 'missing environment variable: localcmd', |
| 72 | 'empty_label': '4', |
| 73 | 'exp_str_empty': 'No kernel given, skipping boot', |
| 74 | 'check_type': 'boot_error', |
| 75 | 'check_pattern': 'ERROR', |
| 76 | } |
| 77 | |
| 78 | # False or omitted if a PXE boot test should be tested. |
| 79 | # If PXE boot testing is not possible or desired, set this variable to True. |
| 80 | # For example: If pxe configuration file is not proper to boot |
| 81 | env__pxe_boot_test_skip = False |
| 82 | |
| 83 | # Here is the example of pxe configuration file ordered based on the execution |
| 84 | # flow: |
| 85 | 1) /tftpboot/pxelinux.cfg/default-arm-zynqmp |
| 86 | |
| 87 | menu include pxelinux.cfg/default-arm |
| 88 | timeout 50 |
| 89 | |
| 90 | default Linux |
| 91 | |
| 92 | 2) /tftpboot/pxelinux.cfg/default-arm |
| 93 | |
| 94 | menu title Linux boot selections |
| 95 | menu include pxelinux.cfg/default |
| 96 | |
| 97 | label install |
| 98 | menu label Invalid boot |
| 99 | kernel kernels/install.bin |
| 100 | append console=ttyAMA0,38400 debug earlyprintk |
| 101 | initrd initrds/uzInitrdDebInstall |
| 102 | |
| 103 | label local |
| 104 | menu label Local boot |
| 105 | append root=/dev/sdb1 |
| 106 | localboot 1 |
| 107 | |
| 108 | label boot |
| 109 | menu label Empty boot |
| 110 | |
| 111 | 3) /tftpboot/pxelinux.cfg/default |
| 112 | |
| 113 | label Linux |
| 114 | menu label Boot kernel |
| 115 | kernel Image |
| 116 | fdt system.dtb |
| 117 | initrd rootfs.cpio.gz.u-boot |
| 118 | """ |
| 119 | |
| 120 | def setup_networking(u_boot_console): |
| 121 | test_net.test_net_dhcp(u_boot_console) |
| 122 | if not test_net.net_set_up: |
| 123 | test_net.test_net_setup_static(u_boot_console) |
| 124 | |
| 125 | def setup_tftpboot_boot(u_boot_console): |
| 126 | f = u_boot_console.config.env.get('env__net_tftp_bootable_file', None) |
| 127 | if not f: |
| 128 | pytest.skip('No TFTP bootable file to read') |
| 129 | |
| 130 | setup_networking(u_boot_console) |
| 131 | addr = f.get('addr', None) |
| 132 | if not addr: |
| 133 | addr = u_boot_utils.find_ram_base(u_boot_console) |
| 134 | |
| 135 | fn = f['fn'] |
| 136 | timeout = f.get('timeout', 50000) |
| 137 | |
| 138 | with u_boot_console.temporary_timeout(timeout): |
| 139 | output = u_boot_console.run_command('tftpboot %x %s' % (addr, fn)) |
| 140 | |
| 141 | expected_text = 'Bytes transferred = ' |
| 142 | sz = f.get('size', None) |
| 143 | if sz: |
| 144 | expected_text += '%d' % sz |
| 145 | assert expected_text in output |
| 146 | |
| 147 | expected_crc = f.get('crc32', None) |
| 148 | output = u_boot_console.run_command('crc32 %x $filesize' % addr) |
| 149 | if expected_crc: |
| 150 | assert expected_crc in output |
| 151 | |
| 152 | pattern = f.get('pattern') |
| 153 | chk_type = f.get('check_type', 'boot_error') |
| 154 | chk_pattern = re.compile(f.get('check_pattern', 'ERROR')) |
| 155 | config = f.get('config', None) |
| 156 | |
| 157 | return addr, timeout, pattern, chk_type, chk_pattern, config |
| 158 | |
Tom Rini | 5568cc3 | 2024-06-18 14:23:43 -0600 | [diff] [blame] | 159 | @pytest.mark.buildconfigspec('cmd_tftpboot') |
Love Kumar | b2835c5 | 2024-06-05 15:19:35 +0530 | [diff] [blame] | 160 | def test_net_tftpboot_boot(u_boot_console): |
| 161 | """Boot the loaded image |
| 162 | |
| 163 | A boot file (fit image) is downloaded from the TFTP server and booted using |
| 164 | bootm command with the default fit configuration, its boot log pattern are |
| 165 | validated. |
| 166 | |
| 167 | The details of the file to download are provided by the boardenv_* file; |
| 168 | see the comment at the beginning of this file. |
| 169 | """ |
| 170 | if u_boot_console.config.env.get('env__tftp_boot_test_skip', True): |
| 171 | pytest.skip('TFTP boot test is not enabled!') |
| 172 | |
| 173 | addr, timeout, pattern, chk_type, chk_pattern, imcfg = setup_tftpboot_boot( |
| 174 | u_boot_console |
| 175 | ) |
| 176 | |
| 177 | if imcfg: |
| 178 | bootcmd = 'bootm %x#%s' % (addr, imcfg) |
| 179 | else: |
| 180 | bootcmd = 'bootm %x' % addr |
| 181 | |
| 182 | with u_boot_console.enable_check( |
| 183 | chk_type, chk_pattern |
| 184 | ), u_boot_console.temporary_timeout(timeout): |
| 185 | try: |
| 186 | # wait_for_prompt=False makes the core code not wait for the U-Boot |
| 187 | # prompt code to be seen, since it won't be on a successful kernel |
| 188 | # boot |
| 189 | u_boot_console.run_command(bootcmd, wait_for_prompt=False) |
| 190 | |
| 191 | # Wait for boot log pattern |
| 192 | u_boot_console.wait_for(pattern) |
| 193 | finally: |
| 194 | # This forces the console object to be shutdown, so any subsequent |
| 195 | # test will reset the board back into U-Boot. We want to force this |
| 196 | # no matter whether the kernel boot passed or failed. |
| 197 | u_boot_console.drain_console() |
| 198 | u_boot_console.cleanup_spawn() |
| 199 | |
| 200 | def setup_pxe_boot(u_boot_console): |
| 201 | f = u_boot_console.config.env.get('env__net_pxe_bootable_file', None) |
| 202 | if not f: |
| 203 | pytest.skip('No PXE bootable file to read') |
| 204 | |
| 205 | setup_networking(u_boot_console) |
| 206 | bootfile = u_boot_console.run_command('echo $bootfile') |
| 207 | if not bootfile: |
| 208 | bootfile = '<NULL>' |
| 209 | |
| 210 | return f, bootfile |
| 211 | |
Love Kumar | b2835c5 | 2024-06-05 15:19:35 +0530 | [diff] [blame] | 212 | @pytest.mark.buildconfigspec('cmd_pxe') |
| 213 | def test_net_pxe_boot(u_boot_console): |
| 214 | """Test the pxe boot command. |
| 215 | |
| 216 | A pxe configuration file is downloaded from the TFTP server and interpreted |
| 217 | to boot the images mentioned in pxe configuration file. |
| 218 | |
| 219 | The details of the file to download are provided by the boardenv_* file; |
| 220 | see the comment at the beginning of this file. |
| 221 | """ |
| 222 | if u_boot_console.config.env.get('env__pxe_boot_test_skip', True): |
| 223 | pytest.skip('PXE boot test is not enabled!') |
| 224 | |
| 225 | f, bootfile = setup_pxe_boot(u_boot_console) |
| 226 | addr = f.get('addr', None) |
| 227 | timeout = f.get('timeout', u_boot_console.p.timeout) |
| 228 | fn = f['fn'] |
| 229 | |
| 230 | if addr: |
| 231 | u_boot_console.run_command('setenv pxefile_addr_r %x' % addr) |
| 232 | |
| 233 | with u_boot_console.temporary_timeout(timeout): |
| 234 | output = u_boot_console.run_command('pxe get') |
| 235 | |
| 236 | expected_text = 'Bytes transferred = ' |
| 237 | sz = f.get('size', None) |
| 238 | if sz: |
| 239 | expected_text += '%d' % sz |
| 240 | assert 'TIMEOUT' not in output |
| 241 | assert expected_text in output |
| 242 | assert f"Config file '{bootfile}' found" in output |
| 243 | |
| 244 | pattern = f.get('pattern') |
| 245 | chk_type = f.get('check_type', 'boot_error') |
| 246 | chk_pattern = re.compile(f.get('check_pattern', 'ERROR')) |
| 247 | |
| 248 | if not addr: |
| 249 | pxe_boot_cmd = 'pxe boot' |
| 250 | else: |
| 251 | pxe_boot_cmd = 'pxe boot %x' % addr |
| 252 | |
| 253 | with u_boot_console.enable_check( |
| 254 | chk_type, chk_pattern |
| 255 | ), u_boot_console.temporary_timeout(timeout): |
| 256 | try: |
| 257 | u_boot_console.run_command(pxe_boot_cmd, wait_for_prompt=False) |
| 258 | u_boot_console.wait_for(pattern) |
| 259 | finally: |
| 260 | u_boot_console.drain_console() |
| 261 | u_boot_console.cleanup_spawn() |
| 262 | |
Love Kumar | b2835c5 | 2024-06-05 15:19:35 +0530 | [diff] [blame] | 263 | @pytest.mark.buildconfigspec('cmd_pxe') |
| 264 | def test_net_pxe_boot_config(u_boot_console): |
| 265 | """Test the pxe boot command by selecting different combination of labels |
| 266 | |
| 267 | A pxe configuration file is downloaded from the TFTP server and interpreted |
| 268 | to boot the images mentioned in pxe configuration file. |
| 269 | |
| 270 | The details of the file to download are provided by the boardenv_* file; |
| 271 | see the comment at the beginning of this file. |
| 272 | """ |
| 273 | if u_boot_console.config.env.get('env__pxe_boot_test_skip', True): |
| 274 | pytest.skip('PXE boot test is not enabled!') |
| 275 | |
| 276 | f, bootfile = setup_pxe_boot(u_boot_console) |
| 277 | addr = f.get('addr', None) |
| 278 | timeout = f.get('timeout', u_boot_console.p.timeout) |
| 279 | fn = f['fn'] |
| 280 | local_label = f['local_label'] |
| 281 | empty_label = f['empty_label'] |
| 282 | exp_str_local = f['exp_str_local'] |
| 283 | exp_str_empty = f['exp_str_empty'] |
| 284 | |
| 285 | if addr: |
| 286 | u_boot_console.run_command('setenv pxefile_addr_r %x' % addr) |
| 287 | |
| 288 | with u_boot_console.temporary_timeout(timeout): |
| 289 | output = u_boot_console.run_command('pxe get') |
| 290 | |
| 291 | expected_text = 'Bytes transferred = ' |
| 292 | sz = f.get('size', None) |
| 293 | if sz: |
| 294 | expected_text += '%d' % sz |
| 295 | assert 'TIMEOUT' not in output |
| 296 | assert expected_text in output |
| 297 | assert f"Config file '{bootfile}' found" in output |
| 298 | |
| 299 | pattern = f.get('pattern') |
| 300 | chk_type = f.get('check_type', 'boot_error') |
| 301 | chk_pattern = re.compile(f.get('check_pattern', 'ERROR')) |
| 302 | |
| 303 | if not addr: |
| 304 | pxe_boot_cmd = 'pxe boot' |
| 305 | else: |
| 306 | pxe_boot_cmd = 'pxe boot %x' % addr |
| 307 | |
| 308 | with u_boot_console.enable_check( |
| 309 | chk_type, chk_pattern |
| 310 | ), u_boot_console.temporary_timeout(timeout): |
| 311 | try: |
| 312 | u_boot_console.run_command(pxe_boot_cmd, wait_for_prompt=False) |
| 313 | |
| 314 | # pxe config is loaded where multiple labels are there and need to |
| 315 | # select particular label to boot and check for expected string |
| 316 | # In this case, local label is selected and it should look for |
| 317 | # localcmd env variable and if that variable is not defined it |
| 318 | # should not boot it and come out to u-boot prompt |
| 319 | u_boot_console.wait_for('Enter choice:') |
| 320 | u_boot_console.run_command(local_label, wait_for_prompt=False) |
| 321 | expected_str = u_boot_console.p.expect([exp_str_local]) |
| 322 | assert ( |
| 323 | expected_str == 0 |
| 324 | ), f'Expected string: {exp_str_local} did not match!' |
| 325 | |
| 326 | # In this case, empty label is selected and it should look for |
| 327 | # kernel image path and if it is not set it should fail it and load |
| 328 | # default label to boot |
| 329 | u_boot_console.run_command(pxe_boot_cmd, wait_for_prompt=False) |
| 330 | u_boot_console.wait_for('Enter choice:') |
| 331 | u_boot_console.run_command(empty_label, wait_for_prompt=False) |
| 332 | expected_str = u_boot_console.p.expect([exp_str_empty]) |
| 333 | assert ( |
| 334 | expected_str == 0 |
| 335 | ), f'Expected string: {exp_str_empty} did not match!' |
| 336 | |
| 337 | u_boot_console.wait_for(pattern) |
| 338 | finally: |
| 339 | u_boot_console.drain_console() |
| 340 | u_boot_console.cleanup_spawn() |
| 341 | |
Love Kumar | b2835c5 | 2024-06-05 15:19:35 +0530 | [diff] [blame] | 342 | @pytest.mark.buildconfigspec('cmd_pxe') |
| 343 | def test_net_pxe_boot_config_invalid(u_boot_console): |
| 344 | """Test the pxe boot command by selecting invalid label |
| 345 | |
| 346 | A pxe configuration file is downloaded from the TFTP server and interpreted |
| 347 | to boot the images mentioned in pxe configuration file. |
| 348 | |
| 349 | The details of the file to download are provided by the boardenv_* file; |
| 350 | see the comment at the beginning of this file. |
| 351 | """ |
| 352 | if u_boot_console.config.env.get('env__pxe_boot_test_skip', True): |
| 353 | pytest.skip('PXE boot test is not enabled!') |
| 354 | |
| 355 | f, bootfile = setup_pxe_boot(u_boot_console) |
| 356 | addr = f.get('addr', None) |
| 357 | timeout = f.get('timeout', u_boot_console.p.timeout) |
| 358 | fn = f['fn'] |
| 359 | invalid_label = f['invalid_label'] |
| 360 | exp_str_invalid = f['exp_str_invalid'] |
| 361 | |
| 362 | if addr: |
| 363 | u_boot_console.run_command('setenv pxefile_addr_r %x' % addr) |
| 364 | |
| 365 | with u_boot_console.temporary_timeout(timeout): |
| 366 | output = u_boot_console.run_command('pxe get') |
| 367 | |
| 368 | expected_text = 'Bytes transferred = ' |
| 369 | sz = f.get('size', None) |
| 370 | if sz: |
| 371 | expected_text += '%d' % sz |
| 372 | assert 'TIMEOUT' not in output |
| 373 | assert expected_text in output |
| 374 | assert f"Config file '{bootfile}' found" in output |
| 375 | |
| 376 | pattern = f.get('pattern') |
| 377 | if not addr: |
| 378 | pxe_boot_cmd = 'pxe boot' |
| 379 | else: |
| 380 | pxe_boot_cmd = 'pxe boot %x' % addr |
| 381 | |
| 382 | with u_boot_console.temporary_timeout(timeout): |
| 383 | try: |
| 384 | u_boot_console.run_command(pxe_boot_cmd, wait_for_prompt=False) |
| 385 | |
| 386 | # pxe config is loaded where multiple labels are there and need to |
| 387 | # select particular label to boot and check for expected string |
| 388 | # In this case invalid label is selected, it should load invalid |
| 389 | # label and if it fails it should load the default label to boot |
| 390 | u_boot_console.wait_for('Enter choice:') |
| 391 | u_boot_console.run_command(invalid_label, wait_for_prompt=False) |
| 392 | expected_str = u_boot_console.p.expect([exp_str_invalid]) |
| 393 | assert ( |
| 394 | expected_str == 0 |
| 395 | ), f'Expected string: {exp_str_invalid} did not match!' |
| 396 | |
| 397 | u_boot_console.wait_for(pattern) |
| 398 | finally: |
| 399 | u_boot_console.drain_console() |
| 400 | u_boot_console.cleanup_spawn() |