1# SPDX-License-Identifier: GPL-2.0 2# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. 3 4import getpass 5import gzip 6import os 7import os.path 8import pytest 9 10import u_boot_utils 11from tests import fs_helper 12 13def mkdir_cond(dirname): 14 """Create a directory if it doesn't already exist 15 16 Args: 17 dirname (str): Name of directory to create 18 """ 19 if not os.path.exists(dirname): 20 os.mkdir(dirname) 21 22def setup_image(cons, mmc_dev, part_type, second_part=False): 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 29 second_part (bool): True to contain a small second partition 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') 37 mnt = os.path.join(cons.config.persistent_data_dir, 'mnt') 38 mkdir_cond(mnt) 39 40 spec = f'type={part_type:x}, size=18M, bootable' 41 if second_part: 42 spec += '\ntype=c' 43 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, 46 stdin=spec.encode('utf-8')) 47 return fname, mnt 48 49def 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': 67 opts += f' -o uid={os.getuid()},gid={os.getgid()}' 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 72def 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 86def 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) 93 94 loop = None 95 mounted = False 96 complete = False 97 try: 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 109setenv load_addr "0x9000000" 110setenv overlay_error "false" 111# default values 112setenv rootdev "/dev/mmcblk%dp1" 113setenv verbosity "1" 114setenv console "both" 115setenv bootlogo "false" 116setenv rootfstype "ext4" 117setenv docker_optimizations "on" 118setenv earlycon "off" 119 120echo "Boot script loaded from ${devtype} ${devnum}" 121 122if test -e ${devtype} ${devnum} ${prefix}armbianEnv.txt; then 123 load ${devtype} ${devnum} ${load_addr} ${prefix}armbianEnv.txt 124 env import -t ${load_addr} ${filesize} 125fi 126 127if test "${logo}" = "disabled"; then setenv logo "logo.nologo"; fi 128 129if test "${console}" = "display" || test "${console}" = "both"; then setenv consoleargs "console=tty1"; fi 130if test "${console}" = "serial" || test "${console}" = "both"; then setenv consoleargs "console=ttyS2,1500000 ${consoleargs}"; fi 131if test "${earlycon}" = "on"; then setenv consoleargs "earlycon ${consoleargs}"; fi 132if 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 135if test "${devtype}" = "mmc"; then part uuid mmc ${devnum}:1 partuuid; fi 136 137setenv bootargs "root=${rootdev} rootwait rootfstype=${rootfstype} ${consoleargs} consoleblank=0 loglevel=${verbosity} ubootpart=${partuuid} usb-storage.quirks=${usbstoragequirks} ${extraargs} ${extraboardargs}" 138 139if test "${docker_optimizations}" = "on"; then setenv bootargs "${bootargs} cgroup_enable=cpuset cgroup_memory=1 cgroup_enable=memory swapaccount=1"; fi 140 141load ${devtype} ${devnum} ${ramdisk_addr_r} ${prefix}uInitrd 142load ${devtype} ${devnum} ${kernel_addr_r} ${prefix}Image 143 144load ${devtype} ${devnum} ${fdt_addr_r} ${prefix}dtb/${fdtfile} 145fdt addr ${fdt_addr_r} 146fdt resize 65536 147for 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 152done 153for 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 158done 159if test "${overlay_error}" = "true"; then 160 echo "Error applying DT overlays, restoring original DT" 161 load ${devtype} ${devnum} ${fdt_addr_r} ${prefix}dtb/${fdtfile} 162else 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 172fi 173booti ${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 207 u_boot_utils.run_and_log( 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: 216 u_boot_utils.run_and_log(cons, 'sudo umount --lazy %s' % mnt) 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 223def setup_bootflow_image(cons): 224 """Create a 20MB disk image with a single FAT partition""" 225 mmc_dev = 1 226 fname, mnt = setup_image(cons, mmc_dev, 0xc, second_part=True) 227 228 loop = None 229 mounted = False 230 complete = False 231 try: 232 loop = mount_image(cons, fname, mnt, 'vfat') 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 239ui menu.c32 240menu autoboot Welcome to Fedora-Workstation-armhfp-31-1.9. Automatic boot in # second{,s}. Press a key for options. 241menu title Fedora-Workstation-armhfp-31-1.9 Boot Options. 242menu hidden 243timeout 20 244totaltimeout 600 245 246label 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: 277 u_boot_utils.run_and_log(cons, 'sudo umount --lazy %s' % mnt) 278 if loop: 279 u_boot_utils.run_and_log(cons, 'sudo losetup -d %s' % loop) 280 281 if not complete: 282 copy_prepared_image(cons, mmc_dev, fname) 283 284 285@pytest.mark.buildconfigspec('ut_dm') 286def 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): 291 data = b'this is a test' 292 data += b'\x00' * ((4 * 1024 * 1024) - len(data)) 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): 298 data = b'\x00' * (2 * 1024 * 1024) 299 with open(fn, 'wb') as fh: 300 fh.write(data) 301 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 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 316@pytest.mark.buildconfigspec('cmd_bootflow') 317def test_ut_dm_init_bootstd(u_boot_console): 318 """Initialise data for bootflow tests""" 319 320 setup_bootflow_image(u_boot_console) 321 setup_bootmenu_image(u_boot_console) 322 323 # Restart so that the new mmc1.img is picked up 324 u_boot_console.restart_uboot() 325 326 327def test_ut(u_boot_console, ut_subtest): 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 """ 344 345 output = u_boot_console.run_command('ut ' + ut_subtest) 346 assert output.endswith('Failures: 0') 347