diff options
Diffstat (limited to 'test')
56 files changed, 2673 insertions, 107 deletions
diff --git a/test/Containerfile b/test/Containerfile new file mode 100644 index 0000000000..fe3e3a6186 --- /dev/null +++ b/test/Containerfile @@ -0,0 +1,83 @@ +# SPDX-License-Identifier: GPL-2.0+ +# This Containerfile is used to build an image containing basic stuff to be used +# to build barebox and run our test suites. + +FROM debian:bullseye +MAINTAINER Sascha Hauer <s.hauer@pengutronix.de> +LABEL Description="This image is for building and testing barebox inside a container" + +# Make sure apt is happy +ENV DEBIAN_FRONTEND=noninteractive + +# Update and install things from apt now +RUN apt-get update && apt-get install -y \ + bison \ + build-essential \ + coreutils \ + flex \ + gawk \ + git \ + imagemagick \ + libc6-i386 \ + libgit2-dev \ + libguestfs-tools \ + liblz4-tool \ + libseccomp-dev \ + libssl-dev \ + libtool \ + libudev-dev \ + libusb-1.0-0-dev \ + lzma-alone \ + lzop \ + mount \ + openssl \ + pkg-config \ + util-linux \ + wget \ + qemu-system-arm \ + qemu-system-misc \ + qemu-system-mips \ + qemu-system-x86 \ + qemu-system-common \ + ovmf \ + python3 \ + python3-pip \ + python3-virtualenv \ + python3-setuptools \ + virtualenv \ + sudo \ + u-boot-tools \ + && rm -rf /var/lib/apt/lists/* + +ENV GCC_VERSION=13.1.0 + +# Manually install the kernel.org Crosstool based toolchains +RUN korg_crosstool_dl() { wget -nv -O - https://mirrors.edge.kernel.org/pub/tools/crosstool/files/bin/${1}/${2}/${1}-gcc-${2}-nolibc-${3}.tar.xz | tar -C /opt -xJ ; } && \ + korg_crosstool_dl x86_64 ${GCC_VERSION} arm-linux-gnueabi && \ + korg_crosstool_dl x86_64 ${GCC_VERSION} aarch64-linux && \ + korg_crosstool_dl x86_64 ${GCC_VERSION} mips-linux && \ + korg_crosstool_dl x86_64 ${GCC_VERSION} or1k-linux && \ + korg_crosstool_dl x86_64 ${GCC_VERSION} powerpc-linux && \ + korg_crosstool_dl x86_64 ${GCC_VERSION} riscv64-linux + +RUN wget "https://github.com/qemu/qemu/blob/v5.2.0/pc-bios/opensbi-riscv32-generic-fw_dynamic.bin?raw=true" -O /usr/share/qemu/opensbi-riscv32-generic-fw_dynamic.bin + +# Create our user/group +RUN useradd -m -U barebox +RUN echo barebox ALL=NOPASSWD: ALL > /etc/sudoers.d/barebox + +# install labgrid +RUN cd /tmp && \ + git clone --depth 1 -b v23.0 https://github.com/labgrid-project/labgrid && \ + cd labgrid && \ + pip3 install . && \ + ln -s $(which pytest) /usr/local/bin/labgrid-pytest; + +ENV CROSS_COMPILE_arm=/opt/gcc-${GCC_VERSION}-nolibc/arm-linux-gnueabi/bin/arm-linux-gnueabi- +ENV CROSS_COMPILE_arm64=/opt/gcc-${GCC_VERSION}-nolibc/aarch64-linux/bin/aarch64-linux- +ENV CROSS_COMPILE_mips=/opt/gcc-${GCC_VERSION}-nolibc/mips-linux/bin/mips-linux- +ENV CROSS_COMPILE_openrisc=/opt/gcc-${GCC_VERSION}-nolibc/or1k-linux/bin/or1k-linux- +ENV CROSS_COMPILE_powerpc=/opt/gcc-${GCC_VERSION}-nolibc/powerpc-linux/bin/powerpc-linux- +ENV CROSS_COMPILE_riscv=/opt/gcc-${GCC_VERSION}-nolibc/riscv64-linux/bin/riscv64-linux- + +USER barebox:barebox diff --git a/test/arm/a15@vexpress_defconfig.yaml b/test/arm/a15@multi_v7_defconfig.yaml index 941e914ab2..dfa73fb3f5 100644 --- a/test/arm/a15@vexpress_defconfig.yaml +++ b/test/arm/a15@multi_v7_defconfig.yaml @@ -2,7 +2,7 @@ targets: main: drivers: QEMUDriver: - qemu_bin: qemu + qemu_bin: qemu-system-arm machine: vexpress-a15 cpu: cortex-a15 memory: 1024M @@ -14,7 +14,5 @@ targets: BareboxTestStrategy: {} images: barebox-vexpress-ca15.img: !template "$LG_BUILDDIR/images/barebox-vexpress-ca15.img" -tools: - qemu: /usr/bin/qemu-system-arm imports: - ../strategy.py diff --git a/test/arm/a9@vexpress_defconfig.yaml b/test/arm/a9@multi_v7_defconfig.yaml index fefee153cf..8e8220102c 100644 --- a/test/arm/a9@vexpress_defconfig.yaml +++ b/test/arm/a9@multi_v7_defconfig.yaml @@ -2,7 +2,7 @@ targets: main: drivers: QEMUDriver: - qemu_bin: qemu + qemu_bin: qemu-system-arm machine: vexpress-a9 cpu: cortex-a9 memory: 1024M @@ -14,7 +14,5 @@ targets: BareboxTestStrategy: {} images: barebox-vexpress-ca9.img: !template "$LG_BUILDDIR/images/barebox-vexpress-ca9.img" -tools: - qemu: /usr/bin/qemu-system-arm imports: - ../strategy.py diff --git a/test/arm/multi_v7_defconfig.yaml b/test/arm/multi_v7_defconfig.yaml new file mode 120000 index 0000000000..ac18c8272c --- /dev/null +++ b/test/arm/multi_v7_defconfig.yaml @@ -0,0 +1 @@ +a9@multi_v7_defconfig.yaml
\ No newline at end of file diff --git a/test/arm/multi_v8_defconfig.yaml b/test/arm/multi_v8_defconfig.yaml new file mode 120000 index 0000000000..cbf4d40e3f --- /dev/null +++ b/test/arm/multi_v8_defconfig.yaml @@ -0,0 +1 @@ +virt@multi_v8_defconfig.yaml
\ No newline at end of file diff --git a/test/arm/qemu-raspi0@multi_v7_defconfig.yaml b/test/arm/qemu-raspi0@multi_v7_defconfig.yaml new file mode 100644 index 0000000000..0a9a727c64 --- /dev/null +++ b/test/arm/qemu-raspi0@multi_v7_defconfig.yaml @@ -0,0 +1,18 @@ +targets: + main: + drivers: + QEMUDriver: + qemu_bin: qemu-system-arm + machine: raspi0 + cpu: arm1176 + memory: 512M + kernel: barebox-raspberry-pi.img + extra_args: '' + BareboxDriver: + prompt: 'barebox@[^:]+:[^ ]+ ' + bootstring: 'commandline:' + BareboxTestStrategy: {} +images: + barebox-raspberry-pi.img: !template "$LG_BUILDDIR/images/barebox-raspberry-pi.img" +imports: + - ../strategy.py diff --git a/test/arm/qemu-raspi1ap@multi_v7_defconfig.yaml b/test/arm/qemu-raspi1ap@multi_v7_defconfig.yaml new file mode 100644 index 0000000000..0950481a63 --- /dev/null +++ b/test/arm/qemu-raspi1ap@multi_v7_defconfig.yaml @@ -0,0 +1,18 @@ +targets: + main: + drivers: + QEMUDriver: + qemu_bin: qemu-system-arm + machine: raspi1ap + cpu: arm1176 + memory: 512M + kernel: barebox-raspberry-pi.img + extra_args: '' + BareboxDriver: + prompt: 'barebox@[^:]+:[^ ]+ ' + bootstring: 'commandline:' + BareboxTestStrategy: {} +images: + barebox-raspberry-pi.img: !template "$LG_BUILDDIR/images/barebox-raspberry-pi.img" +imports: + - ../strategy.py diff --git a/test/arm/qemu-raspi2b@multi_v7_defconfig.yaml b/test/arm/qemu-raspi2b@multi_v7_defconfig.yaml new file mode 100644 index 0000000000..72471c0a7f --- /dev/null +++ b/test/arm/qemu-raspi2b@multi_v7_defconfig.yaml @@ -0,0 +1,18 @@ +targets: + main: + drivers: + QEMUDriver: + qemu_bin: qemu-system-arm + machine: raspi2b + cpu: cortex-a7 + memory: 1G + kernel: barebox-raspberry-pi.img + extra_args: '' + BareboxDriver: + prompt: 'barebox@[^:]+:[^ ]+ ' + bootstring: 'commandline:' + BareboxTestStrategy: {} +images: + barebox-raspberry-pi.img: !template "$LG_BUILDDIR/images/barebox-raspberry-pi.img" +imports: + - ../strategy.py diff --git a/test/arm/qemu-sabrelite@multi_v7_defconfig.yaml b/test/arm/qemu-sabrelite@multi_v7_defconfig.yaml new file mode 100644 index 0000000000..77b7f9e589 --- /dev/null +++ b/test/arm/qemu-sabrelite@multi_v7_defconfig.yaml @@ -0,0 +1,20 @@ +targets: + main: + drivers: + QEMUDriver: + qemu_bin: qemu-system-arm + machine: sabrelite + cpu: cortex-a9 + memory: 1024M + kernel: barebox-dt-2nd.img + dtb: imx6q-sabreliste.dtb + extra_args: '-nographic -serial null' + BareboxDriver: + prompt: 'barebox@[^:]+:[^ ]+ ' + bootstring: 'commandline:' + BareboxTestStrategy: {} +images: + barebox-dt-2nd.img: !template "$LG_BUILDDIR/images/barebox-dt-2nd.img" + imx6q-sabreliste.dtb: !template "$LG_BUILDDIR/arch/arm/dts/imx6q-sabrelite.dtb" +imports: + - ../strategy.py diff --git a/test/arm/vexpress_defconfig.yaml b/test/arm/vexpress_defconfig.yaml deleted file mode 120000 index 732f51b19d..0000000000 --- a/test/arm/vexpress_defconfig.yaml +++ /dev/null @@ -1 +0,0 @@ -a9@vexpress_defconfig.yaml
\ No newline at end of file diff --git a/test/arm/virt@vexpress_defconfig.yaml b/test/arm/virt@multi_v7_defconfig.yaml index 66ecf20690..203f17bfc7 100644 --- a/test/arm/virt@vexpress_defconfig.yaml +++ b/test/arm/virt@multi_v7_defconfig.yaml @@ -2,7 +2,7 @@ targets: main: drivers: QEMUDriver: - qemu_bin: qemu + qemu_bin: qemu-system-arm machine: virt cpu: cortex-a7 memory: 1024M @@ -16,7 +16,5 @@ targets: - virtio-mmio images: barebox-dt-2nd.img: !template "$LG_BUILDDIR/images/barebox-dt-2nd.img" -tools: - qemu: /usr/bin/qemu-system-arm imports: - ../strategy.py diff --git a/test/arm/qemu_virt64_defconfig.yaml b/test/arm/virt@multi_v8_defconfig.yaml index 5da5dfeb7e..d8f8ab5cbf 100644 --- a/test/arm/qemu_virt64_defconfig.yaml +++ b/test/arm/virt@multi_v8_defconfig.yaml @@ -2,7 +2,7 @@ targets: main: drivers: QEMUDriver: - qemu_bin: qemu + qemu_bin: qemu-system-aarch64 machine: virt,highmem=off cpu: cortex-a57 memory: 1024M @@ -18,7 +18,5 @@ targets: tuxmake_arch: arm64 images: barebox-dt-2nd.img: !template "$LG_BUILDDIR/images/barebox-dt-2nd.img" -tools: - qemu: /usr/bin/qemu-system-aarch64 imports: - ../strategy.py diff --git a/test/conftest.py b/test/conftest.py index 1a043a91fa..12daf3a058 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -1,5 +1,6 @@ import pytest import os +import argparse from .py import helper @@ -25,3 +26,80 @@ def pytest_configure(config): if os.environ['LG_BUILDDIR'] is not None: os.environ['LG_BUILDDIR'] = os.path.realpath(os.environ['LG_BUILDDIR']) + +def pytest_addoption(parser): + parser.addoption('--interactive', action='store_const', const='qemu_interactive', + dest='lg_initial_state', + help=('(for debugging) skip tests and just start Qemu interactively')) + parser.addoption('--dry-run', action='store_const', const='qemu_dry_run', + dest='lg_initial_state', + help=('(for debugging) skip tests and just print Qemu command line')) + parser.addoption('--rng', action='count', dest='qemu_rng', + help=('instantiate Virt I/O random number generator')) + parser.addoption('--console', action='count', dest='qemu_console', default=0, + help=('Pass an extra console (Virt I/O or ns16550_pci) to emulated barebox')) + parser.addoption('--blk', action='append', dest='qemu_block', + default=[], metavar="FILE", + help=('Pass block device to emulated barebox. Can be specified more than once')) + parser.addoption('--qemu', dest='qemu_arg', nargs=argparse.REMAINDER, default=[], + help=('Pass all remaining options to QEMU as is')) + +@pytest.fixture(scope="session") +def strategy(request, target, pytestconfig): + try: + strategy = target.get_driver("Strategy") + except NoDriverFoundError as e: + pytest.exit(e) + + try: + features = target.env.config.data["targets"]["main"]["features"] + except KeyError: + features = [] + + virtio = None + + if "virtio-mmio" in features: + virtio = "device" + if "virtio-pci" in features: + virtio = "pci,disable-modern=off" + features.append("pci") + + if virtio and pytestconfig.option.qemu_rng: + for i in range(pytestconfig.option.qemu_rng): + strategy.append_qemu_args("-device", f"virtio-rng-{virtio}") + + for i in range(pytestconfig.option.qemu_console): + if virtio and i == 0: + strategy.append_qemu_args( + "-device", f"virtio-serial-{virtio}", + "-chardev", f"pty,id=virtcon{i}", + "-device", f"virtconsole,chardev=virtcon{i},name=console.virtcon{i}" + ) + continue + + # ns16550 serial driver only works with x86 so far + if 'pci' in features: + strategy.append_qemu_args( + "-chardev", f"pty,id=pcicon{i}", + "-device", f"pci-serial,chardev=pcicon{i}" + ) + else: + pytest.exit("barebox currently supports only a single extra virtio console\n", 1) + + for i, blk in enumerate(pytestconfig.option.qemu_block): + if virtio: + strategy.append_qemu_args( + "-drive", f"if=none,format=raw,id=hd{i},file={blk}", + "-device", f"virtio-blk-{virtio},drive=hd{i}" + ) + else: + pytest.exit("--blk unsupported for target\n", 1) + + for arg in pytestconfig.option.qemu_arg: + strategy.append_qemu_args(arg) + + state = request.config.option.lg_initial_state + if state is not None: + strategy.force(state) + + return strategy diff --git a/test/emulate.pl b/test/emulate.pl index 77b751a329..a4ca6b7aee 100755 --- a/test/emulate.pl +++ b/test/emulate.pl @@ -18,6 +18,17 @@ my @QEMU_INTERACTIVE_OPTS = qw(-serial mon:stdio -trace file=/dev/null); my %targets; +my $LG_BUILDDIR; + +if (exists $ENV{KBUILD_OUTPUT}) { + $LG_BUILDDIR = $ENV{KBUILD_OUTPUT}; +} elsif (-d 'build') { + $LG_BUILDDIR = 'build'; +} else { + $LG_BUILDDIR = getcwd(); +} + + for my $arch (glob dirname(__FILE__) . "/*/") { for my $cfg (glob "$arch/*.yaml") { my $linkdest = readlink $cfg // ''; @@ -152,7 +163,7 @@ sub process { or die "Failed to download resource `$v': $?\n"; } - symlink_force("$dir/$k", "$k") unless $tuxmake; + symlink_force("$dir/$k", "$LG_BUILDDIR/$k") unless $tuxmake; } if ($shell) { @@ -253,6 +264,8 @@ sub emulate { } else { die "--blk unsupported for target\n"; } + + $i++; } # note that barebox doesn't yet support multiple virtio consoles @@ -386,20 +399,11 @@ sub rel2abs { sub abs_configpath { my ($path, $args) = @_; - my $LG_BUILDDIR; return unless defined $path; $path = $args->{target}{images}{$path}; return unless defined $path; - if (exists $ENV{KBUILD_OUTPUT}) { - $LG_BUILDDIR = $ENV{KBUILD_OUTPUT}; - } elsif (-d 'build') { - $LG_BUILDDIR = 'build'; - } else { - $LG_BUILDDIR = getcwd(); - } - $path =~ s/\$LG_BUILDDIR\b/$LG_BUILDDIR/g; return rel2abs($path, $args->{builddir}) diff --git a/test/generate-dummy-fw.sh b/test/generate-dummy-fw.sh new file mode 100755 index 0000000000..19bc8a9e7c --- /dev/null +++ b/test/generate-dummy-fw.sh @@ -0,0 +1,66 @@ +#!/bin/sh +# +# Generate dummy firmware files for compile tests +# + +FW_NXP_LPDDR4=" + firmware/lpddr4_pmu_train_1d_dmem.bin + firmware/lpddr4_pmu_train_1d_imem.bin + firmware/lpddr4_pmu_train_2d_dmem.bin + firmware/lpddr4_pmu_train_2d_imem.bin +" + +FW_NXP_DDR4=" + firmware/ddr4_dmem_1d.bin + firmware/ddr4_dmem_2d.bin + firmware/ddr4_imem_1d.bin + firmware/ddr4_imem_2d.bin +" + +FW_BL31=" + firmware/imx8mm-bl31.bin + firmware/imx8mn-bl31.bin + firmware/imx8mp-bl31.bin + firmware/imx8mq-bl31.bin + firmware/rk3568-bl31.bin + firmware/rk3588-bl31.bin + firmware/ls1028a-bl31.bin +" + +FW_ROCKCHIP_SDRAM_INIT=" + arch/arm/boards/pine64-quartz64/sdram-init.bin + arch/arm/boards/radxa-rock3/sdram-init.bin + arch/arm/boards/rockchip-rk3568-bpi-r2pro/sdram-init.bin + arch/arm/boards/rockchip-rk3568-evb/sdram-init.bin + arch/arm/boards/radxa-rock5/sdram-init.bin +" + +FW_MVEBU_BINARY0=" + arch/arm/boards/globalscale-mirabox/binary.0 + arch/arm/boards/lenovo-ix4-300d/binary.0 + arch/arm/boards/marvell-armada-xp-db/binary.0 + arch/arm/boards/marvell-armada-xp-gp/binary.0 + arch/arm/boards/netgear-rn104/binary.0 + arch/arm/boards/netgear-rn2120/binary.0 + arch/arm/boards/plathome-openblocks-ax3/binary.0 + arch/arm/boards/turris-omnia/binary.0 +" + +FW_NXP_LAYERSCAPE=" + firmware/fsl_fman_ucode_ls1046_r1.0_106_4_18.bin + firmware/ppa-ls1046a.bin +" + +FW=" + $FW_NXP_LPDDR4 + $FW_NXP_DDR4 + $FW_BL31 + $FW_ROCKCHIP_SDRAM_INIT + $FW_MVEBU_BINARY0 + $FW_NXP_LAYERSCAPE +" + +for i in $FW; do + mkdir -p $(dirname $i) + echo "Dummy firmware generated for $i" > $i +done diff --git a/test/kconfig/disable_size_check.kconf b/test/kconfig/disable_size_check.kconf new file mode 100644 index 0000000000..3819f8a32c --- /dev/null +++ b/test/kconfig/disable_size_check.kconf @@ -0,0 +1 @@ +CONFIG_BAREBOX_MAX_IMAGE_SIZE=0xffffffff diff --git a/test/kconfig/disable_target_tools.kconf b/test/kconfig/disable_target_tools.kconf new file mode 100644 index 0000000000..55cb99b891 --- /dev/null +++ b/test/kconfig/disable_target_tools.kconf @@ -0,0 +1,5 @@ +# CONFIG_IMD_TARGET is not set +# CONFIG_KERNEL_INSTALL_TARGET is not set +# CONFIG_BAREBOXENV_TARGET is not set +# CONFIG_BAREBOXCRC32_TARGET is not set +# CONFIG_MVEBU_KWBOOT_TARGET is not set diff --git a/test/kconfig/enable_self_test.kconf b/test/kconfig/enable_self_test.kconf new file mode 100644 index 0000000000..f5cd7821b9 --- /dev/null +++ b/test/kconfig/enable_self_test.kconf @@ -0,0 +1,5 @@ +CONFIG_SELFTEST_ENABLE_ALL=y +CONFIG_TEST=y +CONFIG_CMD_SELFTEST=y +CONFIG_SELFTEST=y + diff --git a/test/kconfig/enable_werror.kconf b/test/kconfig/enable_werror.kconf new file mode 100644 index 0000000000..f54ff6cdaa --- /dev/null +++ b/test/kconfig/enable_werror.kconf @@ -0,0 +1 @@ +CONFIG_WERROR=y diff --git a/test/mips/be@qemu-malta_defconfig.yaml b/test/mips/be@qemu-malta_defconfig.yaml index 774023cd84..5f438e4b90 100644 --- a/test/mips/be@qemu-malta_defconfig.yaml +++ b/test/mips/be@qemu-malta_defconfig.yaml @@ -2,7 +2,7 @@ targets: main: drivers: QEMUDriver: - qemu_bin: qemu + qemu_bin: qemu-system-mips machine: malta cpu: M14Kc memory: 256M @@ -16,7 +16,5 @@ targets: - virtio-pci images: barebox-qemu-malta.img: !template "$LG_BUILDDIR/images/barebox-qemu-malta.img" -tools: - qemu: /usr/bin/qemu-system-mips imports: - ../strategy.py diff --git a/test/mips/le@qemu-malta_defconfig.yaml b/test/mips/le@qemu-malta_defconfig.yaml index 8fc8c4dae9..ecf9484dcc 100644 --- a/test/mips/le@qemu-malta_defconfig.yaml +++ b/test/mips/le@qemu-malta_defconfig.yaml @@ -2,7 +2,7 @@ targets: main: drivers: QEMUDriver: - qemu_bin: qemu + qemu_bin: qemu-system-mipsel machine: malta cpu: M14Kc memory: 256M @@ -19,7 +19,5 @@ targets: - CONFIG_CPU_LITTLE_ENDIAN=y images: barebox-qemu-malta.img.swapped: !template "$LG_BUILDDIR/images/barebox-qemu-malta.img.swapped" -tools: - qemu: /usr/bin/qemu-system-mipsel imports: - ../strategy.py diff --git a/test/mips/qemu-malta64el_defconfig.yaml b/test/mips/qemu-malta64el_defconfig.yaml new file mode 100644 index 0000000000..bf8563e662 --- /dev/null +++ b/test/mips/qemu-malta64el_defconfig.yaml @@ -0,0 +1,20 @@ +targets: + main: + drivers: + QEMUDriver: + qemu_bin: qemu-system-mips64el + machine: malta + cpu: 5KEf + memory: 256M + bios: barebox-qemu-malta.img.swapped + extra_args: '' + BareboxDriver: + prompt: 'barebox@[^:]+:[^ ]+ ' + bootstring: 'commandline:' + BareboxTestStrategy: {} + features: + - virtio-pci +images: + barebox-qemu-malta.img.swapped: !template "$LG_BUILDDIR/images/barebox-qemu-malta.img.swapped" +imports: + - ../strategy.py diff --git a/test/openrisc/generic_defconfig.yaml b/test/openrisc/generic_defconfig.yaml index 0a2d4a7a18..93ba9586c4 100644 --- a/test/openrisc/generic_defconfig.yaml +++ b/test/openrisc/generic_defconfig.yaml @@ -2,7 +2,7 @@ targets: main: drivers: QEMUDriver: - qemu_bin: qemu + qemu_bin: qemu-system-or1k machine: or1k-sim cpu: or1200 memory: 256M @@ -14,7 +14,5 @@ targets: BareboxTestStrategy: {} images: barebox: !template "$LG_BUILDDIR/barebox" -tools: - qemu: /usr/bin/qemu-system-or1k imports: - ../strategy.py diff --git a/test/riscv/qemu@virt64_defconfig.yaml b/test/riscv/qemu-virt64@rv64i_defconfig.yaml index fefbd20e5c..c920413a17 100644 --- a/test/riscv/qemu@virt64_defconfig.yaml +++ b/test/riscv/qemu-virt64@rv64i_defconfig.yaml @@ -2,12 +2,11 @@ targets: main: drivers: QEMUDriver: - qemu_bin: qemu + qemu_bin: qemu-system-riscv64 machine: virt cpu: rv64 memory: 256M kernel: barebox-dt-2nd.img - bios: opensbi-riscv64-generic-fw_dynamic.bin extra_args: '' BareboxDriver: prompt: 'barebox@[^:]+:[^ ]+ ' @@ -15,13 +14,7 @@ targets: BareboxTestStrategy: {} features: - virtio-mmio - runner: - download: - opensbi-riscv64-generic-fw_dynamic.bin: https://github.com/qemu/qemu/blob/v5.2.0/pc-bios/opensbi-riscv64-generic-fw_dynamic.bin?raw=true images: barebox-dt-2nd.img: !template "$LG_BUILDDIR/images/barebox-dt-2nd.img" - opensbi-riscv64-generic-fw_dynamic.bin: !template "$LG_BUILDDIR/opensbi-riscv64-generic-fw_dynamic.bin" -tools: - qemu: /usr/bin/qemu-system-riscv64 imports: - ../strategy.py diff --git a/test/riscv/qemu@virt32_defconfig.yaml b/test/riscv/qemu@virt32_defconfig.yaml index 5c602635d4..da1bb5bddd 100644 --- a/test/riscv/qemu@virt32_defconfig.yaml +++ b/test/riscv/qemu@virt32_defconfig.yaml @@ -2,7 +2,7 @@ targets: main: drivers: QEMUDriver: - qemu_bin: qemu + qemu_bin: qemu-system-riscv32 machine: virt cpu: rv32 memory: 256M @@ -21,7 +21,5 @@ targets: images: barebox-dt-2nd.img: !template "$LG_BUILDDIR/images/barebox-dt-2nd.img" opensbi-riscv32-generic-fw_dynamic.bin: !template "$LG_BUILDDIR/opensbi-riscv32-generic-fw_dynamic.bin" -tools: - qemu: /usr/bin/qemu-system-riscv32 imports: - ../strategy.py diff --git a/test/riscv/rv64i_defconfig.yaml b/test/riscv/rv64i_defconfig.yaml new file mode 120000 index 0000000000..46080baed6 --- /dev/null +++ b/test/riscv/rv64i_defconfig.yaml @@ -0,0 +1 @@ +qemu-virt64@rv64i_defconfig.yaml
\ No newline at end of file diff --git a/test/riscv/sifive_defconfig.yaml b/test/riscv/sifive@rv64i_defconfig.yaml index f7299453a4..ce4ea0e1e2 100644 --- a/test/riscv/sifive_defconfig.yaml +++ b/test/riscv/sifive@rv64i_defconfig.yaml @@ -2,24 +2,17 @@ targets: main: drivers: QEMUDriver: - qemu_bin: qemu + qemu_bin: qemu-system-riscv64 machine: sifive_u cpu: sifive-u54 memory: 256M kernel: barebox-hifive-unleashed.img - bios: opensbi-riscv64-generic-fw_dynamic.bin extra_args: '' BareboxDriver: prompt: 'barebox@[^:]+:[^ ]+ ' bootstring: 'commandline:' BareboxTestStrategy: {} - runner: - download: - opensbi-riscv64-generic-fw_dynamic.bin: https://github.com/qemu/qemu/blob/v5.2.0/pc-bios/opensbi-riscv64-generic-fw_dynamic.bin?raw=true images: barebox-hifive-unleashed.img: !template "$LG_BUILDDIR/images/barebox-hifive-unleashed.img" - opensbi-riscv64-generic-fw_dynamic.bin: !template "$LG_BUILDDIR/opensbi-riscv64-generic-fw_dynamic.bin" -tools: - qemu: /usr/bin/qemu-system-riscv64 imports: - ../strategy.py diff --git a/test/riscv/tinyemu@virt64_defconfig.yaml b/test/riscv/tinyemu@rv64i_defconfig.yaml index e9624160ef..cb22afc685 100644 --- a/test/riscv/tinyemu@virt64_defconfig.yaml +++ b/test/riscv/tinyemu@rv64i_defconfig.yaml @@ -13,7 +13,7 @@ targets: - virtio-mmio runner: download: - bbl64.bin: https://barebox.org/jsbarebox/bbl64.bin + bbl64.bin: https://barebox.org/demo/bbl64.bin images: barebox-dt-2nd.img: !template "$LG_BUILDDIR/images/barebox-dt-2nd.img" tools: diff --git a/test/riscv/tinyemu@virt32_defconfig.yaml b/test/riscv/tinyemu@virt32_defconfig.yaml index 1102f36aca..bc6eada376 100644 --- a/test/riscv/tinyemu@virt32_defconfig.yaml +++ b/test/riscv/tinyemu@virt32_defconfig.yaml @@ -13,7 +13,7 @@ targets: - virtio-mmio runner: download: - bbl32.bin: https://barebox.org/jsbarebox/bbl32.bin + bbl32.bin: https://barebox.org/demo/bbl32.bin images: barebox-dt-2nd.img: !template "$LG_BUILDDIR/images/barebox-dt-2nd.img" tools: diff --git a/test/riscv/virt64_defconfig.yaml b/test/riscv/virt64_defconfig.yaml deleted file mode 120000 index ab419d5e7f..0000000000 --- a/test/riscv/virt64_defconfig.yaml +++ /dev/null @@ -1 +0,0 @@ -qemu@virt64_defconfig.yaml
\ No newline at end of file diff --git a/test/self/Kconfig b/test/self/Kconfig index cf11efe544..33e478aee8 100644 --- a/test/self/Kconfig +++ b/test/self/Kconfig @@ -10,6 +10,7 @@ if SELFTEST config CMD_SELFTEST bool "selftest command" depends on COMMAND_SUPPORT + default y help Command to run enabled barebox self-tests. If run without arguments, all tests are run @@ -28,7 +29,22 @@ config SELFTEST_AUTORUN config SELFTEST_ENABLE_ALL bool "Enable all self-tests" select SELFTEST_PRINTF + select SELFTEST_MALLOC select SELFTEST_PROGRESS_NOTIFIER + select SELFTEST_OF_MANIPULATION + select SELFTEST_ENVIRONMENT_VARIABLES if ENVIRONMENT_VARIABLES + select SELFTEST_FS_RAMFS if FS_RAMFS + select SELFTEST_DIRFD if FS_RAMFS && FS_DEVFS + select SELFTEST_TFTP if FS_TFTP + select SELFTEST_JSON if JSMN + select SELFTEST_JWT if JWT + select SELFTEST_DIGEST if DIGEST + select SELFTEST_MMU if MMU + select SELFTEST_STRING + select SELFTEST_SETJMP if ARCH_HAS_SJLJ + select SELFTEST_REGULATOR if REGULATOR_FIXED + select SELFTEST_TEST_COMMAND if CMD_TEST + select SELFTEST_IDR help Selects all self-tests compatible with current configuration @@ -51,4 +67,54 @@ config SELFTEST_OF_MANIPULATION config SELFTEST_PROGRESS_NOTIFIER bool "progress notifier selftest" +config SELFTEST_ENVIRONMENT_VARIABLES + bool "environment variable selftest" + +config SELFTEST_FS_RAMFS + bool "ramfs selftest" + depends on FS_RAMFS + +config SELFTEST_DIRFD + bool "dirfd selftest" + depends on FS_RAMFS && FS_DEVFS + +config SELFTEST_JSON + bool "JSON selftest" + depends on JSMN + +config SELFTEST_JWT + bool "JSON Web Token selftest" + depends on JWT + +config SELFTEST_MMU + bool "MMU remapping selftest" + select MEMTEST + depends on MMU + +config SELFTEST_DIGEST + bool "Digest selftest" + depends on DIGEST + select PRINTF_HEXSTR + +config SELFTEST_STRING + bool "String library selftest" + select VERSION_CMP + +config SELFTEST_SETJMP + bool "setjmp/longjmp library selftest" + depends on ARCH_HAS_SJLJ + +config SELFTEST_REGULATOR + bool "Regulator selftest" + depends on REGULATOR_FIXED + select OF_OVERLAY + +config SELFTEST_TEST_COMMAND + bool "test command selftest" + depends on CMD_TEST + +config SELFTEST_IDR + bool "idr selftest" + select IDR + endif diff --git a/test/self/Makefile b/test/self/Makefile index 65d01596b8..fbc1867254 100644 --- a/test/self/Makefile +++ b/test/self/Makefile @@ -3,5 +3,29 @@ obj-$(CONFIG_SELFTEST) += core.o obj-$(CONFIG_SELFTEST_MALLOC) += malloc.o obj-$(CONFIG_SELFTEST_PRINTF) += printf.o +CFLAGS_printf.o += -Wno-format-security -Wno-format obj-$(CONFIG_SELFTEST_PROGRESS_NOTIFIER) += progress-notifier.o obj-$(CONFIG_SELFTEST_OF_MANIPULATION) += of_manipulation.o of_manipulation.dtb.o +obj-$(CONFIG_SELFTEST_ENVIRONMENT_VARIABLES) += envvar.o +obj-$(CONFIG_SELFTEST_FS_RAMFS) += ramfs.o +obj-$(CONFIG_SELFTEST_DIRFD) += dirfd.o +obj-$(CONFIG_SELFTEST_JSON) += json.o +obj-$(CONFIG_SELFTEST_JWT) += jwt.o jwt_test.pem.o +obj-$(CONFIG_SELFTEST_DIGEST) += digest.o +obj-$(CONFIG_SELFTEST_MMU) += mmu.o +obj-$(CONFIG_SELFTEST_STRING) += string.o +obj-$(CONFIG_SELFTEST_SETJMP) += setjmp.o +obj-$(CONFIG_SELFTEST_REGULATOR) += regulator.o test_regulator.dtbo.o +obj-$(CONFIG_SELFTEST_TEST_COMMAND) += test_command.o +obj-$(CONFIG_SELFTEST_IDR) += idr.o + +ifdef REGENERATE_RSATOC + +$(obj)/jwt_test.pem.c_shipped: $(src)/jwt_test.pem FORCE + $(call if_changed,rsa_keys,$(basename $(target-stem)):$<,-s) + +endif + +clean-files := *.dtb *.dtb.S .*.dtc .*.pre .*.dts *.dtb.z +clean-files += *.dtbo *.dtbo.S .*.dtso +clean-files += *.pem.c diff --git a/test/self/core.c b/test/self/core.c index caa4c27f6d..40f5ee842d 100644 --- a/test/self/core.c +++ b/test/self/core.c @@ -7,6 +7,30 @@ LIST_HEAD(selftests); +int selftest_run(struct selftest *test) +{ + int err; + + test->running = true; + err = test->func(); + test->running = false; + + return err; +} + +bool selftest_is_running(struct selftest *test) +{ + if (test) + return test->running; + + list_for_each_entry(test, &selftests, list) { + if (selftest_is_running(test)) + return true; + } + + return false; +} + void selftests_run(void) { struct selftest *test; @@ -15,7 +39,7 @@ void selftests_run(void) pr_notice("Configured tests will run now\n"); list_for_each_entry(test, &selftests, list) - err |= test->func(); + err |= selftest_run(test); if (err) pr_err("Some selftests failed\n"); diff --git a/test/self/digest.c b/test/self/digest.c new file mode 100644 index 0000000000..4cda5b0963 --- /dev/null +++ b/test/self/digest.c @@ -0,0 +1,213 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <common.h> +#include <bselftest.h> +#include <clock.h> +#include <digest.h> + +BSELFTEST_GLOBALS(); + +struct digest_test_case { + const char *name; + const void *buf; + size_t buf_size; + const void *digest_str; + u64 time_ns; +}; + +#define TEST_CASE(buf, digest_str) \ + { #buf, buf, sizeof(buf), digest_str } + +#define test_digest(option, algo, ...) do { \ + struct digest_test_case *t, cases[] = { __VA_ARGS__, { /* sentinel */ } }; \ + for (t = cases; t->buf; t++) \ + __test_digest((option), (algo), t, __func__, __LINE__); \ + if (!__is_defined(DEBUG)) \ + break; \ + printf("%s:\t", algo); \ + for (t = cases; t->buf; t++) \ + printf(" digest(%zu bytes) = %10lluns", t->buf_size, t->time_ns); \ + printf("\n"); \ +} while (0) + +static inline const char *digest_suffix(const char *str, const char *suffix) +{ + static char buf[32]; + + if (!*suffix) + return str; + + WARN_ON(snprintf(buf, sizeof(buf), "%s-%s", str, suffix) >= sizeof(buf)); + return buf; +} + +static void __test_digest(bool option, + const char *algo, struct digest_test_case *t, + const char *func, int line) +{ + unsigned char *output, *digest; + struct digest *d; + int hash_len, digest_len; + u64 start; + int ret; + + total_tests++; + + if (!option) { + skipped_tests++; + return; + } + + d = digest_alloc(algo); + if (!d) { + printf("%s:%d: failed to allocate %s digest\n", func, line, algo); + goto fail; + } + + hash_len = digest_length(d); + digest_len = strlen(t->digest_str) / 2; + if (hash_len != digest_len) { + printf("%s:%d: %s digests have length %u, but %u expected\n", + func, line, algo, hash_len, digest_len); + goto fail; + } + + output = calloc(hash_len, 1); + if (WARN_ON(!output)) + goto fail; + + digest = calloc(digest_len, 1); + if (WARN_ON(!digest)) + goto fail; + + ret = hex2bin(digest, t->digest_str, digest_len); + if (WARN_ON(ret)) + goto fail; + + start = get_time_ns(); + + ret = digest_digest(d, t->buf, t->buf_size, output); + if (ret) { + printf("%s:%d: error calculating %s(%s): %pe\n", + func, line, algo, t->name, ERR_PTR(ret)); + goto fail; + } + + t->time_ns = get_time_ns() - start; + + if (memcmp(output, digest, hash_len)) { + printf("%s:%d: mismatch calculating %s(%s):\n\tgot: %*phN\n\tbut: %*phN expected\n", + func, line, algo, t->name, hash_len, output, hash_len, digest); + goto fail; + } + + return; +fail: + failed_tests++; +} + +static const u8 zeroes7[7] = {}; +static const u8 one32[32] = { 1 }; +static u8 inc4097[4097]; + +static void test_digest_md5(const char *suffix) +{ + bool cond; + + cond = !strcmp(suffix, "generic") ? IS_ENABLED(CONFIG_DIGEST_MD5_GENERIC) : + IS_ENABLED(CONFIG_HAVE_DIGEST_MD5); + + test_digest(cond, digest_suffix("md5", suffix), + TEST_CASE(zeroes7, "d310a40483f9399dd7ed1712e0fdd702"), + TEST_CASE(one32, "b39ac6e2aa7e375c38ba7ae921b5ba89"), + TEST_CASE(inc4097, "70410aad262cd11e63ae854804c8024b")); +} + +static void test_digests_sha12(const char *suffix) +{ + bool cond; + + cond = !strcmp(suffix, "generic") ? IS_ENABLED(CONFIG_DIGEST_SHA1_GENERIC) : + !strcmp(suffix, "asm") ? IS_ENABLED(CONFIG_DIGEST_SHA1_ARM) : + IS_ENABLED(CONFIG_HAVE_DIGEST_SHA1); + + test_digest(cond, digest_suffix("sha1", suffix), + TEST_CASE(zeroes7, "77ce0377defbd11b77b1f4ad54ca40ea5ef28490"), + TEST_CASE(one32, "cbd9cbfc20182e4b71e593e7ad598fc383cc6058"), + TEST_CASE(inc4097, "c627e736efd8bb0dff1778335c9c79cb1f27e396")); + + + cond = !strcmp(suffix, "generic") ? IS_ENABLED(CONFIG_DIGEST_SHA224_GENERIC) : + !strcmp(suffix, "asm") ? IS_ENABLED(CONFIG_DIGEST_SHA256_ARM) : + !strcmp(suffix, "ce") ? IS_ENABLED(CONFIG_DIGEST_SHA256_ARM64_CE) : + IS_ENABLED(CONFIG_HAVE_DIGEST_SHA224); + + test_digest(cond, digest_suffix("sha224", suffix), + TEST_CASE(zeroes7, "fbf6df85218ac5632461a8a17c6f294e6f35264cbfc0a9774a4f665b"), + TEST_CASE(one32, "343cb3950305e6e6331e294b0a4925739d09ecbd2b43a2fc87c09941"), + TEST_CASE(inc4097, "6596b5dcfbd857f4246d6b94508b8a1a5b715a4f644a0c1e7d54c4f7")); + + + cond = !strcmp(suffix, "generic") ? IS_ENABLED(CONFIG_DIGEST_SHA256_GENERIC) : + !strcmp(suffix, "asm") ? IS_ENABLED(CONFIG_DIGEST_SHA256_ARM) : + !strcmp(suffix, "ce") ? IS_ENABLED(CONFIG_DIGEST_SHA256_ARM64_CE) : + IS_ENABLED(CONFIG_HAVE_DIGEST_SHA256); + + test_digest(cond, digest_suffix("sha256", suffix), + TEST_CASE(zeroes7, "837885c8f8091aeaeb9ec3c3f85a6ff470a415e610b8ba3e49f9b33c9cf9d619"), + TEST_CASE(one32, "01d0fabd251fcbbe2b93b4b927b26ad2a1a99077152e45ded1e678afa45dbec5"), + TEST_CASE(inc4097, "1e973d029df2b2c66cb42a942c5edb45966f02abaff29fe99410e44d271d0efc")); +} + + +static void test_digests_sha35(const char *suffix) +{ + bool cond; + + cond = !strcmp(suffix, "generic") ? IS_ENABLED(CONFIG_DIGEST_SHA384_GENERIC) : + IS_ENABLED(CONFIG_HAVE_DIGEST_SHA384); + + test_digest(cond, digest_suffix("sha384", suffix), + TEST_CASE(zeroes7, "b56705a73cf280f06d3a6b482c441a3d280c930d0c44b04f364dcdcedcfbc47c" + "f3645a71da7b97f9e5d3a0924f6b9634"), + TEST_CASE(one32, "dd606b49d7658a5eae905d593271c280819f92eb1a9a4986057aedc0a5f2eaea" + "99052904718f6d83f16ad209d793f253"), + TEST_CASE(inc4097, "f76046b90890f20ae94066a3ad33010f5b3b2fd46977414636bbc634898b06fd" + "4cb8f85e0926e8817e518300a930529e")); + + + cond = !strcmp(suffix, "generic") ? IS_ENABLED(CONFIG_DIGEST_SHA512_GENERIC) : + IS_ENABLED(CONFIG_HAVE_DIGEST_SHA512); + + test_digest(cond, digest_suffix("sha512", suffix), + TEST_CASE(zeroes7, "76afca18a9b81ffb967ffcf0460ed221c3605d3820057214d785fa88259bb5cb" + "729576178e6edb0134f645d2e2e92cbabf1333462f3b9058692c950f51c64a92"), + TEST_CASE(one32, "ce0c265ecc82dd8cee6e56ce44e45dafd7a0c5750df914b253a1fb7a8af66ddb" + "99763607f0a85d0bd43669194a3a40577a528af395f4f17e06f1defcc6deb2a5"), + TEST_CASE(inc4097, "42eb09aca460d79b0c0aeac28187ed055a92e33602b69428461697680ff9f48f" + "60a5a68aa0017e3446433349b42592b74713d7787628a58e400b7f588b9bd69b")); +} + +static void test_digests(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(inc4097); i++) + inc4097[i] = i; + + test_digest_md5("generic"); + + test_digests_sha12("generic"); + if (IS_ENABLED(CONFIG_CPU_32)) + test_digests_sha12("asm"); + + test_digests_sha35("generic"); + + test_digest_md5(""); + test_digests_sha12(""); + test_digests_sha35(""); + +} +bselftest(core, test_digests); diff --git a/test/self/dirfd.c b/test/self/dirfd.c new file mode 100644 index 0000000000..20b5425871 --- /dev/null +++ b/test/self/dirfd.c @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <common.h> +#include <fcntl.h> +#include <fs.h> +#include <string.h> +#include <linux/bitfield.h> +#include <unistd.h> +#include <bselftest.h> + +BSELFTEST_GLOBALS(); + +#define expect(cond, res, fmt, ...) ({ \ + int __cond = (cond); \ + int __res = (res); \ + total_tests++; \ + if (__cond != __res) { \ + failed_tests++; \ + printf("%s:%d failed: %s == %d: " fmt "\n", \ + __func__, __LINE__, #cond, __cond, ##__VA_ARGS__); \ + } \ + __cond == __res; \ +}) + +static void check_statat(const char *at, int dirfd, const char *prefix, unsigned expected) +{ + static const char *paths[] = { ".", "..", "zero", "dev" }; + struct stat s; + + for (int i = 0; i < ARRAY_SIZE(paths); i++) { + const char *path = paths[i]; + char *fullpath = NULL, *testpath = basprintf("%s%s", prefix, path); + struct fs_device *fsdev1, *fsdev2; + int ret; + + ret = statat(dirfd, testpath, &s); + if (!expect(ret == 0, FIELD_GET(BIT(2), expected), + "statat(%s, %s): %m", at, testpath)) + goto next; + + fullpath = canonicalize_path(dirfd, testpath); + if (!expect(fullpath != NULL, FIELD_GET(BIT(1), expected), + "canonicalize_path(%s, %s): %m", at, testpath)) + goto next; + + if (!fullpath) + goto next; + + fsdev1 = get_fsdevice_by_path(AT_FDCWD, fullpath); + if (!expect(IS_ERR_OR_NULL(fsdev1), false, "get_fsdevice_by_path(AT_FDCWD, %s)", + fullpath)) + goto next; + + fsdev2 = get_fsdevice_by_path(dirfd, testpath); + if (!expect(IS_ERR_OR_NULL(fsdev1), false, "get_fsdevice_by_path(%s, %s)", + at, testpath)) + goto next; + + if (!expect(fsdev1 == fsdev2, true, + "get_fsdevice_by_path(%s, %s) != get_fsdevice_by_path(AT_FDCWD, %s)", + fullpath, at, testpath)) + goto next; + + ret = strcmp_ptr(fsdev1->path, "/dev"); + if (!expect(ret == 0, FIELD_GET(BIT(0), expected), + "fsdev_of(%s)->path = %s != /dev", fullpath, fsdev1->path)) + goto next; + +next: + expected >>= 3; + free(testpath); + free(fullpath); + } +} + +static void do_test_dirfd(const char *at, int dirfd, + unsigned expected1, unsigned expected2, + unsigned expected3, unsigned expected4) +{ + if (dirfd < 0 && dirfd != AT_FDCWD) + return; + + check_statat(at, dirfd, "", expected1); + check_statat(at, dirfd, "./", expected1); + check_statat(at, dirfd, "/dev/", expected2); + check_statat(at, dirfd, "/dev/./", expected2); + check_statat(at, dirfd, "/dev/../dev/", expected2); + check_statat(at, dirfd, "/", expected3); + check_statat(at, dirfd, "../", expected4); + + if (dirfd >= 0) + close(dirfd); +} + + +static void test_dirfd(void) +{ + int fd; + + fd = open("/", O_PATH); + if (expect(fd < 0, false, "open(/, O_PATH) = %d", fd)) + close(fd); + +#define B(dot, dotdot, zero, dev) 0b##dev##zero##dotdot##dot + /* We do fiften tests for every configuration + * for dir in ./ /dev / ../ ; do + * for file in . .. zero dev ; do + * test if file exists + * test if file can be canonicalized + * test if parent FS is mounted at /dev + * done + * done + * + * The bits belows correspond to whether a test fails in the above loop + */ + + do_test_dirfd("AT_FDCWD", AT_FDCWD, + B(110,110,000,111), B(111,110,111,000), + B(110,110,000,111), B(110,110,000,111)); + do_test_dirfd("/dev", open("/dev", O_PATH), + B(111,110,111,000), B(111,110,111,000), + B(110,110,000,111), B(110,110,000,111)); + do_test_dirfd("/dev O_CHROOT", open("/dev", O_PATH | O_CHROOT), + B(111,111,111,000), B(000,000,000,000), + B(111,111,111,000), B(111,111,111,000)); +} +bselftest(core, test_dirfd); diff --git a/test/self/envvar.c b/test/self/envvar.c new file mode 100644 index 0000000000..a4620f0437 --- /dev/null +++ b/test/self/envvar.c @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <common.h> +#include <environment.h> +#include <bselftest.h> +#include <linux/sizes.h> + +BSELFTEST_GLOBALS(); + +static int strequal(const char *a, const char *b) +{ + if (!a || !b) + return a == b; + + return !strcmp(a, b); +} + +static void __expect_getenv(const char *var, const char *expect, + const char *func, int line) +{ + const char *val; + + total_tests++; + + val = getenv(var); + if (!IS_ENABLED(CONFIG_ENVIRONMENT_VARIABLES)) { + if (val == NULL) { + skipped_tests++; + return; + } + } + + if (!strequal(val, expect)) { + failed_tests++; + printf("%s:%d: failure: getenv(%s) == \"%s\", but \"%s\" expected\n", + func, line, var, val ?: "<NULL>", expect ?: "<NULL>"); + } +} + +#define expect_getenv(v, e) __expect_getenv(v, e, __func__, __LINE__) + +static void test_envvar(void) +{ + + if (!IS_ENABLED(CONFIG_ENVIRONMENT_VARIABLES)) + pr_info("built without environment variable support: Skipping tests\n"); + + expect_getenv("__TEST_VAR1", NULL); + + setenv("__TEST_VAR1", "VALUE1"); + + expect_getenv("__TEST_VAR1", "VALUE1"); + + unsetenv("__TEST_VAR1"); + + expect_getenv("__TEST_VAR1", NULL); + + setenv("__TEST_VAR1", "VALUE1"); + + expect_getenv("__TEST_VAR1", "VALUE1"); + + setenv("__TEST_VAR1", "VALUE2"); + + expect_getenv("__TEST_VAR1", "VALUE2"); + + setenv("__TEST_VAR1", "1337"); + + expect_getenv("__TEST_VAR1", "1337"); + + pr_setenv("__TEST_VAR1", "0x%s", "1337"); + + expect_getenv("__TEST_VAR1", "0x1337"); + + pr_setenv("__TEST_VAR1", "%ux1%c%x", 0, '3', 0x37); + + expect_getenv("__TEST_VAR1", "0x1337"); + + unsetenv("__TEST_VAR1"); +} +bselftest(core, test_envvar); diff --git a/test/self/idr.c b/test/self/idr.c new file mode 100644 index 0000000000..3d23141e0f --- /dev/null +++ b/test/self/idr.c @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <printk.h> +#include <linux/idr.h> +#include <bselftest.h> + +BSELFTEST_GLOBALS(); + +#define __expect(cond, fmt, ...) ({ \ + bool __cond = (cond); \ + total_tests++; \ + \ + if (!__cond) { \ + failed_tests++; \ + printf("%s failed at %s:%d " fmt "\n", \ + #cond, __func__, __LINE__, ##__VA_ARGS__); \ + } \ + __cond; \ +}) + +#define expect(ret, ...) __expect((ret), __VA_ARGS__) + +static int cmp[3] = { 7, 1, 2}; +static int sorted_cmp[3] = { 1, 2, 7}; + +static int test_idr_for_each(int id, void *p, void *data) +{ + expect(data == &cmp[2]); + expect(*(int *)p == id); + + return id == 1 ? 0 : -1; +} + +static int count_idr(int id, void *p, void *data) +{ + int *count = data; + + ++*count; + + return 0; +} + +static void test_idr(void) +{ + void *ptr; + int id, count; + + DEFINE_IDR(idr); + + expect(idr_is_empty(&idr)); + + expect(!idr_find(&idr, cmp[0])); + + id = idr_alloc_one(&idr, &cmp[0], cmp[0]); + expect(id == cmp[0]); + + expect(!idr_is_empty(&idr)); + + ptr = idr_find(&idr, cmp[0]); + expect(ptr); + expect(ptr == &cmp[0]); + + id = idr_alloc_one(&idr, &cmp[1], cmp[1]); + expect(id == cmp[1]); + + id = idr_alloc_one(&idr, &cmp[2], cmp[2]); + expect(id == cmp[2]); + + count = 0; + + idr_for_each_entry(&idr, ptr, id) { + expect(id == sorted_cmp[count]); + expect(*(int *)ptr == sorted_cmp[count]); + + count++; + + } + + expect(count == 3); + + expect(idr_for_each(&idr, test_idr_for_each, &cmp[2]) == -1); + + count = 0; + expect(idr_for_each(&idr, count_idr, &count) == 0); + expect(count == 3); + + idr_remove(&idr, 1); + + count = 0; + expect(idr_for_each(&idr, count_idr, &count) == 0); + expect(count == 2); + + idr_remove(&idr, 7); + + count = 0; + expect(idr_for_each(&idr, count_idr, &count) == 0); + expect(count == 1); + + idr_remove(&idr, 2); + + count = 0; + expect(idr_for_each(&idr, count_idr, &count) == 0); + expect(count == 0); + + expect(idr_is_empty(&idr)); + + idr_alloc_one(&idr, &cmp[0], cmp[0]); + idr_alloc_one(&idr, &cmp[1], cmp[1]); + idr_alloc_one(&idr, &cmp[2], cmp[2]); + + expect(!idr_is_empty(&idr)); + + idr_destroy(&idr); + + expect(idr_is_empty(&idr)); +} +bselftest(core, test_idr); diff --git a/test/self/json.c b/test/self/json.c new file mode 100644 index 0000000000..4896717689 --- /dev/null +++ b/test/self/json.c @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <common.h> +#include <bselftest.h> +#include <jsmn.h> + +BSELFTEST_GLOBALS(); + +static const jsmntok_t* +__json_expect(const char *json, const jsmntok_t *token, const char **lookup, + jsmntype_t expected_type, const char *expected_value) +{ + bool success = true; + + total_tests++; + + if (token->type != expected_type) { + failed_tests++; + printf("%pJP: type mismatch: got %d, but %d expected\n", + lookup, token->type, expected_type); + success = false; + } + + if (!expected_value) + goto out; + + total_tests++; + + if (!jsmn_eq(expected_value, json, token)) { + failed_tests++; + printf("%pJP: string mismatch: got %.*s, but %s expected\n", + lookup, (int)(token->end - token->start), + &json[token->start], expected_value); + success = false; + } + +out: + return success ? token : NULL; +} + +static const jsmntok_t* +json_expect(const char *json, const jsmntok_t *tokens, const char **lookup, + jsmntype_t expected_type, const char *expected_value) +{ + const jsmntok_t *token; + + total_tests++; + + token = jsmn_locate(lookup, json, tokens); + if (!token) { + failed_tests++; + printf("%pJP: couldn't be located\n", lookup); + return NULL; + } + + return __json_expect(json, token, lookup, expected_type, expected_value); +} + +#define JP(...) (const char *[]) { __VA_ARGS__, NULL } + +/* Wonky indentation is intended */ +static const char json[] = +"{\n" +" \"null\" :null,\"number\" : 0x42,\n" +" \"object\": {\n" +" \"a\": \"b\",\n" +" \"C\": \"dE\"," +" \"e\": \"\"\n" +" }," +" \"array\": [ \"1\",\"2\",\t\t\"3\"],\n" +" \"boolean\": true,\n" +"\"string\": \"Hello World\n\"}\n"; + +static void test_json(void) +{ + const jsmntok_t *token; + jsmntok_t *tokens; + char *string; + + total_tests++; + + /* Figure out how many tokens we need. */ + tokens = jsmn_parse_alloc(json, sizeof(json), NULL); + if (!tokens) { + printf("failed to parse JSON\n"); + failed_tests++; + return; + } + + json_expect(json, tokens, JP("null"), JSMN_PRIMITIVE, "null"); + json_expect(json, tokens, JP("number"), JSMN_PRIMITIVE, "0x42"); + json_expect(json, tokens, JP("object"), JSMN_OBJECT, NULL); + json_expect(json, tokens, JP("object", "a"), JSMN_STRING, "b"); + json_expect(json, tokens, JP("object", "C"), JSMN_STRING, "dE"); + json_expect(json, tokens, JP("object", "e"), JSMN_STRING, ""); + + token = json_expect(json, tokens, JP("array"), JSMN_ARRAY, NULL); + + token = jsmn_skip_value(token); + __json_expect(json, token, JP("boolean"), JSMN_STRING, "boolean"); + + token = jsmn_skip_value(token); + __json_expect(json, token, JP("boolean"), JSMN_PRIMITIVE, "true"); + + string = jsmn_strdup(JP("string"), json, tokens); + if (WARN_ON(!string)) + goto out; + + total_tests++; + if (strcmp(string, "Hello World\n")) { + failed_tests++; + printf("%pJP: string mismatch\n", JP("string")); + } + + free(string); +out: + free(tokens); +} +bselftest(parser, test_json); diff --git a/test/self/jwt.c b/test/self/jwt.c new file mode 100644 index 0000000000..015a0e4367 --- /dev/null +++ b/test/self/jwt.c @@ -0,0 +1,156 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <common.h> +#include <rsa.h> +#include <bselftest.h> +#include <crypto/jwt.h> +#include <console.h> + +BSELFTEST_GLOBALS(); + +static const jsmntok_t *check_token(const jsmntok_t *token, + const char *claim, + const char *payload, + jsmntype_t expected_type, + const char *expected_value) +{ + total_tests++; + + if (token->type != expected_type) { + failed_tests++; + printf("claim %s has type mismatch: got %d, but %d expected\n", + claim, token->type, expected_type); + return NULL; + } + + total_tests++; + + if (!jsmn_eq(expected_value, payload, token)) { + failed_tests++; + printf("claim %s: value has mismatch: got %.*s, but %s expected\n", + claim, (int)(token->end - token->start), + &payload[token->start], expected_value); + return NULL; + } + + return token; +} + +static const jsmntok_t *jwt_check_claim(const struct jwt *jwt, + const char *claim, + jsmntype_t expected_type, + const char *expected_value) +{ + const jsmntok_t *token; + + total_tests++; + + token = jwt_get_claim(jwt, claim); + if (!token) { + failed_tests++; + printf("claim %s couldn't be located\n", claim); + return NULL; + } + + return check_token(token, claim, jwt_get_payload(jwt), + expected_type, expected_value); +} + +static const char jwt_rs256[] = + " eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9." + "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0." + "NHVaYe26MbtOYhSKkoKYdFVomg4i8ZJd8_-RU8VNbftc4TSMb4bXP3l3YlNWACwyXPGf" + "fz5aXHc6lty1Y2t4SWRqGteragsVdZufDn5BlnJl9pdR_kdVFUsra2rWKEofkZeIC4yW" + "ytE58sMIihvo9H1ScmmVwBcQP6XETqYd0aSHp1gOa9RdUPDvoXQ5oqygTqVtxaDr6wUF" + "KrKItgBMzWIdNZ6y7O9E0DhEPTbE9rfBo6KTFsHAZnMg4k68CDp2woYIaXbmYTWcvbzI" + "uHO7_37GT79XdIwkm95QJ7hYC9RiwrV7mesbY4PAahERJawntho0my942XheVLmGwLMBkQ\n \n"; + +static void test_jwt(void) +{ + char *jwt_rs256_mangled, *ch; + struct jwt_key jwt_key; + struct jwt *jwt; + extern const struct rsa_public_key __key_jwt_test; + int old_loglevel; + + jwt_key.alg = JWT_ALG_RS256; + jwt_key.material.rsa_pub = &__key_jwt_test; + total_tests++; + + jwt = jwt_decode(jwt_rs256, &jwt_key); + if (IS_ERR(jwt)) { + printf("failed to parse jwt\n"); + failed_tests++; + } else { + jwt_check_claim(jwt, "sub", JSMN_STRING, "1234567890"); + jwt_check_claim(jwt, "name", JSMN_STRING, "John Doe"); + jwt_check_claim(jwt, "admin", JSMN_PRIMITIVE, "true"); + jwt_check_claim(jwt, "iat", JSMN_PRIMITIVE, "1516239022"); + + jwt_free(jwt); + } + + /* + * Following tests intentionally fail and JWT failures are intentionally + * noisy, so we decrease logging a bit during their run + */ + + old_loglevel = barebox_set_loglevel(MSG_CRIT); + + jwt_rs256_mangled = strdup(jwt_rs256); + ch = &jwt_rs256_mangled[strlen(jwt_rs256_mangled) - 1]; + *ch = *ch == '_' ? '-' : '_'; + + total_tests++; + + jwt = jwt_decode(jwt_rs256_mangled, &jwt_key); + if (!IS_ERR(jwt)) { + printf("%s:%d expected JWT verification to fail\n", __func__, __LINE__); + failed_tests++; + jwt_free(jwt); + } + + free(jwt_rs256_mangled); + + jwt_rs256_mangled = strdup(jwt_rs256); + ch = &jwt_rs256_mangled[0]; + *ch = *ch == '_' ? '-' : '_'; + + total_tests++; + + jwt = jwt_decode(jwt_rs256_mangled, &jwt_key); + if (!IS_ERR(jwt)) { + printf("%s:%d expected JWT verification to fail\n", __func__, __LINE__); + failed_tests++; + jwt_free(jwt); + } + + free(jwt_rs256_mangled); + + total_tests++; + + jwt_key.alg = JWT_ALG_RS384; + + jwt = jwt_decode(jwt_rs256, &jwt_key); + if (!IS_ERR(jwt)) { + printf("%s:%d expected JWT verification to fail\n", __func__, __LINE__); + failed_tests++; + jwt_free(jwt); + } + + total_tests++; + + jwt_key.alg = JWT_ALG_NONE; + + jwt = jwt_decode(jwt_rs256, &jwt_key); + if (!IS_ERR(jwt)) { + printf("%s:%d expected JWT verification to fail\n", __func__, __LINE__); + failed_tests++; + jwt_free(jwt); + } + + barebox_set_loglevel(old_loglevel); +} +bselftest(parser, test_jwt); diff --git a/test/self/jwt_test.pem b/test/self/jwt_test.pem new file mode 100644 index 0000000000..349a5b6a47 --- /dev/null +++ b/test/self/jwt_test.pem @@ -0,0 +1,37 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu1SU1LfVLPHCozMxH2Mo +4lgOEePzNm0tRgeLezV6ffAt0gunVTLw7onLRnrq0/IzW7yWR7QkrmBL7jTKEn5u ++qKhbwKfBstIs+bMY2Zkp18gnTxKLxoS2tFczGkPLPgizskuemMghRniWaoLcyeh +kd3qqGElvW/VDL5AaWTg0nLVkjRo9z+40RQzuVaE8AkAFmxZzow3x+VJYKdjykkJ +0iT9wCS0DRTXu269V264Vf/3jvredZiKRkgwlL9xNAwxXFg0x/XFw005UWVRIkdg +cKWTjpBP2dPwVZ4WWC+9aGVd+Gyn1o0CLelf4rEjGoXbAAEgAqeGUxrcIlbjXfbc +mwIDAQAB +-----END PUBLIC KEY----- +-----BEGIN PRIVATE KEY----- +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC7VJTUt9Us8cKj +MzEfYyjiWA4R4/M2bS1GB4t7NXp98C3SC6dVMvDuictGeurT8jNbvJZHtCSuYEvu +NMoSfm76oqFvAp8Gy0iz5sxjZmSnXyCdPEovGhLa0VzMaQ8s+CLOyS56YyCFGeJZ +qgtzJ6GR3eqoYSW9b9UMvkBpZODSctWSNGj3P7jRFDO5VoTwCQAWbFnOjDfH5Ulg +p2PKSQnSJP3AJLQNFNe7br1XbrhV//eO+t51mIpGSDCUv3E0DDFcWDTH9cXDTTlR +ZVEiR2BwpZOOkE/Z0/BVnhZYL71oZV34bKfWjQIt6V/isSMahdsAASACp4ZTGtwi +VuNd9tybAgMBAAECggEBAKTmjaS6tkK8BlPXClTQ2vpz/N6uxDeS35mXpqasqskV +laAidgg/sWqpjXDbXr93otIMLlWsM+X0CqMDgSXKejLS2jx4GDjI1ZTXg++0AMJ8 +sJ74pWzVDOfmCEQ/7wXs3+cbnXhKriO8Z036q92Qc1+N87SI38nkGa0ABH9CN83H +mQqt4fB7UdHzuIRe/me2PGhIq5ZBzj6h3BpoPGzEP+x3l9YmK8t/1cN0pqI+dQwY +dgfGjackLu/2qH80MCF7IyQaseZUOJyKrCLtSD/Iixv/hzDEUPfOCjFDgTpzf3cw +ta8+oE4wHCo1iI1/4TlPkwmXx4qSXtmw4aQPz7IDQvECgYEA8KNThCO2gsC2I9PQ +DM/8Cw0O983WCDY+oi+7JPiNAJwv5DYBqEZB1QYdj06YD16XlC/HAZMsMku1na2T +N0driwenQQWzoev3g2S7gRDoS/FCJSI3jJ+kjgtaA7Qmzlgk1TxODN+G1H91HW7t +0l7VnL27IWyYo2qRRK3jzxqUiPUCgYEAx0oQs2reBQGMVZnApD1jeq7n4MvNLcPv +t8b/eU9iUv6Y4Mj0Suo/AU8lYZXm8ubbqAlwz2VSVunD2tOplHyMUrtCtObAfVDU +AhCndKaA9gApgfb3xw1IKbuQ1u4IF1FJl3VtumfQn//LiH1B3rXhcdyo3/vIttEk +48RakUKClU8CgYEAzV7W3COOlDDcQd935DdtKBFRAPRPAlspQUnzMi5eSHMD/ISL +DY5IiQHbIH83D4bvXq0X7qQoSBSNP7Dvv3HYuqMhf0DaegrlBuJllFVVq9qPVRnK +xt1Il2HgxOBvbhOT+9in1BzA+YJ99UzC85O0Qz06A+CmtHEy4aZ2kj5hHjECgYEA +mNS4+A8Fkss8Js1RieK2LniBxMgmYml3pfVLKGnzmng7H2+cwPLhPIzIuwytXywh +2bzbsYEfYx3EoEVgMEpPhoarQnYPukrJO4gwE2o5Te6T5mJSZGlQJQj9q4ZB2Dfz +et6INsK0oG8XVGXSpQvQh3RUYekCZQkBBFcpqWpbIEsCgYAnM3DQf3FJoSnXaMhr +VBIovic5l0xFkEHskAjFTevO86Fsz1C2aSeRKSqGFoOQ0tmJzBEs1R6KqnHInicD +TQrKhArgLXX4v3CddjfTRJkFWDbE/CkvKZNOrcf1nhaGCPspRJj2KUkj1Fhl9Cnc +dn/RsYEONbwQSjIfMPkvxF+8HQ== +-----END PRIVATE KEY----- diff --git a/test/self/jwt_test.pem.c_shipped b/test/self/jwt_test.pem.c_shipped new file mode 100644 index 0000000000..2142ae15df --- /dev/null +++ b/test/self/jwt_test.pem.c_shipped @@ -0,0 +1,49 @@ +#include <rsa.h> + +static uint32_t jwt_test_modulus[] = { + 0x5df6dc9b, 0xdc2256e3, 0xa786531a, 0x00012002, + 0x231a85db, 0xe95fe2b1, 0xd68d022d, 0x5df86ca7, + 0x2fbd6865, 0x559e1658, 0x4fd9d3f0, 0xa5938e90, + 0x22476070, 0x39516551, 0xf5c5c34d, 0x5c5834c7, + 0x71340c31, 0x483094bf, 0x75988a46, 0xf78efade, + 0x6eb855ff, 0xbb6ebd57, 0xb40d14d7, 0x24fdc024, + 0xca4909d2, 0x4960a763, 0x8c37c7e5, 0x166c59ce, + 0x84f00900, 0x1433b956, 0xf73fb8d1, 0xd5923468, + 0x64e0d272, 0x0cbe4069, 0x25bd6fd5, 0xddeaa861, + 0x7327a191, 0xe259aa0b, 0x63208519, 0xcec92e7a, + 0x0f2cf822, 0xd15ccc69, 0x2f1a12da, 0x209d3c4a, + 0x6664a75f, 0xb3e6cc63, 0x9f06cb48, 0xa2a16f02, + 0x127e6efa, 0x4bee34ca, 0xb424ae60, 0x5bbc9647, + 0xead3f233, 0x89cb467a, 0x5532f0ee, 0x2dd20ba7, + 0x357a7df0, 0x46078b7b, 0xf3366d2d, 0x580e11e3, + 0x1f6328e2, 0xc2a33331, 0xb7d52cf1, 0xbb5494d4, +}; + +static uint32_t jwt_test_rr[] = { + 0xec4954b7, 0x61f69199, 0x9e489481, 0x14f25ec8, + 0x712de1ab, 0x9c4ed93b, 0xcff16ec3, 0xb6e0c808, + 0x56551022, 0x1206f0dc, 0x72051e96, 0x6ab07919, + 0x8d29bea3, 0xa2a79109, 0x18a5e53d, 0x0a1ed2ae, + 0xae6544f4, 0x5fb16424, 0x5253250c, 0x3fc04654, + 0x9b9a3028, 0xf7219ed8, 0x8f9a7d60, 0x1020027e, + 0xa7bb0182, 0xca68b839, 0x86a507ca, 0x725d9efb, + 0xf43e09cd, 0xd373027e, 0x6c22f55c, 0x074bee70, + 0x49525052, 0x0506900e, 0xf51bde0d, 0xc8f82c0e, + 0x4a00d71e, 0x0a517ae2, 0x616e76fb, 0xb17b75d0, + 0x4bfcbb90, 0x3efd07cf, 0xaf30c7cb, 0xa18dee7f, + 0x02ed9615, 0x9185d985, 0x630a209e, 0xef23435c, + 0x46277653, 0x57d47ec5, 0x86e58fcf, 0x8f0ebe09, + 0x3b26c77e, 0xa3ef370d, 0xf83df63e, 0xa30a742e, + 0x49c2fb64, 0xea9fbed9, 0xb7471da7, 0x7a411345, + 0x303732ed, 0x6660f318, 0xe3a7df4c, 0x6a784bd5, +}; + +struct rsa_public_key __key_jwt_test; +struct rsa_public_key __key_jwt_test = { + .len = 64, + .n0inv = 0x17d8566d, + .modulus = jwt_test_modulus, + .rr = jwt_test_rr, + .exponent = 0x10001, + .key_name_hint = "jwt_test", +}; diff --git a/test/self/malloc.c b/test/self/malloc.c index c25b416b97..0feec81018 100644 --- a/test/self/malloc.c +++ b/test/self/malloc.c @@ -7,26 +7,62 @@ #include <malloc.h> #include <memory.h> #include <linux/sizes.h> +#include <linux/bitops.h> BSELFTEST_GLOBALS(); -static void __expect(bool cond, bool expect, +#define get_alignment(val) \ + BIT(__builtin_constant_p(val) ? __builtin_ffsll(val) : __ffs64(val)) + +static_assert(get_alignment(0x1) != 1); +static_assert(get_alignment(0x2) != 2); +static_assert(get_alignment(0x3) != 1); +static_assert(get_alignment(0x4) != 4); +static_assert(get_alignment(0x5) != 1); +static_assert(get_alignment(0x6) != 2); +static_assert(get_alignment(0x8) != 8); +static_assert(get_alignment(0x99) != 0x1); +static_assert(get_alignment(0xDEADBEE0) != 0x10); + +static bool __expect_cond(bool cond, bool expect, + const char *condstr, const char *func, int line) +{ + total_tests++; + if (cond == expect) + return true; + + failed_tests++; + printf("%s:%d: %s to %s\n", func, line, + expect ? "failed" : "unexpectedly succeeded", + condstr); + return false; + +} + +static void *__expect(void *ptr, bool expect, const char *condstr, const char *func, int line) { + bool ok; total_tests++; - if (cond != expect) { - failed_tests++; - printf("%s:%d: %s to %s\n", func, line, - expect ? "failed" : "unexpectedly succeeded", - condstr); + + ok = __expect_cond(ptr != NULL, expect, condstr, func, line); + if (ok && ptr) { + unsigned alignment = get_alignment((uintptr_t)ptr); + if (alignment < CONFIG_MALLOC_ALIGNMENT) { + failed_tests++; + printf("%s:%d: invalid alignment of %u in %s = %p\n", func, line, + alignment, condstr, ptr); + } } + + return ptr; } -#define expect_alloc_ok(cond) \ - __expect((cond), true, #cond, __func__, __LINE__) +#define expect_alloc_ok(ptr) \ + __expect((ptr), true, #ptr, __func__, __LINE__) -#define expect_alloc_fail(cond) \ - __expect((cond), false, #cond, __func__, __LINE__) +#define expect_alloc_fail(ptr) \ + __expect((ptr), false, #ptr, __func__, __LINE__) static void test_malloc(void) { @@ -45,37 +81,42 @@ static void test_malloc(void) mem_malloc_size = 0; } - expect_alloc_ok(p = malloc(1)); + p = expect_alloc_ok(malloc(1)); free(p); if (mem_malloc_size) { - expect_alloc_fail(malloc(SIZE_MAX)); + tmp = expect_alloc_fail(malloc(RELOC_HIDE(SIZE_MAX, 0))); + free(tmp); if (0xf0000000 > mem_malloc_size) { - expect_alloc_fail((tmp = malloc(0xf0000000))); + tmp = expect_alloc_fail(malloc(RELOC_HIDE(0xf0000000, 0))); free(tmp); } } else { skipped_tests += 2; } - p = realloc(NULL, 1); - expect_alloc_ok(p = realloc(NULL, 1)); + free(realloc(NULL, 1)); + p = expect_alloc_ok(realloc(NULL, 1)); *p = 0x42; - expect_alloc_ok(tmp = realloc(p, 2)); + tmp = expect_alloc_ok(realloc(p, 2)); p = tmp; - __expect(*p == 0x42, true, "reread after realloc", __func__, __LINE__); + __expect_cond(*p == 0x42, true, "reread after realloc", __func__, __LINE__); if (mem_malloc_size) { - expect_alloc_fail(tmp = realloc(p, mem_malloc_size)); + tmp = expect_alloc_fail(realloc(p, mem_malloc_size)); + free(tmp); - if (0xf0000000 > mem_malloc_size) - expect_alloc_fail((tmp = realloc(p, 0xf0000000))); + if (0xf0000000 > mem_malloc_size) { + tmp = expect_alloc_fail(realloc(p, RELOC_HIDE(0xf0000000, 0))); + free(tmp); + } - expect_alloc_fail(tmp = realloc(p, SIZE_MAX)); + tmp = expect_alloc_fail(realloc(p, RELOC_HIDE(SIZE_MAX, 0))); + free(tmp); } else { skipped_tests += 3; @@ -83,9 +124,12 @@ static void test_malloc(void) free(p); - expect_alloc_ok(p = malloc(0)); - expect_alloc_ok(tmp = malloc(0)); + p = expect_alloc_ok(malloc(0)); + tmp = expect_alloc_ok(malloc(0)); + + __expect_cond(p != tmp, true, "allocate distinct 0-size buffers", __func__, __LINE__); - __expect(p != tmp, true, "allocate distinct 0-size buffers", __func__, __LINE__); + free(p); + free(tmp); } bselftest(core, test_malloc); diff --git a/test/self/mmu.c b/test/self/mmu.c new file mode 100644 index 0000000000..60bc9b38e8 --- /dev/null +++ b/test/self/mmu.c @@ -0,0 +1,271 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <common.h> +#include <bselftest.h> +#include <mmu.h> +#include <memtest.h> +#include <abort.h> +#include <zero_page.h> +#include <linux/sizes.h> +#include <efi/efi-mode.h> +#include <memory.h> + +#define TEST_BUFFER_SIZE SZ_1M +#define TEST_BUFFER_ALIGN SZ_4K + +BSELFTEST_GLOBALS(); + +#define __expect(ret, cond, fmt, ...) do { \ + bool __cond = (cond); \ + int __ret = (ret); \ + total_tests++; \ + \ + if (!__cond) { \ + failed_tests++; \ + printf("%s:%d error %pe: " fmt "\n", \ + __func__, __LINE__, ERR_PTR(__ret), ##__VA_ARGS__); \ + } \ +} while (0) + +#define expect_success(ret, ...) __expect((ret), ((ret) >= 0), __VA_ARGS__) + +static void memtest(void __iomem *start, size_t size, const char *desc) +{ + int ret; + + ret = mem_test_bus_integrity((resource_size_t)start, + (resource_size_t)start + size - 1, 0); + expect_success(ret, "%s bus test", desc); + + ret = mem_test_moving_inversions((resource_size_t)start, + (resource_size_t)start + size - 1, 0); + expect_success(ret, "%s moving inverstions test", desc); +} + +static inline int __check_mirroring(void __iomem *a, void __iomem *b, bool is_mirror, + const char *func, int line) +{ + if ((readl(a) == readl(b)) == (is_mirror)) + return 0; + + printf("%s:%d: mirroring unexpectedly %s: (*%p = 0x%x) %s (*%p = 0x%x)\n", func, line, + is_mirror ? "failed" : "succeeded", + a, readl(a), is_mirror ? "!=" : "==", b, readl(b)); + + mmuinfo(a); + mmuinfo(b); + + return -EILSEQ; +} + +#define check_mirroring(a, b, is_mirror) \ + __check_mirroring((a), (b), (is_mirror), __func__, __LINE__) + +static void test_remap(void) +{ + u8 __iomem *buffer = NULL, *mirror = NULL; + phys_addr_t buffer_phys; + int i, ret; + + buffer = memalign(TEST_BUFFER_ALIGN, TEST_BUFFER_SIZE); + if (WARN_ON(!buffer)) + goto out; + + buffer_phys = virt_to_phys(buffer); + + mirror = memalign(TEST_BUFFER_ALIGN, TEST_BUFFER_SIZE); + if (WARN_ON(!mirror)) + goto out; + + pr_debug("allocated buffer = 0x%p, mirror = 0x%p\n", buffer, mirror); + + memtest(buffer, TEST_BUFFER_SIZE, "cached buffer"); + memtest(mirror, TEST_BUFFER_SIZE, "cached mirror"); + + if (!arch_can_remap()) { + skipped_tests += 18; + goto out; + } + + ret = remap_range(buffer, TEST_BUFFER_SIZE, MAP_UNCACHED); + memtest(buffer, TEST_BUFFER_SIZE, "uncached buffer"); + + ret = remap_range(mirror, TEST_BUFFER_SIZE, MAP_UNCACHED); + memtest(mirror, TEST_BUFFER_SIZE, "uncached mirror"); + + for (i = 0; i < TEST_BUFFER_SIZE; i += sizeof(u32)) { + int m = i, b = i; + writel(0xDEADBEEF, &mirror[m]); + writel(i, &buffer[b]); + ret = check_mirroring(&mirror[m], &buffer[b], false); + if (ret) + break; + } + + expect_success(ret, "asserting no mirror before remap"); + + ret = arch_remap_range(mirror, buffer_phys, TEST_BUFFER_SIZE, MAP_UNCACHED); + expect_success(ret, "remapping with mirroring"); + + for (i = 0; i < TEST_BUFFER_SIZE; i += sizeof(u32)) { + int m = i, b = i; + writel(0xDEADBEEF, &mirror[m]); + writel(i, &buffer[b]); + ret = check_mirroring(&mirror[m], &buffer[b], true); + if (ret) + break; + } + + expect_success(ret, "asserting mirroring after remap"); + + ret = arch_remap_range(mirror, buffer_phys + SZ_4K, + TEST_BUFFER_SIZE / 2, MAP_UNCACHED); + expect_success(ret, "remapping with mirroring (phys += 4K)"); + + for (i = 0; i < TEST_BUFFER_SIZE / 2; i += sizeof(u32)) { + int m = i, b = i + SZ_4K; + writel(0xDEADBEEF, &mirror[m]); + writel(i, &buffer[b]); + ret = check_mirroring(&mirror[m], &buffer[b], true); + if (ret) + break; + } + + expect_success(ret, "asserting mirroring after remap (phys += 4K)"); + + ret = arch_remap_range(mirror + SZ_4K, buffer_phys, + TEST_BUFFER_SIZE / 2, MAP_UNCACHED); + expect_success(ret, "remapping with mirroring (virt += 4K)"); + + for (i = 0; i < TEST_BUFFER_SIZE / 2; i += sizeof(u32)) { + int m = i + SZ_4K, b = i; + writel(0xDEADBEEF, &mirror[m]); + writel(i, &buffer[b]); + ret = check_mirroring(&mirror[m], &buffer[b], true); + if (ret) + break; + } + + expect_success(ret, "asserting mirroring after remap (virt += 4K)"); + + ret = remap_range(buffer, TEST_BUFFER_SIZE, MAP_DEFAULT); + expect_success(ret, "remapping buffer with default attrs"); + memtest(buffer, TEST_BUFFER_SIZE, "newly cached buffer"); + + ret = remap_range(mirror, TEST_BUFFER_SIZE, MAP_DEFAULT); + expect_success(ret, "remapping mirror with default attrs"); + memtest(mirror, TEST_BUFFER_SIZE, "newly cached mirror"); + + for (i = 0; i < TEST_BUFFER_SIZE; i += sizeof(u32)) { + int m = i, b = i; + writel(0xDEADBEEF, &mirror[m]); + writel(i, &buffer[b]); + ret = check_mirroring(&mirror[m], &buffer[b], false); + if (ret) + break; + } + + expect_success(ret, "asserting no mirror after remap restore"); +out: + free(buffer); + free(mirror); +} + +static bool zero_page_access_ok(void) +{ + struct memory_bank *bank; + const char *reason; + struct resource null_res = { + .name = "null", + .flags = IORESOURCE_MEM, + .start = 0, + .end = sizeof(int) - 1, + }; + + if (!IS_ENABLED(CONFIG_ARCH_HAS_ZERO_PAGE)) { + reason = "CONFIG_ARCH_HAS_ZERO_PAGE=n"; + goto not_ok; + } + + for_each_memory_bank(bank) { + if (resource_contains(bank->res, &null_res)) + return true; + } + + reason = "no memory at address zero"; + +not_ok: + pr_info("skipping rest of zero page tests because %s\n", reason); + return false; +} + +static void test_zero_page(void) +{ + void __iomem *null = NULL; + + total_tests += 3; + + if (!IS_ENABLED(CONFIG_ARCH_HAS_DATA_ABORT_MASK)) { + pr_info("skipping %s because CONFIG_ARCH_HAS_DATA_ABORT_MASK=n\n", + __func__); + skipped_tests += 3; + return; + } + + OPTIMIZER_HIDE_VAR(null); + + /* Check if *NULL traps and data_abort_mask works */ + + data_abort_mask(); + + (void)readl(null); + + if (!data_abort_unmask()) { + printf("%s: NULL pointer access did not trap\n", __func__); + failed_tests++; + } + + if (!zero_page_access_ok()) { + skipped_tests += 2; + return; + } + + /* Check if zero_page_access() works */ + + data_abort_mask(); + + zero_page_access(); + (void)readl(null); + zero_page_faulting(); + + if (data_abort_unmask()) { + printf("%s: unexpected fault on zero page access\n", __func__); + failed_tests++; + } + + /* Check if zero_page_faulting() works */ + + data_abort_mask(); + + (void)readl(null); + + if (!data_abort_unmask()) { + printf("%s NULL pointer access did not trap\n", __func__); + failed_tests++; + } +} + +static void test_mmu(void) +{ + if (efi_is_payload()) { + pr_info("MMU was not initialized by us\n"); + skipped_tests += 23; + return; + } + + test_zero_page(); + test_remap(); +} +bselftest(core, test_mmu); diff --git a/test/self/of_manipulation.c b/test/self/of_manipulation.c index 1bcd593c86..8d645b1137 100644 --- a/test/self/of_manipulation.c +++ b/test/self/of_manipulation.c @@ -57,16 +57,27 @@ static void test_of_basics(struct device_node *root) of_property_write_bool(node1, "property1", true); assert_equal(node1, node2); + + of_property_write_bool(node2, "property1", false); + of_property_write_u32(node2, "property1", 1); + of_property_write_u32(node2, "property2", 2); + + of_property_write_u32(node1, "property3", 1); + of_copy_property(node2, "property2", node1); + of_rename_property(node1, "property3", "property1"); + + assert_equal(node1, node2); } static void test_of_property_strings(struct device_node *root) { - struct device_node *np1, *np2, *np3; + struct device_node *np1, *np2, *np3, *np4; char properties[] = "ayy\0bee\0sea"; np1 = of_new_node(root, "np1"); np2 = of_new_node(root, "np2"); np3 = of_new_node(root, "np3"); + np4 = of_new_node(root, "np4"); of_property_sprintf(np1, "property-single", "%c%c%c", 'a', 'y', 'y'); @@ -89,6 +100,14 @@ static void test_of_property_strings(struct device_node *root) of_set_property(np1, "property-multi", properties, sizeof(properties) - 1, 0); assert_different(np1, np2, 1); + + of_append_property(np4, "property-single", "ayy", 4); + + of_append_property(np4, "property-multi", "bee", 4); + of_append_property(np4, "property-multi", "sea", 4); + of_prepend_property(np4, "property-multi", "ayy", 4); + + assert_equal(np3, np4); } static void __init test_of_manipulation(void) @@ -102,6 +121,8 @@ static void __init test_of_manipulation(void) expected = of_unflatten_dtb(__dtb_of_manipulation_start, __dtb_of_manipulation_end - __dtb_of_manipulation_start); + if (WARN_ON(IS_ERR(expected))) + return; assert_equal(root, expected); diff --git a/test/self/of_manipulation.dts b/test/self/of_manipulation.dts index 3b690bb7f0..2cc6773fa9 100644 --- a/test/self/of_manipulation.dts +++ b/test/self/of_manipulation.dts @@ -4,12 +4,14 @@ / { node1 { - property1; + property1 = <1>; + property2 = <2>; node21 { }; }; node2 { - property1; + property1 = <1>; + property2 = <2>; node21 { }; }; @@ -27,4 +29,9 @@ property-single = "ayy"; property-multi = "ayy", "bee", "sea"; }; + + np4 { + property-single = "ayy"; + property-multi = "ayy", "bee", "sea"; + }; }; diff --git a/test/self/printf.c b/test/self/printf.c index 7a74660868..eae40ed242 100644 --- a/test/self/printf.c +++ b/test/self/printf.c @@ -305,6 +305,22 @@ test_pointer(void) errptr(); } +static void __init +test_jsonpath(void) +{ + if (!IS_ENABLED(CONFIG_JSMN)) { + pr_info("skipping jsonpath tests: CONFIG_JSMN disabled in config\n"); + skipped_tests += 5; + return; + } + + test("<NULL>", "%pJP", NULL); + test("$", "%pJP", &(char *[]){ NULL }); + test("$.1", "%pJP", &(char *[]){ "1", NULL }); + test("$.1.23", "%pJP", &(char *[]){ "1", "23", NULL }); + test("$.1.23.456", "%pJP", &(char *[]){ "1", "23", "456", NULL }); +} + static void __init test_printf(void) { alloced_buffer = malloc(BUF_SIZE + 2*PAD_SIZE); @@ -317,6 +333,7 @@ static void __init test_printf(void) test_string(); test_pointer(); test_hexstr(); + test_jsonpath(); free(alloced_buffer); } diff --git a/test/self/ramfs.c b/test/self/ramfs.c new file mode 100644 index 0000000000..1bc2b3b068 --- /dev/null +++ b/test/self/ramfs.c @@ -0,0 +1,213 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <common.h> +#include <fcntl.h> +#include <fs.h> +#include <dirent.h> +#include <libfile.h> +#include <sys/stat.h> +#include <unistd.h> +#include <bselftest.h> +#include <linux/sizes.h> + +BSELFTEST_GLOBALS(); + +#define __expect(ret, cond, fmt, ...) ({ \ + bool __cond = (cond); \ + int __ret = (ret); \ + total_tests++; \ + \ + if (!__cond) { \ + failed_tests++; \ + printf("%s:%d error %pe: " fmt "\n", \ + __func__, __LINE__, ERR_PTR(__ret), ##__VA_ARGS__); \ + } \ + __cond; \ +}) + +static inline int ptr_to_err(const void *ptr) +{ + if (ptr) + return PTR_ERR_OR_ZERO(ptr); + + return -errno ?: -EFAULT; +} + +#define expect_success(ret, ...) __expect((ret), (ret) >= 0, __VA_ARGS__) +#define expect_ptrok(ptr, ...) expect_success(ptr_to_err(ptr), __VA_ARGS__) +#define expect_fail(ret, ...) __expect((ret), (ret) < 0, __VA_ARGS__) + +static inline int get_file_count(int i) +{ + if (40 <= i && i < 50) + return 40; + + if (i >= 50) + i -= 10; + + /* we create a file for i == 0 as well */ + return i + 1; +} + +static void test_ramfs(void) +{ + int files[] = { 1, 3, 5, 7, 11, 13, 17 }; + char fname[128]; + char *content = NULL; + char *oldpwd = NULL; + DIR *dir = NULL; + const char *dname; + struct stat st; + int i, j, ret, fd; + struct dirent *d; + + dname = make_temp("ramfs-test"); + ret = mkdir(dname, 0777); + + if (!expect_success(ret, "creating directory")) + return; + + ret = stat(dname, &st); + if (!expect_success(ret, "stating directory")) + goto out; + + expect_success(S_ISDIR(st.st_mode) ? 0 : -ENOTDIR, + "directory check"); + + content = malloc(99); + if (WARN_ON(!content)) + goto out; + + for (i = 0; i < 99; i++) { + scnprintf(fname, sizeof(fname), "%s/file-%02d", dname, i); + + fd = open(fname, O_RDWR | O_CREAT); + if (!expect_success(fd, "creating file")) + continue; + + for (j = 0; j < i; j++) + content[j] = i; + + ret = write(fd, content, i); + expect_success(ret, "writing file"); + close(fd); + + if (40 <= i && i < 50) { + ret = unlink(fname); + expect_success(ret, "unlinking file"); + } + + ret = stat(fname, &st); + if (40 <= i && i < 50) { + expect_fail(ret, "stating file"); + } else { + expect_success(ret, "stating file"); + + expect_success(S_ISREG(st.st_mode) ? 0 : -EINVAL, + "stating file"); + } + + dir = opendir(dname); + if (!expect_ptrok(dir, "opening parent directory")) + continue; + + j = 0; + + while ((d = readdir(dir))) { + if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) + continue; + + j++; + } + + expect_success(j == get_file_count(i) ? 0 : -EINVAL, + "unexpected file count iterating directory"); + + closedir(dir); + } + + oldpwd = pushd(dname); + if (!expect_ptrok(oldpwd, "pushd()")) + goto out;; + + dir = opendir("."); + if (!expect_ptrok(dir, "opening parent directory")) + goto out; + + i = 1; + j = 0; + + while ((d = readdir(dir))) { + size_t size = 0; + char *buf; + + if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) + continue; + + buf = read_file(d->d_name, &size); + if (size) { + for (j = 0; j < size; j++) { + expect_success(buf[j] == size ? 0 : -EINVAL, + "unexpected file content"); + } + } + + scnprintf(fname, sizeof(fname), "file-%02zu", size); + + expect_success(strcmp(d->d_name, fname) == 0 ? 0 : -EINVAL, + "unexpected file content"); + + free(buf); + + /* + * For select files, we test unreaddir once and check if + * we read back same element on repeated readdir + */ + for (j = 0; j < ARRAY_SIZE(files); j++) { + if (size == files[j]) { + ret = unreaddir(dir, d); + expect_success(ret, "unreaddir"); + files[j] = -1; + break; + } + } + if (j == ARRAY_SIZE(files)) + i++; + } + + expect_success(i == 90 ? 0 : -EINVAL, + "unexpected file count iterating directory: %u", i); + + ret = make_directory("test/a/b/c/d/e/f/g/h/i/j/k/l"); + expect_success(ret, "make_directory()"); + + if (!ret) { + const char hello[] = "hello"; + char *buf; + + ret = write_file("test/a/b/c/d/e/f/g/h/i/j/k/l/FILE", + ARRAY_AND_SIZE(hello)); + expect_success(ret, "write_file()"); + + buf = read_file("test/a/b/c/d/e/f/g/h/i/j/k/l/FILE", NULL); + if (expect_ptrok(buf, "read_file()")) { + expect_success(memcmp(buf, ARRAY_AND_SIZE(hello)), + "read_file() content"); + } + } + +out: + popd(oldpwd); + free(content); + + closedir(dir); + + ret = unlink_recursive(dname, NULL); + expect_success(ret, "unlinking directory"); + + dir = opendir(dname); + expect_fail(dir ? 0 : -EISDIR, "opening removed directory"); +} +bselftest(core, test_ramfs); diff --git a/test/self/regulator.c b/test/self/regulator.c new file mode 100644 index 0000000000..bcbcbe33e1 --- /dev/null +++ b/test/self/regulator.c @@ -0,0 +1,190 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <common.h> +#include <bselftest.h> +#include <driver.h> +#include <of.h> +#include <regulator.h> +#include <linux/regulator/of_regulator.h> + +struct test_regulator { + struct device *dev; +}; + +struct test_regulator_cfg { + struct regulator_desc desc; + struct regulator_dev rdev; +}; + +BSELFTEST_GLOBALS(); + +static bool __ok(bool cond, const char *func, int line) +{ + total_tests++; + if (!cond) { + failed_tests++; + printf("%s:%d: assertion failure\n", func, line); + return false; + } + + return true; +} + +#define ok(cond) \ + __ok(cond, __func__, __LINE__) + +static void test_regulator_selfref_always_on(struct device *dev) +{ + ok(1 == 1); +} + +static int test_regulator_enable_noop(struct regulator_dev *rdev) +{ + dev_dbg(rdev->dev, "enabling %s-supply\n", rdev->desc->supply_name); + failed_tests--; + return 0; +} + +static int test_regulator_disable_noop(struct regulator_dev *rdev) +{ + dev_dbg(rdev->dev, "disabling %s-supply\n", rdev->desc->supply_name); + failed_tests--; + return 0; +} + +static const struct regulator_ops test_regulator_ops_range = { + .enable = test_regulator_enable_noop, + .disable = test_regulator_disable_noop, +}; + +enum { + /* + * Ordering LDO1 before TEST_BUCK currently fails. This needs to be fixed + */ + TEST_BUCK, + TEST_LDO1, + TEST_LDO2, + TEST_REGULATORS_NUM +}; + +static struct test_regulator_cfg test_pmic_reg[] = { + [TEST_BUCK] = {{ + .supply_name = "buck", + .ops = &test_regulator_ops_range, + }}, + [TEST_LDO1] = {{ + .supply_name = "ldo1", + .ops = &test_regulator_ops_range, + }}, + [TEST_LDO2] = {{ + .supply_name = "ldo2", + .ops = &test_regulator_ops_range, + }}, +}; + +static struct of_regulator_match test_reg_matches[] = { + [TEST_BUCK] = { .name = "BUCK", .desc = &test_pmic_reg[TEST_BUCK].desc }, + [TEST_LDO1] = { .name = "LDO1", .desc = &test_pmic_reg[TEST_LDO1].desc }, + [TEST_LDO2] = { .name = "LDO2", .desc = &test_pmic_reg[TEST_LDO2].desc }, +}; + +static int test_regulator_register(struct test_regulator *priv, int id, + struct of_regulator_match *match, + struct test_regulator_cfg *cfg) +{ + struct device *dev = priv->dev; + int ret; + + if (!match->of_node) { + dev_warn(dev, "Skip missing DTB regulator %s\n", match->name); + return 0; + } + + cfg->rdev.desc = &cfg->desc; + cfg->rdev.dev = dev; + + dev_dbg(dev, "registering %s\n", match->name); + + ret = of_regulator_register(&cfg->rdev, match->of_node); + if (ret) + return dev_err_probe(dev, ret, "failed to register %s regulator\n", + match->name); + + return 0; +} + +static int regulator_probe(struct device *dev) +{ + size_t nregulators = ARRAY_SIZE(test_pmic_reg); + struct device_node *np = dev->of_node; + struct test_regulator *priv; + int ret, i; + + priv = xzalloc(sizeof(*priv)); + priv->dev = dev; + + total_tests += 2; + failed_tests += 2; + + np = of_get_child_by_name(np, "regulators"); + if (!np) + return -ENOENT; + + ret = of_regulator_match(dev, np, test_reg_matches, nregulators); + if (ret < 0) + return ret; + + ok(ret == TEST_REGULATORS_NUM); + + for (i = 0; i < nregulators; i++) { + ret = test_regulator_register(priv, i, &test_reg_matches[i], + &test_pmic_reg[i]); + ok(ret == 0); + if (ret < 0) + return ret; + } + + return 0; +} + +static const struct of_device_id test_regulator_of_match[] = { + { .compatible = "barebox,regulator-self-test" }, + { /* sentintel */ }, +}; +MODULE_DEVICE_TABLE(of, test_regulator_of_match); + +static struct driver regulator_test_driver = { + .name = "regulator-test", + .probe = regulator_probe, + .of_match_table = test_regulator_of_match, +}; + +static struct device *dev; + +static void test_regulator(void) +{ + extern char __dtbo_test_regulator_start[]; + struct device_node *overlay; + int ret; + + if (!dev) { + ret = platform_driver_register(®ulator_test_driver); + if (ret) + return; + + overlay = of_unflatten_dtb(__dtbo_test_regulator_start, INT_MAX); + if (WARN_ON(IS_ERR(overlay))) + return; + of_overlay_apply_tree(of_get_root_node(), overlay); + of_probe(); + + dev = of_find_device_by_node_path("/regulator-self-test-pmic"); + + ok(dev->driver != NULL); + } + + test_regulator_selfref_always_on(dev); +} +bselftest(core, test_regulator); diff --git a/test/self/setjmp.c b/test/self/setjmp.c new file mode 100644 index 0000000000..1cbb44bdf9 --- /dev/null +++ b/test/self/setjmp.c @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: MIT +// SPDX-FileCopyrightText: Copyright (C) 2003 Hewlett-Packard +/* + * Code contributed by David Mosberger-Tang <davidm@hpl.hp.com> + * to libunwind - a platform-independent unwind library + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <common.h> +#include <bselftest.h> +#include <string.h> +#include <asm/setjmp.h> + +BSELFTEST_GLOBALS(); + +static __noreturn void raise_longjmp(jmp_buf jbuf, int i, int n) +{ + while (i < n) + raise_longjmp(jbuf, i + 1, n); + + longjmp(jbuf, n); +} + +static jmp_buf jbuf; + +static void __noreturn initjmp_entry(void) +{ + volatile u32 arr[256]; + int i; + + for (i = 0; i < ARRAY_SIZE(arr); i++) + writel(i, &arr[i]); + + /* ensure arr[] is allocated on stack */ + OPTIMIZER_HIDE_VAR(i); + if (i == 0) + initjmp_entry(); + + longjmp(jbuf, 0x1337); +} + +static void test_setjmp(void) +{ + void *stack; + jmp_buf ijbuf; + volatile int i; + int ret; + + total_tests += 20; + + for (i = 0; i < 10; ++i) { + if ((ret = setjmp(jbuf))) { + pr_debug("%s: secondary setjmp() return, ret=%d\n", + __func__, ret); + + if (ret != i + 1) { + printf("%s: setjmp() returned %d, expected %d\n", + __func__, ret, i + 1); + failed_tests += 2; + } + continue; + } + + pr_debug("%s.%d: done with setjmp(); calling children\n", + __func__, i + 1); + + raise_longjmp(jbuf, 0, i + 1); + + printf("%s: raise_longjmp() returned unexpectedly\n", __func__); + failed_tests++; + } + + stack = memalign(16, CONFIG_STACK_SIZE); + if (WARN_ON(!stack)) { + skipped_tests++; + return; + } + + pr_debug("%s: testing initjmp\n", __func__); + + total_tests += 1; + + /* initialize new context with fresh stack */ + initjmp(ijbuf, initjmp_entry, stack + CONFIG_STACK_SIZE); + + switch (setjmp(jbuf)) { + case 0x1337: + break; + case 0: + longjmp(ijbuf, 0x42); + default: + failed_tests++; + } + + free(stack); +} +bselftest(core, test_setjmp); diff --git a/test/self/string.c b/test/self/string.c new file mode 100644 index 0000000000..d33e2d7918 --- /dev/null +++ b/test/self/string.c @@ -0,0 +1,203 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <common.h> +#include <bselftest.h> +#include <string.h> + +BSELFTEST_GLOBALS(); + +static const char *strverscmp_expect_str(int expect) +{ + switch (expect) { + case -1: return "<"; + case 0: return "=="; + case 1: return ">"; + default: return "?!"; + } +} + +static int strverscmp_assert_one(const char *lhs, const char *rhs, int expect) +{ + int actual; + + total_tests++; + + actual = strverscmp(lhs, rhs); + if (actual != expect) { + failed_tests++; + printf("(%s %s %s), but (%s %s %s) expected\n", + lhs, strverscmp_expect_str(actual), rhs, + lhs, strverscmp_expect_str(expect), rhs); + } + + return actual; +} + +static int __strverscmp_assert(char *expr) +{ + const char *token, *tokens[3]; + int expect = -42; + int i = 0; + + while ((token = strsep_unescaped(&expr, " "))) { + if (i == 3) { + pr_err("invalid expression\n"); + return -EILSEQ; + } + + tokens[i++] = token; + } + + if (!strcmp(tokens[1], "<")) + expect = -1; + else if (!strcmp(tokens[1], "==")) + expect = 0; + else if (!strcmp(tokens[1], ">")) + expect = 1; + + return strverscmp_assert_one(tokens[0], tokens[2], expect); +} + +#define strverscmp_assert(expr) ({ \ + char __expr_mut[] = expr; \ + __strverscmp_assert(__expr_mut); \ +}) + +static void test_strverscmp_spec_examples(void) +{ + /* + * Taken from specification at + * https://uapi-group.org/specifications/specs/version_format_specification/#examples + */ + strverscmp_assert("11 == 11"); + strverscmp_assert("systemd-123 == systemd-123"); + strverscmp_assert("bar-123 < foo-123"); + strverscmp_assert("123a > 123"); + strverscmp_assert("123.a > 123"); + strverscmp_assert("123.a < 123.b"); + strverscmp_assert("123a > 123.a"); + strverscmp_assert("11α == 11β"); + strverscmp_assert("A < a"); + strverscmp_assert_one("", "0", -1); + strverscmp_assert("0. > 0"); + strverscmp_assert("0.0 > 0"); + strverscmp_assert("0 > ~"); + strverscmp_assert_one("", "~", 1); + strverscmp_assert("1_ == 1"); + strverscmp_assert("_1 == 1"); + strverscmp_assert("1_ < 1.2"); + strverscmp_assert("1_2_3 > 1.3.3"); + strverscmp_assert("1+ == 1"); + strverscmp_assert("+1 == 1"); + strverscmp_assert("1+ < 1.2"); + strverscmp_assert("1+2+3 > 1.3.3"); +} + +static void test_strverscmp_one(const char *newer, const char *older) +{ + strverscmp_assert_one(newer, newer, 0); + strverscmp_assert_one(newer, older, 1); + strverscmp_assert_one(older, newer, -1); + strverscmp_assert_one(older, older, 0); +} + +static void test_strverscmp_spec_systemd(void) +{ + /* + * Taken from systemd tests at + * 87b7d9b6ff23ec10b66bf53efeabf16ad85d7ad8 + */ + static const char * const versions[] = { + "~1", "", "ab", "abb", "abc", "0001", "002", "12", "122", "122.9", + "123~rc1", "123", "123-a", "123-a.1", "123-a1", "123-a1.1", "123-3", + "123-3.1", "123^patch1", "123^1", "123.a-1" "123.1-1", "123a-1", "124", + NULL, + }; + const char * const *p, * const *q; + + for (p = versions; *p; p++) + for (q = p + 1; *q; q++) + test_strverscmp_one(*q, *p); + + test_strverscmp_one("123.45-67.89", "123.45-67.88"); + test_strverscmp_one("123.45-67.89a", "123.45-67.89"); + test_strverscmp_one("123.45-67.89", "123.45-67.ab"); + test_strverscmp_one("123.45-67.89", "123.45-67.9"); + test_strverscmp_one("123.45-67.89", "123.45-67"); + test_strverscmp_one("123.45-67.89", "123.45-66.89"); + test_strverscmp_one("123.45-67.89", "123.45-9.99"); + test_strverscmp_one("123.45-67.89", "123.42-99.99"); + test_strverscmp_one("123.45-67.89", "123-99.99"); + + /* '~' : pre-releases */ + test_strverscmp_one("123.45-67.89", "123~rc1-99.99"); + test_strverscmp_one("123-45.67.89", "123~rc1-99.99"); + test_strverscmp_one("123~rc2-67.89", "123~rc1-99.99"); + test_strverscmp_one("123^aa2-67.89", "123~rc1-99.99"); + test_strverscmp_one("123aa2-67.89", "123~rc1-99.99"); + + /* '-' : separator between version and release. */ + test_strverscmp_one("123.45-67.89", "123-99.99"); + test_strverscmp_one("123^aa2-67.89", "123-99.99"); + test_strverscmp_one("123aa2-67.89", "123-99.99"); + + /* '^' : patch releases */ + test_strverscmp_one("123.45-67.89", "123^45-67.89"); + test_strverscmp_one("123^aa2-67.89", "123^aa1-99.99"); + test_strverscmp_one("123aa2-67.89", "123^aa2-67.89"); + + /* '.' : point release */ + test_strverscmp_one("123aa2-67.89", "123.aa2-67.89"); + test_strverscmp_one("123.ab2-67.89", "123.aa2-67.89"); + + /* invalid characters */ + strverscmp_assert_one("123_aa2-67.89", "123aa+2-67.89", 0); +} + +static void test_strverscmp(void) +{ + test_strverscmp_spec_examples(); + test_strverscmp_spec_systemd(); + + /* and now some corner cases */ + strverscmp_assert_one(NULL, NULL, 0); + strverscmp_assert_one(NULL, "", 0); + strverscmp_assert_one("", NULL, 0); + strverscmp_assert_one("", "", 0); +} + +static void __expect_streq(const char *func, int line, + char *is, const char *expect, bool free_is) +{ + total_tests++; + if (strcmp(is, expect)) { + failed_tests++; + printf("%s:%d: got %s, but %s expected\n", func, line, is, expect); + } + + if (free_is) + free(is); +} + +#define expect_dynstreq(args...) \ + __expect_streq(__func__, __LINE__, args, true) + +static void test_strjoin(void) +{ + char *strs[] = { "ayy", "bee", "cee" }; + + expect_dynstreq(strjoin("", strs, ARRAY_SIZE(strs)), "ayybeecee"); + expect_dynstreq(strjoin(" ", strs, ARRAY_SIZE(strs)), "ayy bee cee"); + expect_dynstreq(strjoin(", ", strs, ARRAY_SIZE(strs)), "ayy, bee, cee"); + expect_dynstreq(strjoin(" ", strs, 1), "ayy"); + expect_dynstreq(strjoin(" ", NULL, 0), ""); +} + +static void test_string(void) +{ + test_strverscmp(); + test_strjoin(); +} +bselftest(parser, test_string); diff --git a/test/self/test_command.c b/test/self/test_command.c new file mode 100644 index 0000000000..358855d0f6 --- /dev/null +++ b/test/self/test_command.c @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <bselftest.h> +#include <command.h> + +BSELFTEST_GLOBALS(); + +#if __LONG_MAX__ == 0x7fffffff +#define LONG_MIN_STR "-2147483648" +#define LONG_MIN_PLUS_1_STR "-2147483647" +#define LONG_MAX_STR "2147483647" +#define LONG_MAX_PLUS_1_STR "2147483648" +#else +#define LONG_MIN_STR "-9223372036854775808" +#define LONG_MIN_PLUS_1_STR "-9223372036854775807" +#define LONG_MAX_STR "9223372036854775807" +#define LONG_MAX_PLUS_1_STR "9223372036854775808" +#endif + +static void __assert_eq(const char *expr, bool result, const char *func, int line) +{ + int ret; + + total_tests++; + + ret = run_command(expr); + if ((result && ret != 0) || (!result && ret != 1)) { + failed_tests++; + printf("%s:%d: %s: assertion failure, ret=%d\n", func, line, expr, ret); + } +} + +#define assert_eq(expr, result) __assert_eq(expr, result, __func__, __LINE__) + +static void test_test_command(void) +{ + assert_eq("[ 0 -eq 0 ]", true); + assert_eq("[ 0 -eq 1 ]", false); + assert_eq("[ -1 -le 1 ]", true); + assert_eq("[ -1 -ge -5 ]", true); + + assert_eq("[ " LONG_MIN_STR " -lt " LONG_MAX_STR " ]", true); + assert_eq("[ " LONG_MIN_STR " -gt " LONG_MAX_STR " ]", false); + + assert_eq("[ " LONG_MIN_STR " -eq " LONG_MIN_STR " ]", true); + assert_eq("[ " LONG_MAX_STR " -eq " LONG_MAX_STR " ]", true); + assert_eq("[ " LONG_MIN_STR " -ne " LONG_MIN_STR " ]", false); + assert_eq("[ " LONG_MAX_STR " -ne " LONG_MAX_STR " ]", false); + + assert_eq("[ " LONG_MIN_PLUS_1_STR " -eq " LONG_MIN_PLUS_1_STR " ]", true); + assert_eq("[ " LONG_MAX_PLUS_1_STR " -eq " LONG_MAX_PLUS_1_STR " ]", true); + assert_eq("[ " LONG_MIN_PLUS_1_STR " -ne " LONG_MIN_PLUS_1_STR " ]", false); + assert_eq("[ " LONG_MAX_PLUS_1_STR " -ne " LONG_MAX_PLUS_1_STR " ]", false); + + assert_eq("[ " LONG_MIN_PLUS_1_STR " -gt " LONG_MIN_STR " ]", true); + assert_eq("[ " LONG_MAX_PLUS_1_STR " -gt " LONG_MAX_STR " ]", true); + assert_eq("[ " LONG_MIN_PLUS_1_STR " -lt " LONG_MIN_STR " ]", false); + assert_eq("[ " LONG_MAX_PLUS_1_STR " -lt " LONG_MAX_STR " ]", false); + assert_eq("[ " LONG_MIN_STR " -le " LONG_MIN_PLUS_1_STR " ]", true); + assert_eq("[ " LONG_MAX_STR " -le " LONG_MAX_PLUS_1_STR " ]", true); +} +bselftest(core, test_test_command); diff --git a/test/self/test_regulator.dtso b/test/self/test_regulator.dtso new file mode 100644 index 0000000000..65d2b13098 --- /dev/null +++ b/test/self/test_regulator.dtso @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +/dts-v1/; +/plugin/; + +&{/} { + regulator_test_fixed: regulator-self-test-fixed { + compatible = "regulator-fixed"; + regulator-name = "test_fixed"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + }; + + regulator-self-test-pmic { + compatible = "barebox,regulator-self-test"; + + buck-supply = <®ulator_test_fixed>; + ldo1-supply = <&test_pmic_buck>; + ldo2-supply = <&test_pmic_buck>; + + regulators { + test_pmic_buck: BUCK { + regulator-name = "buck"; + regulator-min-microvolt = <330000>; + regulator-max-microvolt = <330000>; + }; + + test_pmic_ldo1: LDO1 { + regulator-name = "ldo1"; + regulator-always-on; + regulator-boot-on; + regulator-min-microvolt = <180000>; + regulator-max-microvolt = <180000>; + }; + + test_pmic_ldo2: LDO2 { + regulator-name = "ldo2"; + regulator-min-microvolt = <180000>; + regulator-max-microvolt = <180000>; + }; + }; + }; +}; diff --git a/test/strategy.py b/test/strategy.py index 1fe1b7d818..8aa58151f6 100644 --- a/test/strategy.py +++ b/test/strategy.py @@ -1,14 +1,21 @@ import enum import attr +import pytest +import subprocess +import os +import shutil +import sys -from labgrid import target_factory, step +from labgrid import target_factory, step, driver from labgrid.strategy import Strategy, StrategyError class Status(enum.Enum): unknown = 0 off = 1 barebox = 2 + qemu_dry_run = 3 + qemu_interactive = 4 @target_factory.reg_driver @attr.s(eq=False) @@ -21,9 +28,13 @@ class BareboxTestStrategy(Strategy): } status = attr.ib(default=Status.unknown) + qemu = None def __attrs_post_init__(self): super().__attrs_post_init__() + if isinstance(self.console, driver.QEMUDriver): + self.qemu = self.console + self.patchtools() @step(args=['status']) def transition(self, status, *, step): @@ -51,3 +62,77 @@ class BareboxTestStrategy(Strategy): format(self.status, status) ) self.status = status + + def force(self, state): + self.transition(Status.off) # pylint: disable=missing-kwoa + + if state == "qemu_dry_run" or state == "qemu_interactive": + cmd = self.get_qemu_base_args() + + cmd.append("-serial") + cmd.append("mon:stdio") + cmd.append("-trace") + cmd.append("file=/dev/null") + + with open("/dev/tty", "r+b", buffering=0) as tty: + tty.write(bytes("running: \n{}\n".format(quote_cmd(cmd)), "UTF-8")) + if state == "qemu_dry_run": + pytest.exit('Dry run. Terminating.') + subprocess.run(cmd, stdin=tty, stdout=tty, stderr=tty) + + pytest.exit('Interactive session terminated') + else: + pytest.exit('Can only force to: qemu_dry_run, qemu_interactive') + + def get_qemu_base_args(self): + if self.qemu is None: + pytest.exit('interactive mode only supported with QEMUDriver') + + try: + # https://github.com/labgrid-project/labgrid/pull/1212 + cmd = self.qemu.get_qemu_base_args() + except AttributeError: + self.qemu.on_activate() + orig = self.qemu._cmd + cmd = [] + + list_iter = enumerate(orig) + for i, opt in list_iter: + if opt == "-S": + continue + opt2 = double_opt(opt, orig, i) + if (opt2.startswith("-chardev socket,id=serialsocket") or + opt2 == "-serial chardev:serialsocket" or + opt2 == "-qmp stdio"): + # skip over two elements at once + next(list_iter, None) + continue + + cmd.append(opt) + + return cmd + + def patchtools(self): + # https://github.com/labgrid-project/labgrid/commit/69fd553c6969526b609d0be6bb81f0c35f08d1d0 + if self.qemu is None: + return + + if 'tools' not in self.target.env.config.data: + self.target.env.config.data['tools'] = {} + self.target.env.config.data["tools"][self.qemu.qemu_bin] = \ + shutil.which(self.qemu.qemu_bin) + + def append_qemu_args(self, *args): + if self.qemu is None: + pytest.exit('Qemu option supplied for non-Qemu target') + for arg in args: + self.console.extra_args += " " + arg + +def quote_cmd(cmd): + quoted = map(lambda s : s if s.find(" ") == -1 else "'" + s + "'", cmd) + return " ".join(quoted) + +def double_opt(opt, orig, i): + if opt == orig[-1]: + return opt + return " ".join([opt, orig[i + 1]]) diff --git a/test/x86/pc@efi_defconfig.yaml b/test/x86/pc@efi_defconfig.yaml index 280f5dcee9..ae06fbf697 100644 --- a/test/x86/pc@efi_defconfig.yaml +++ b/test/x86/pc@efi_defconfig.yaml @@ -2,13 +2,12 @@ targets: main: drivers: QEMUDriver: - qemu_bin: qemu + qemu_bin: qemu-system-x86_64 machine: pc cpu: Nehalem memory: 1024M kernel: barebox.efi - bios: OVMF.fd - extra_args: '' + extra_args: '-bios OVMF.fd' BareboxDriver: prompt: 'barebox@[^:]+:[^ ]+ ' bootstring: 'commandline:' @@ -20,12 +19,7 @@ targets: kconfig_add: - CONFIG_DRIVER_SERIAL_NS16550=y - CONFIG_CONSOLE_ACTIVATE_FIRST=y # avoid duplicate output - download: - OVMF.fd: /usr/share/qemu/OVMF.fd images: barebox.efi: !template "$LG_BUILDDIR/barebox.efi" - OVMF.fd: !template "$LG_BUILDDIR/OVMF.fd" -tools: - qemu: /usr/bin/qemu-system-x86_64 imports: - ../strategy.py diff --git a/test/x86/q35@efi_defconfig.yaml b/test/x86/q35@efi_defconfig.yaml index dcb3f604c0..578a3654fe 100644 --- a/test/x86/q35@efi_defconfig.yaml +++ b/test/x86/q35@efi_defconfig.yaml @@ -2,13 +2,12 @@ targets: main: drivers: QEMUDriver: - qemu_bin: qemu + qemu_bin: qemu-system-x86_64 machine: q35 cpu: Nehalem memory: 1024M kernel: barebox.efi - bios: OVMF.fd - extra_args: -global ICH9-LPC.noreboot=false + extra_args: '-bios OVMF.fd -global ICH9-LPC.noreboot=false' BareboxDriver: prompt: 'barebox@[^:]+:[^ ]+ ' bootstring: 'commandline:' @@ -20,12 +19,7 @@ targets: kconfig_add: - CONFIG_DRIVER_SERIAL_NS16550=y - CONFIG_CONSOLE_ACTIVATE_FIRST=y # avoid duplicate output - download: - OVMF.fd: /usr/share/qemu/OVMF.fd images: barebox.efi: !template "$LG_BUILDDIR/barebox.efi" - OVMF.fd: !template "$LG_BUILDDIR/OVMF.fd" -tools: - qemu: /usr/bin/qemu-system-x86_64 imports: - ../strategy.py diff --git a/test/x86/virtio@efi_defconfig.yaml b/test/x86/virtio@efi_defconfig.yaml index 326fcbc689..83340d19dd 100644 --- a/test/x86/virtio@efi_defconfig.yaml +++ b/test/x86/virtio@efi_defconfig.yaml @@ -2,13 +2,12 @@ targets: main: drivers: QEMUDriver: - qemu_bin: qemu + qemu_bin: qemu-system-x86_64 machine: pc cpu: Nehalem memory: 1024M kernel: barebox.efi - bios: OVMF.fd - extra_args: '' + extra_args: '-bios OVMF.fd' BareboxDriver: prompt: 'barebox@[^:]+:[^ ]+ ' bootstring: 'commandline:' @@ -21,12 +20,7 @@ targets: - test/kconfig/virtio-pci.cfg - CONFIG_DRIVER_SERIAL_NS16550=y - CONFIG_CONSOLE_ACTIVATE_FIRST=y # avoid duplicate output - download: - OVMF.fd: /usr/share/qemu/OVMF.fd images: barebox.efi: !template "$LG_BUILDDIR/barebox.efi" - OVMF.fd: !template "$LG_BUILDDIR/OVMF.fd" -tools: - qemu: /usr/bin/qemu-system-x86_64 imports: - ../strategy.py |