Stephen Warren | 770fe17 | 2016-02-08 14:44:16 -0700 | [diff] [blame] | 1 | # SPDX-License-Identifier: GPL-2.0 |
Tom Rini | 10e4779 | 2018-05-06 17:58:06 -0400 | [diff] [blame] | 2 | # Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. |
Stephen Warren | 770fe17 | 2016-02-08 14:44:16 -0700 | [diff] [blame] | 3 | |
Simon Glass | d2bc33ed | 2023-01-06 08:52:41 -0600 | [diff] [blame] | 4 | import getpass |
Simon Glass | d3203bd | 2022-04-24 23:31:25 -0600 | [diff] [blame] | 5 | import gzip |
| 6 | import os |
Stephen Warren | 770fe17 | 2016-02-08 14:44:16 -0700 | [diff] [blame] | 7 | import os.path |
| 8 | import pytest |
| 9 | |
Simon Glass | d3203bd | 2022-04-24 23:31:25 -0600 | [diff] [blame] | 10 | import u_boot_utils |
Simon Glass | 2c7b0e4 | 2022-10-29 19:47:19 -0600 | [diff] [blame] | 11 | from tests import fs_helper |
Simon Glass | d3203bd | 2022-04-24 23:31:25 -0600 | [diff] [blame] | 12 | |
| 13 | def mkdir_cond(dirname): |
| 14 | """Create a directory if it doesn't already exist |
| 15 | |
| 16 | Args: |
Simon Glass | d2bc33ed | 2023-01-06 08:52:41 -0600 | [diff] [blame] | 17 | dirname (str): Name of directory to create |
Simon Glass | d3203bd | 2022-04-24 23:31:25 -0600 | [diff] [blame] | 18 | """ |
| 19 | if not os.path.exists(dirname): |
| 20 | os.mkdir(dirname) |
| 21 | |
Simon Glass | f5e2df0 | 2023-01-17 10:47:41 -0700 | [diff] [blame] | 22 | def setup_image(cons, mmc_dev, part_type, second_part=False): |
Simon Glass | d2bc33ed | 2023-01-06 08:52:41 -0600 | [diff] [blame] | 23 | """Create a 20MB disk image with a single partition |
| 24 | |
| 25 | Args: |
| 26 | cons (ConsoleBase): Console to use |
| 27 | mmc_dev (int): MMC device number to use, e.g. 1 |
| 28 | part_type (int): Partition type, e.g. 0xc for FAT32 |
Simon Glass | f5e2df0 | 2023-01-17 10:47:41 -0700 | [diff] [blame] | 29 | second_part (bool): True to contain a small second partition |
Simon Glass | d2bc33ed | 2023-01-06 08:52:41 -0600 | [diff] [blame] | 30 | |
| 31 | Returns: |
| 32 | tuple: |
| 33 | str: Filename of MMC image |
| 34 | str: Directory name of 'mnt' directory |
| 35 | """ |
| 36 | fname = os.path.join(cons.config.source_dir, f'mmc{mmc_dev}.img') |
Simon Glass | d3203bd | 2022-04-24 23:31:25 -0600 | [diff] [blame] | 37 | mnt = os.path.join(cons.config.persistent_data_dir, 'mnt') |
| 38 | mkdir_cond(mnt) |
| 39 | |
Simon Glass | f5e2df0 | 2023-01-17 10:47:41 -0700 | [diff] [blame] | 40 | spec = f'type={part_type:x}, size=18M, bootable' |
| 41 | if second_part: |
| 42 | spec += '\ntype=c' |
| 43 | |
Simon Glass | d3203bd | 2022-04-24 23:31:25 -0600 | [diff] [blame] | 44 | u_boot_utils.run_and_log(cons, 'qemu-img create %s 20M' % fname) |
| 45 | u_boot_utils.run_and_log(cons, 'sudo sfdisk %s' % fname, |
Simon Glass | f5e2df0 | 2023-01-17 10:47:41 -0700 | [diff] [blame] | 46 | stdin=spec.encode('utf-8')) |
Simon Glass | d2bc33ed | 2023-01-06 08:52:41 -0600 | [diff] [blame] | 47 | return fname, mnt |
| 48 | |
| 49 | def mount_image(cons, fname, mnt, fstype): |
| 50 | """Create a filesystem and mount it on partition 1 |
| 51 | |
| 52 | Args: |
| 53 | cons (ConsoleBase): Console to use |
| 54 | fname (str): Filename of MMC image |
| 55 | mnt (str): Directory name of 'mnt' directory |
| 56 | fstype (str): Filesystem type ('vfat' or 'ext4') |
| 57 | |
| 58 | Returns: |
| 59 | str: Name of loop device used |
| 60 | """ |
| 61 | out = u_boot_utils.run_and_log(cons, 'sudo losetup --show -f -P %s' % fname) |
| 62 | loop = out.strip() |
| 63 | part = f'{loop}p1' |
| 64 | u_boot_utils.run_and_log(cons, f'sudo mkfs.{fstype} {part}') |
| 65 | opts = '' |
| 66 | if fstype == 'vfat': |
Simon Glass | f5e2df0 | 2023-01-17 10:47:41 -0700 | [diff] [blame] | 67 | opts += f' -o uid={os.getuid()},gid={os.getgid()}' |
Simon Glass | d2bc33ed | 2023-01-06 08:52:41 -0600 | [diff] [blame] | 68 | u_boot_utils.run_and_log(cons, f'sudo mount -o loop {part} {mnt}{opts}') |
| 69 | u_boot_utils.run_and_log(cons, f'sudo chown {getpass.getuser()} {mnt}') |
| 70 | return loop |
| 71 | |
| 72 | def copy_prepared_image(cons, mmc_dev, fname): |
| 73 | """Use a prepared image since we cannot create one |
| 74 | |
| 75 | Args: |
| 76 | cons (ConsoleBase): Console touse |
| 77 | mmc_dev (int): MMC device number |
| 78 | fname (str): Filename of MMC image |
| 79 | """ |
| 80 | infname = os.path.join(cons.config.source_dir, |
| 81 | f'test/py/tests/bootstd/mmc{mmc_dev}.img.xz') |
| 82 | u_boot_utils.run_and_log( |
| 83 | cons, |
| 84 | ['sh', '-c', 'xz -dc %s >%s' % (infname, fname)]) |
| 85 | |
| 86 | def setup_bootmenu_image(cons): |
| 87 | """Create a 20MB disk image with a single ext4 partition |
| 88 | |
| 89 | This is modelled on Armbian 22.08 Jammy |
| 90 | """ |
| 91 | mmc_dev = 4 |
| 92 | fname, mnt = setup_image(cons, mmc_dev, 0x83) |
Simon Glass | d3203bd | 2022-04-24 23:31:25 -0600 | [diff] [blame] | 93 | |
| 94 | loop = None |
| 95 | mounted = False |
| 96 | complete = False |
| 97 | try: |
Simon Glass | d2bc33ed | 2023-01-06 08:52:41 -0600 | [diff] [blame] | 98 | loop = mount_image(cons, fname, mnt, 'ext4') |
| 99 | mounted = True |
| 100 | |
| 101 | vmlinux = 'Image' |
| 102 | initrd = 'uInitrd' |
| 103 | dtbdir = 'dtb' |
| 104 | script = '''# DO NOT EDIT THIS FILE |
| 105 | # |
| 106 | # Please edit /boot/armbianEnv.txt to set supported parameters |
| 107 | # |
| 108 | |
| 109 | setenv load_addr "0x9000000" |
| 110 | setenv overlay_error "false" |
| 111 | # default values |
| 112 | setenv rootdev "/dev/mmcblk%dp1" |
| 113 | setenv verbosity "1" |
| 114 | setenv console "both" |
| 115 | setenv bootlogo "false" |
| 116 | setenv rootfstype "ext4" |
| 117 | setenv docker_optimizations "on" |
| 118 | setenv earlycon "off" |
| 119 | |
| 120 | echo "Boot script loaded from ${devtype} ${devnum}" |
| 121 | |
| 122 | if test -e ${devtype} ${devnum} ${prefix}armbianEnv.txt; then |
| 123 | load ${devtype} ${devnum} ${load_addr} ${prefix}armbianEnv.txt |
| 124 | env import -t ${load_addr} ${filesize} |
| 125 | fi |
| 126 | |
| 127 | if test "${logo}" = "disabled"; then setenv logo "logo.nologo"; fi |
| 128 | |
| 129 | if test "${console}" = "display" || test "${console}" = "both"; then setenv consoleargs "console=tty1"; fi |
| 130 | if test "${console}" = "serial" || test "${console}" = "both"; then setenv consoleargs "console=ttyS2,1500000 ${consoleargs}"; fi |
| 131 | if test "${earlycon}" = "on"; then setenv consoleargs "earlycon ${consoleargs}"; fi |
| 132 | if test "${bootlogo}" = "true"; then setenv consoleargs "bootsplash.bootfile=bootsplash.armbian ${consoleargs}"; fi |
| 133 | |
| 134 | # get PARTUUID of first partition on SD/eMMC the boot script was loaded from |
| 135 | if test "${devtype}" = "mmc"; then part uuid mmc ${devnum}:1 partuuid; fi |
| 136 | |
| 137 | setenv bootargs "root=${rootdev} rootwait rootfstype=${rootfstype} ${consoleargs} consoleblank=0 loglevel=${verbosity} ubootpart=${partuuid} usb-storage.quirks=${usbstoragequirks} ${extraargs} ${extraboardargs}" |
| 138 | |
| 139 | if test "${docker_optimizations}" = "on"; then setenv bootargs "${bootargs} cgroup_enable=cpuset cgroup_memory=1 cgroup_enable=memory swapaccount=1"; fi |
| 140 | |
| 141 | load ${devtype} ${devnum} ${ramdisk_addr_r} ${prefix}uInitrd |
| 142 | load ${devtype} ${devnum} ${kernel_addr_r} ${prefix}Image |
| 143 | |
| 144 | load ${devtype} ${devnum} ${fdt_addr_r} ${prefix}dtb/${fdtfile} |
| 145 | fdt addr ${fdt_addr_r} |
| 146 | fdt resize 65536 |
| 147 | for overlay_file in ${overlays}; do |
| 148 | if load ${devtype} ${devnum} ${load_addr} ${prefix}dtb/rockchip/overlay/${overlay_prefix}-${overlay_file}.dtbo; then |
| 149 | echo "Applying kernel provided DT overlay ${overlay_prefix}-${overlay_file}.dtbo" |
| 150 | fdt apply ${load_addr} || setenv overlay_error "true" |
| 151 | fi |
| 152 | done |
| 153 | for overlay_file in ${user_overlays}; do |
| 154 | if load ${devtype} ${devnum} ${load_addr} ${prefix}overlay-user/${overlay_file}.dtbo; then |
| 155 | echo "Applying user provided DT overlay ${overlay_file}.dtbo" |
| 156 | fdt apply ${load_addr} || setenv overlay_error "true" |
| 157 | fi |
| 158 | done |
| 159 | if test "${overlay_error}" = "true"; then |
| 160 | echo "Error applying DT overlays, restoring original DT" |
| 161 | load ${devtype} ${devnum} ${fdt_addr_r} ${prefix}dtb/${fdtfile} |
| 162 | else |
| 163 | if load ${devtype} ${devnum} ${load_addr} ${prefix}dtb/rockchip/overlay/${overlay_prefix}-fixup.scr; then |
| 164 | echo "Applying kernel provided DT fixup script (${overlay_prefix}-fixup.scr)" |
| 165 | source ${load_addr} |
| 166 | fi |
| 167 | if test -e ${devtype} ${devnum} ${prefix}fixup.scr; then |
| 168 | load ${devtype} ${devnum} ${load_addr} ${prefix}fixup.scr |
| 169 | echo "Applying user provided fixup script (fixup.scr)" |
| 170 | source ${load_addr} |
| 171 | fi |
| 172 | fi |
| 173 | booti ${kernel_addr_r} ${ramdisk_addr_r} ${fdt_addr_r} |
| 174 | |
| 175 | # Recompile with: |
| 176 | # mkimage -C none -A arm -T script -d /boot/boot.cmd /boot/boot.scr |
| 177 | ''' % (mmc_dev) |
| 178 | bootdir = os.path.join(mnt, 'boot') |
| 179 | mkdir_cond(bootdir) |
| 180 | cmd_fname = os.path.join(bootdir, 'boot.cmd') |
| 181 | scr_fname = os.path.join(bootdir, 'boot.scr') |
| 182 | with open(cmd_fname, 'w') as outf: |
| 183 | print(script, file=outf) |
| 184 | |
| 185 | infname = os.path.join(cons.config.source_dir, |
| 186 | 'test/py/tests/bootstd/armbian.bmp.xz') |
| 187 | bmp_file = os.path.join(bootdir, 'boot.bmp') |
| 188 | u_boot_utils.run_and_log( |
| 189 | cons, |
| 190 | ['sh', '-c', f'xz -dc {infname} >{bmp_file}']) |
| 191 | |
| 192 | u_boot_utils.run_and_log( |
| 193 | cons, f'mkimage -C none -A arm -T script -d {cmd_fname} {scr_fname}') |
| 194 | |
| 195 | kernel = 'vmlinuz-5.15.63-rockchip64' |
| 196 | target = os.path.join(bootdir, kernel) |
| 197 | with open(target, 'wb') as outf: |
| 198 | print('kernel', outf) |
| 199 | |
| 200 | symlink = os.path.join(bootdir, 'Image') |
| 201 | if os.path.exists(symlink): |
| 202 | os.remove(symlink) |
| 203 | u_boot_utils.run_and_log( |
| 204 | cons, f'echo here {kernel} {symlink}') |
| 205 | os.symlink(kernel, symlink) |
| 206 | |
Simon Glass | d3203bd | 2022-04-24 23:31:25 -0600 | [diff] [blame] | 207 | u_boot_utils.run_and_log( |
Simon Glass | d2bc33ed | 2023-01-06 08:52:41 -0600 | [diff] [blame] | 208 | cons, f'mkimage -C none -A arm -T script -d {cmd_fname} {scr_fname}') |
| 209 | complete = True |
| 210 | |
| 211 | except ValueError as exc: |
| 212 | print('Falled to create image, failing back to prepared copy: %s', |
| 213 | str(exc)) |
| 214 | finally: |
| 215 | if mounted: |
Tom Rini | 52520d7 | 2023-04-05 22:19:39 -0400 | [diff] [blame] | 216 | u_boot_utils.run_and_log(cons, 'sudo umount --lazy %s' % mnt) |
Simon Glass | d2bc33ed | 2023-01-06 08:52:41 -0600 | [diff] [blame] | 217 | if loop: |
| 218 | u_boot_utils.run_and_log(cons, 'sudo losetup -d %s' % loop) |
| 219 | |
| 220 | if not complete: |
| 221 | copy_prepared_image(cons, mmc_dev, fname) |
| 222 | |
| 223 | def setup_bootflow_image(cons): |
| 224 | """Create a 20MB disk image with a single FAT partition""" |
| 225 | mmc_dev = 1 |
Simon Glass | f5e2df0 | 2023-01-17 10:47:41 -0700 | [diff] [blame] | 226 | fname, mnt = setup_image(cons, mmc_dev, 0xc, second_part=True) |
Simon Glass | d2bc33ed | 2023-01-06 08:52:41 -0600 | [diff] [blame] | 227 | |
| 228 | loop = None |
| 229 | mounted = False |
| 230 | complete = False |
| 231 | try: |
| 232 | loop = mount_image(cons, fname, mnt, 'vfat') |
Simon Glass | d3203bd | 2022-04-24 23:31:25 -0600 | [diff] [blame] | 233 | mounted = True |
| 234 | |
| 235 | vmlinux = 'vmlinuz-5.3.7-301.fc31.armv7hl' |
| 236 | initrd = 'initramfs-5.3.7-301.fc31.armv7hl.img' |
| 237 | dtbdir = 'dtb-5.3.7-301.fc31.armv7hl' |
| 238 | script = '''# extlinux.conf generated by appliance-creator |
| 239 | ui menu.c32 |
| 240 | menu autoboot Welcome to Fedora-Workstation-armhfp-31-1.9. Automatic boot in # second{,s}. Press a key for options. |
| 241 | menu title Fedora-Workstation-armhfp-31-1.9 Boot Options. |
| 242 | menu hidden |
| 243 | timeout 20 |
| 244 | totaltimeout 600 |
| 245 | |
| 246 | label Fedora-Workstation-armhfp-31-1.9 (5.3.7-301.fc31.armv7hl) |
| 247 | kernel /%s |
| 248 | append ro root=UUID=9732b35b-4cd5-458b-9b91-80f7047e0b8a rhgb quiet LANG=en_US.UTF-8 cma=192MB cma=256MB |
| 249 | fdtdir /%s/ |
| 250 | initrd /%s''' % (vmlinux, dtbdir, initrd) |
| 251 | ext = os.path.join(mnt, 'extlinux') |
| 252 | mkdir_cond(ext) |
| 253 | |
| 254 | with open(os.path.join(ext, 'extlinux.conf'), 'w') as fd: |
| 255 | print(script, file=fd) |
| 256 | |
| 257 | inf = os.path.join(cons.config.persistent_data_dir, 'inf') |
| 258 | with open(inf, 'wb') as fd: |
| 259 | fd.write(gzip.compress(b'vmlinux')) |
| 260 | u_boot_utils.run_and_log(cons, 'mkimage -f auto -d %s %s' % |
| 261 | (inf, os.path.join(mnt, vmlinux))) |
| 262 | |
| 263 | with open(os.path.join(mnt, initrd), 'w') as fd: |
| 264 | print('initrd', file=fd) |
| 265 | |
| 266 | mkdir_cond(os.path.join(mnt, dtbdir)) |
| 267 | |
| 268 | dtb_file = os.path.join(mnt, '%s/sandbox.dtb' % dtbdir) |
| 269 | u_boot_utils.run_and_log( |
| 270 | cons, 'dtc -o %s' % dtb_file, stdin=b'/dts-v1/; / {};') |
| 271 | complete = True |
| 272 | except ValueError as exc: |
| 273 | print('Falled to create image, failing back to prepared copy: %s', |
| 274 | str(exc)) |
| 275 | finally: |
| 276 | if mounted: |
Tom Rini | 52520d7 | 2023-04-05 22:19:39 -0400 | [diff] [blame] | 277 | u_boot_utils.run_and_log(cons, 'sudo umount --lazy %s' % mnt) |
Simon Glass | d3203bd | 2022-04-24 23:31:25 -0600 | [diff] [blame] | 278 | if loop: |
| 279 | u_boot_utils.run_and_log(cons, 'sudo losetup -d %s' % loop) |
| 280 | |
| 281 | if not complete: |
Simon Glass | d2bc33ed | 2023-01-06 08:52:41 -0600 | [diff] [blame] | 282 | copy_prepared_image(cons, mmc_dev, fname) |
Simon Glass | d3203bd | 2022-04-24 23:31:25 -0600 | [diff] [blame] | 283 | |
| 284 | |
Stephen Warren | 770fe17 | 2016-02-08 14:44:16 -0700 | [diff] [blame] | 285 | @pytest.mark.buildconfigspec('ut_dm') |
| 286 | def test_ut_dm_init(u_boot_console): |
| 287 | """Initialize data for ut dm tests.""" |
| 288 | |
| 289 | fn = u_boot_console.config.source_dir + '/testflash.bin' |
| 290 | if not os.path.exists(fn): |
Tom Rini | 439ed3e | 2019-10-24 11:59:22 -0400 | [diff] [blame] | 291 | data = b'this is a test' |
| 292 | data += b'\x00' * ((4 * 1024 * 1024) - len(data)) |
Stephen Warren | 770fe17 | 2016-02-08 14:44:16 -0700 | [diff] [blame] | 293 | with open(fn, 'wb') as fh: |
| 294 | fh.write(data) |
| 295 | |
| 296 | fn = u_boot_console.config.source_dir + '/spi.bin' |
| 297 | if not os.path.exists(fn): |
Tom Rini | 439ed3e | 2019-10-24 11:59:22 -0400 | [diff] [blame] | 298 | data = b'\x00' * (2 * 1024 * 1024) |
Stephen Warren | 770fe17 | 2016-02-08 14:44:16 -0700 | [diff] [blame] | 299 | with open(fn, 'wb') as fh: |
| 300 | fh.write(data) |
| 301 | |
Simon Glass | 509f32e | 2022-09-21 16:21:47 +0200 | [diff] [blame] | 302 | # Create a file with a single partition |
| 303 | fn = u_boot_console.config.source_dir + '/scsi.img' |
| 304 | if not os.path.exists(fn): |
| 305 | data = b'\x00' * (2 * 1024 * 1024) |
| 306 | with open(fn, 'wb') as fh: |
| 307 | fh.write(data) |
| 308 | u_boot_utils.run_and_log( |
| 309 | u_boot_console, f'sfdisk {fn}', stdin=b'type=83') |
| 310 | |
Simon Glass | 2c7b0e4 | 2022-10-29 19:47:19 -0600 | [diff] [blame] | 311 | fs_helper.mk_fs(u_boot_console.config, 'ext2', 0x200000, '2MB', |
| 312 | use_src_dir=True) |
| 313 | fs_helper.mk_fs(u_boot_console.config, 'fat32', 0x100000, '1MB', |
| 314 | use_src_dir=True) |
| 315 | |
Simon Glass | d3203bd | 2022-04-24 23:31:25 -0600 | [diff] [blame] | 316 | @pytest.mark.buildconfigspec('cmd_bootflow') |
| 317 | def test_ut_dm_init_bootstd(u_boot_console): |
| 318 | """Initialise data for bootflow tests""" |
| 319 | |
| 320 | setup_bootflow_image(u_boot_console) |
Simon Glass | d2bc33ed | 2023-01-06 08:52:41 -0600 | [diff] [blame] | 321 | setup_bootmenu_image(u_boot_console) |
Simon Glass | d3203bd | 2022-04-24 23:31:25 -0600 | [diff] [blame] | 322 | |
| 323 | # Restart so that the new mmc1.img is picked up |
| 324 | u_boot_console.restart_uboot() |
| 325 | |
| 326 | |
Stephen Warren | 770fe17 | 2016-02-08 14:44:16 -0700 | [diff] [blame] | 327 | def test_ut(u_boot_console, ut_subtest): |
Heinrich Schuchardt | df6c36c | 2020-05-06 18:26:07 +0200 | [diff] [blame] | 328 | """Execute a "ut" subtest. |
| 329 | |
| 330 | The subtests are collected in function generate_ut_subtest() from linker |
| 331 | generated lists by applying a regular expression to the lines of file |
| 332 | u-boot.sym. The list entries are created using the C macro UNIT_TEST(). |
| 333 | |
| 334 | Strict naming conventions have to be followed to match the regular |
| 335 | expression. Use UNIT_TEST(foo_test_bar, _flags, foo_test) for a test bar in |
| 336 | test suite foo that can be executed via command 'ut foo bar' and is |
| 337 | implemented in C function foo_test_bar(). |
| 338 | |
| 339 | Args: |
| 340 | u_boot_console (ConsoleBase): U-Boot console |
| 341 | ut_subtest (str): test to be executed via command ut, e.g 'foo bar' to |
| 342 | execute command 'ut foo bar' |
| 343 | """ |
Stephen Warren | 770fe17 | 2016-02-08 14:44:16 -0700 | [diff] [blame] | 344 | |
| 345 | output = u_boot_console.run_command('ut ' + ut_subtest) |
| 346 | assert output.endswith('Failures: 0') |