#!/usr/bin/env bash if [ -z "${BASH_VERSINFO[0]}" ] || [ "${BASH_VERSINFO[0]}" -lt 4 ] ; then echo "Error: Bash version 4 or newer is required for the kvm-unit-tests" exit 1 fi # Return the default CPU type to compile for function get_default_processor() { local arch="$1" case "$arch" in "arm") echo "cortex-a15" ;; "arm64") echo "cortex-a57" ;; *) echo "$arch" ;; esac } # Return the default CPU type to run on function get_default_qemu_cpu() { local arch="$1" case "$arch" in "arm") echo "cortex-a15" ;; "arm64") echo "cortex-a57" ;; "riscv32" | "riscv64") echo "max" ;; esac } srcdir=$(cd "$(dirname "$0")"; pwd) prefix=/usr/local cc=gcc cflags= ld=ld objcopy=objcopy objdump=objdump readelf=readelf ar=ar addr2line=addr2line arch=$(uname -m | sed -e 's/i.86/i386/;s/arm64/aarch64/;s/arm.*/arm/;s/ppc64.*/ppc64/') host=$arch cross_prefix= endian="" pretty_print_stacks=yes environ_default=yes u32_long= wa_divide= target= errata_force=0 erratatxt="$srcdir/errata.txt" host_key_document= gen_se_header= enable_dump=no page_size= earlycon= console= efi= efi_direct= target_cpu= # Enable -Werror by default for git repositories only (i.e. developer builds) if [ -e "$srcdir"/.git ]; then werror=-Werror else werror= fi usage() { [ "$arch" = "aarch64" ] && arch="arm64" [ -z "$processor" ] && processor=$(get_default_processor $arch) cat <<-EOF Usage: $0 [options] Options include: --arch=ARCH architecture to compile for ($arch). ARCH can be one of: arm, arm64, i386, ppc64, riscv32, riscv64, s390x, x86_64 --processor=PROCESSOR processor to compile for ($processor) --target-cpu=CPU the CPU model to run on. If left unset, the run script selects the best value based on the host system and the test configuration. --target=TARGET target platform that the tests will be running on (qemu or kvmtool, default is qemu) (arm/arm64 only) --cross-prefix=PREFIX cross compiler prefix --cc=CC c compiler to use ($cc) --cflags=FLAGS extra options to be passed to the c compiler --ld=LD ld linker to use ($ld) --prefix=PREFIX where to install things ($prefix) --endian=ENDIAN endianness to compile for (little or big, ppc64 only) --[enable|disable]-pretty-print-stacks enable or disable pretty stack printing (enabled by default) --[enable|disable]-default-environ enable or disable the generation of a default environ when no environ is provided by the user (enabled by default) --erratatxt=FILE specify a file to use instead of errata.txt. Use '--erratatxt=' to ensure no file is used. --host-key-document=HOST_KEY_DOCUMENT Specify the machine-specific host-key document for creating a PVM image with 'genprotimg' (s390x only) --gen-se-header=GEN_SE_HEADER Provide an executable to generate a PV header requires --host-key-document. (s390x-snippets only) --[enable|disable]-dump Allow PV guests to be dumped. Requires at least z16. (s390x only) --page-size=PAGE_SIZE Specify the page size (translation granule). PAGE_SIZE can be 4k [default], 16k, 64k for arm64. 4k [default], 64k for ppc64. --earlycon=EARLYCON Specify the UART name, type and address used for the earlycon (optional). The specified address will overwrite the UART address set by the --target option. EARLYCON can be one of (case sensitive): uart[8250],mmio,ADDR Specify an 8250 compatible UART at address ADDR. Supported register stride is 8 bit only. pl011,ADDR pl011,mmio32,ADDR Specify a PL011 compatible UART at address ADDR. Supported register stride is 32 bit only. (arm/arm64 and riscv32/riscv64 only) --console=CONSOLE Specify the device used for output (optional). sbi Use SBI DBCN (riscv only) --[enable|disable]-efi Boot and run from UEFI (disabled by default, x86_64 and arm64 only) --[enable|disable]-werror Select whether to compile with the -Werror compiler flag --[enable|disable]-efi-direct Select whether to run EFI tests directly with QEMU's -kernel option. When not enabled, tests will be placed in an EFI file system and run from the UEFI shell. Ignored when efi isn't enabled and defaults to enabled when efi is enabled for riscv64. (arm64 and riscv64 only) EOF exit 1 } optno=1 argc=$# while [[ $optno -le $argc ]]; do opt="$1"; shift optno=$(( $optno + 1 )) arg= if [[ "$opt" = *=* ]]; then arg="${opt#*=}" opt="${opt%%=*}" fi case "$opt" in --prefix) prefix="$arg" ;; --arch) arch="$arg" ;; --processor) processor="$arg" ;; --target-cpu) target_cpu="$arg" ;; --target) target="$arg" ;; --cross-prefix) cross_prefix="$arg" ;; --endian) endian="$arg" ;; --cc) cc="$arg" cc_selected=yes ;; --cflags) cflags="$arg" ;; --ld) ld="$arg" ;; --enable-pretty-print-stacks) pretty_print_stacks=yes ;; --disable-pretty-print-stacks) pretty_print_stacks=no ;; --enable-default-environ) environ_default=yes ;; --disable-default-environ) environ_default=no ;; --erratatxt) erratatxt= [ "$arg" ] && erratatxt=$(eval realpath "$arg") ;; --host-key-document) host_key_document="$arg" ;; --gen-se-header) gen_se_header="$arg" ;; --enable-dump) enable_dump=yes ;; --disable-dump) enable_dump=no ;; --page-size) page_size="$arg" ;; --earlycon) earlycon="$arg" ;; --console) console="$arg" ;; --enable-efi) efi=y ;; --disable-efi) efi=n ;; --enable-efi-direct) efi_direct=y ;; --disable-efi-direct) efi_direct=n ;; --enable-werror) werror=-Werror ;; --disable-werror) werror= ;; --help) usage ;; *) echo "Unknown option '$opt'" echo usage ;; esac done if [ -z "$cc_selected" ] && [ "$cross_prefix" ]; then cc="$cross_prefix$cc" fi if [ -z "$efi" ] || [ "$efi" = "n" ]; then [ "$efi_direct" = "y" ] && efi_direct= fi if [ -n "$host_key_document" ] && [ ! -f "$host_key_document" ]; then echo "Host key document doesn't exist at the specified location." exit 1 fi if [ "$erratatxt" ] && [ ! -f "$erratatxt" ]; then echo "erratatxt: $erratatxt does not exist or is not a regular file" exit 1 fi arch_name=$arch [ "$arch" = "aarch64" ] && arch="arm64" [ "$arch_name" = "arm64" ] && arch_name="aarch64" arch_libdir=$arch if [ "$arch" = "riscv" ]; then echo "riscv32 or riscv64 must be specified" exit 1 fi if [ -z "$target" ]; then target="qemu" else if [ "$arch" != "arm64" ] && [ "$arch" != "arm" ]; then echo "--target is not supported for $arch" usage fi fi if [ "$efi" ] && [ "$arch" != "x86_64" ] && [ "$arch" != "arm64" ] && [ "$arch" != "riscv64" ]; then echo "--[enable|disable]-efi is not supported for $arch" usage fi if [ "$efi" ] && [ "$arch" = "riscv64" ] && [ -z "$efi_direct" ]; then efi_direct=y fi if [ -z "$page_size" ]; then if [ "$arch" = "arm" ] || [ "$arch" = "arm64" ]; then page_size="4096" elif [ "$arch" = "ppc64" ]; then page_size="65536" fi else if [ "${page_size: -1}" = "K" ] || [ "${page_size: -1}" = "k" ]; then page_size=$(( ${page_size%?} * 1024 )) fi if [ "$arch" = "arm64" ]; then if [ "$page_size" != "4096" ] && [ "$page_size" != "16384" ] && [ "$page_size" != "65536" ]; then echo "arm64 doesn't support page size of $page_size" usage fi if [ "$efi" = 'y' ] && [ "$page_size" != "4096" ]; then echo "efi must use 4K pages" exit 1 fi elif [ "$arch" = "ppc64" ]; then if [ "$page_size" != "4096" ] && [ "$page_size" != "65536" ]; then echo "ppc64 doesn't support page size of $page_size" usage fi else echo "--page-size is not supported for $arch" usage fi fi if [ "$earlycon" ]; then IFS=, read -r name type_addr addr <<<"$earlycon" if [ "$name" != "uart" ] && [ "$name" != "uart8250" ] && [ "$name" != "pl011" ]; then echo "unknown earlycon name: $name" usage fi if [ "$name" = "pl011" ]; then if [ -z "$addr" ]; then addr=$type_addr else if [ "$type_addr" != "mmio32" ]; then echo "unknown $name earlycon type: $type_addr" usage fi fi else if [ "$type_addr" != "mmio" ]; then echo "unknown $name earlycon type: $type_addr" usage fi fi if [ -z "$addr" ]; then echo "missing $name earlycon address" usage fi if [[ $addr =~ ^0(x|X)[0-9a-fA-F]+$ ]] || [[ $addr =~ ^[0-9]+$ ]]; then uart_early_addr=$addr else echo "invalid $name earlycon address: $addr" usage fi fi # $arch will have changed when cross-compiling. [ -z "$processor" ] && processor=$(get_default_processor $arch) if [ "$arch" = "i386" ] || [ "$arch" = "x86_64" ]; then testdir=x86 elif [ "$arch" = "arm" ] || [ "$arch" = "arm64" ]; then testdir=arm if [ "$target" = "qemu" ]; then : "${uart_early_addr:=0x9000000}" elif [ "$target" = "kvmtool" ]; then : "${uart_early_addr:=0x1000000}" errata_force=1 else echo "--target must be one of 'qemu' or 'kvmtool'!" usage fi elif [ "$arch" = "ppc64" ]; then testdir=powerpc firmware="$testdir/boot_rom.bin" if [ "$endian" != "little" ] && [ "$endian" != "big" ]; then echo "You must provide endianness (big or little)!" usage fi elif [ "$arch" = "riscv32" ] || [ "$arch" = "riscv64" ]; then testdir=riscv arch_libdir=riscv : "${uart_early_addr:=0x10000000}" elif [ "$arch" = "s390x" ]; then testdir=s390x else echo "arch $arch is not supported!" arch= usage fi if [ ! -d "$srcdir/$testdir" ]; then echo "$srcdir/$testdir does not exist!" exit 1 fi if [ "$efi" = "y" ] && [ -f "$srcdir/$testdir/efi/run" ]; then ln -fs "$srcdir/$testdir/efi/run" $testdir-run elif [ -f "$srcdir/$testdir/run" ]; then ln -fs "$srcdir/$testdir/run" $testdir-run fi testsubdir=$testdir if [ "$efi" = "y" ]; then testsubdir=$testdir/efi fi # check if uint32_t needs a long format modifier cat << EOF > lib-test.c __UINT32_TYPE__ EOF u32_long=$("$cc" $cflags -E lib-test.c | grep -v '^#' | grep -q long && echo yes) rm -f lib-test.c # check if slash can be used for division if [ "$arch" = "i386" ] || [ "$arch" = "x86_64" ]; then cat << EOF > lib-test.S foo: movl (8 / 2), %eax EOF wa_divide=$("$cc" $cflags -c lib-test.S >/dev/null 2>&1 || echo yes) rm -f lib-test.{o,S} fi # warn if enhanced getopt is unavailable getopt -T > /dev/null if [ $? -ne 4 ]; then echo "Without enhanced getopt you won't be able to use run_tests.sh." echo "Add it to your PATH?" fi # Are we in a separate build tree? If so, link the Makefile # and shared stuff so that 'make' and run_tests.sh work. if test ! -e Makefile; then echo "linking Makefile..." ln -s "$srcdir/Makefile" . echo "linking tests..." mkdir -p $testsubdir ln -sf "$srcdir/$testdir/run" $testdir/ if test "$testdir" != "$testsubdir"; then ln -sf "$srcdir/$testsubdir/run" $testsubdir/ fi ln -sf "$srcdir/$testdir/unittests.cfg" $testdir/ ln -sf "$srcdir/run_tests.sh" if [ -d "$srcdir/$testdir/snippets" ]; then mkdir -p "$testdir/snippets/c" fi echo "linking scripts..." ln -sf "$srcdir/scripts" fi # link lib/asm for the architecture rm -f lib/asm asm="asm-generic" if [ -d "$srcdir/lib/$arch/asm" ]; then asm="$srcdir/lib/$arch/asm" mkdir -p "lib/$arch" elif [ -d "$srcdir/lib/$arch_libdir/asm" ]; then asm="$srcdir/lib/$arch_libdir/asm" mkdir -p "lib/$arch_libdir" elif [ -d "$srcdir/lib/$testdir/asm" ]; then asm="$srcdir/lib/$testdir/asm" mkdir -p "lib/$testdir" fi ln -sf "$asm" lib/asm mkdir -p lib/generated lib/libfdt # create the config cat < config.mak # Shellcheck does not see these are used # shellcheck disable=SC2034 # Shellcheck can give pointless quoting warnings for some commands # shellcheck disable=SC2209 SRCDIR=$srcdir PREFIX=$prefix HOST=$host ARCH=$arch ARCH_NAME=$arch_name ARCH_LIBDIR=$arch_libdir PROCESSOR=$processor TARGET_CPU=$target_cpu DEFAULT_QEMU_CPU=$(get_default_qemu_cpu $arch) CC=$cc CFLAGS=$cflags LD=$cross_prefix$ld OBJCOPY=$cross_prefix$objcopy OBJDUMP=$cross_prefix$objdump READELF=$cross_prefix$readelf AR=$cross_prefix$ar ADDR2LINE=$cross_prefix$addr2line TEST_DIR=$testdir TEST_SUBDIR=$testsubdir FIRMWARE=$firmware ENDIAN=$endian PRETTY_PRINT_STACKS=$pretty_print_stacks ENVIRON_DEFAULT=$environ_default ERRATATXT=$erratatxt U32_LONG_FMT=$u32_long WA_DIVIDE=$wa_divide GENPROTIMG=${GENPROTIMG-genprotimg} HOST_KEY_DOCUMENT=$host_key_document CONFIG_DUMP=$enable_dump CONFIG_EFI=$efi EFI_DIRECT=$efi_direct CONFIG_WERROR=$werror GEN_SE_HEADER=$gen_se_header EOF if [ "$arch" = "arm" ] || [ "$arch" = "arm64" ]; then echo "TARGET=$target" >> config.mak fi cat < lib/config.h #ifndef _CONFIG_H_ #define _CONFIG_H_ /* * Generated file. DO NOT MODIFY. * */ EOF if [ "$arch" = "arm" ] || [ "$arch" = "arm64" ]; then cat <> lib/config.h #define CONFIG_UART_EARLY_BASE ${uart_early_addr} #define CONFIG_ERRATA_FORCE ${errata_force} EOF fi if [ "$arch" = "arm" ] || [ "$arch" = "arm64" ] || [ "$arch" = "ppc64" ]; then cat <> lib/config.h #define CONFIG_PAGE_SIZE _AC(${page_size}, UL) EOF elif [ "$arch" = "riscv32" ] || [ "$arch" = "riscv64" ]; then echo "#define CONFIG_UART_EARLY_BASE ${uart_early_addr}" >> lib/config.h [ "$console" = "sbi" ] && echo "#define CONFIG_SBI_CONSOLE" >> lib/config.h echo >> lib/config.h fi echo "#endif" >> lib/config.h