|  | #!/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 | 
|  |  | 
|  | 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= | 
|  | efi= | 
|  | efi_direct= | 
|  |  | 
|  | # Enable -Werror by default for git repositories only (i.e. developer builds) | 
|  | if [ -e "$srcdir"/.git ]; then | 
|  | werror=-Werror | 
|  | else | 
|  | werror= | 
|  | fi | 
|  |  | 
|  | usage() { | 
|  | 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 ($arch) | 
|  | --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 (optional, arm and | 
|  | arm64 only). 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. | 
|  | --[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 | 
|  | } | 
|  |  | 
|  | while [[ "$1" = -* ]]; do | 
|  | opt="$1"; shift | 
|  | arg= | 
|  | if [[ "$opt" = *=* ]]; then | 
|  | arg="${opt#*=}" | 
|  | opt="${opt%%=*}" | 
|  | fi | 
|  | case "$opt" in | 
|  | --prefix) | 
|  | prefix="$arg" | 
|  | ;; | 
|  | --arch) | 
|  | arch="$arg" | 
|  | ;; | 
|  | --processor) | 
|  | processor="$arg" | 
|  | ;; | 
|  | --target) | 
|  | target="$arg" | 
|  | ;; | 
|  | --cross-prefix) | 
|  | cross_prefix="$arg" | 
|  | ;; | 
|  | --endian) | 
|  | endian="$arg" | 
|  | ;; | 
|  | --cc) | 
|  | cc="$arg" | 
|  | ;; | 
|  | --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" | 
|  | ;; | 
|  | --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 "$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 | 
|  |  | 
|  | [ -z "$processor" ] && processor="$arch" | 
|  |  | 
|  | if [ "$processor" = "arm64" ]; then | 
|  | processor="cortex-a57" | 
|  | elif [ "$processor" = "arm" ]; then | 
|  | processor="cortex-a15" | 
|  | fi | 
|  |  | 
|  | if [ "$arch" = "i386" ] || [ "$arch" = "x86_64" ]; then | 
|  | testdir=x86 | 
|  | elif [ "$arch" = "arm" ] || [ "$arch" = "arm64" ]; then | 
|  | testdir=arm | 
|  | if [ "$target" = "qemu" ]; then | 
|  | arm_uart_early_addr=0x09000000 | 
|  | elif [ "$target" = "kvmtool" ]; then | 
|  | arm_uart_early_addr=0x1000000 | 
|  | errata_force=1 | 
|  | else | 
|  | echo "--target must be one of 'qemu' or 'kvmtool'!" | 
|  | usage | 
|  | 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 | 
|  | arm_uart_early_addr=$addr | 
|  | else | 
|  | echo "invalid $name earlycon address: $addr" | 
|  | usage | 
|  | fi | 
|  | 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 | 
|  | 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=$("$cross_prefix$cc" -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=$("$cross_prefix$cc" -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" | 
|  | elif [ -d "$srcdir/lib/$testdir/asm" ]; then | 
|  | asm="$srcdir/lib/$testdir/asm" | 
|  | fi | 
|  | mkdir -p lib | 
|  | ln -sf "$asm" lib/asm | 
|  |  | 
|  |  | 
|  | # create the config | 
|  | cat <<EOF > 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 | 
|  | CC=$cross_prefix$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 <<EOF > lib/config.h | 
|  | #ifndef _CONFIG_H_ | 
|  | #define _CONFIG_H_ | 
|  | /* | 
|  | * Generated file. DO NOT MODIFY. | 
|  | * | 
|  | */ | 
|  |  | 
|  | EOF | 
|  | if [ "$arch" = "arm" ] || [ "$arch" = "arm64" ]; then | 
|  | cat <<EOF >> lib/config.h | 
|  |  | 
|  | #define CONFIG_UART_EARLY_BASE ${arm_uart_early_addr} | 
|  | #define CONFIG_ERRATA_FORCE ${errata_force} | 
|  |  | 
|  | EOF | 
|  | fi | 
|  |  | 
|  | if [ "$arch" = "arm" ] || [ "$arch" = "arm64" ] || [ "$arch" = "ppc64" ]; then | 
|  | cat <<EOF >> lib/config.h | 
|  |  | 
|  | #define CONFIG_PAGE_SIZE _AC(${page_size}, UL) | 
|  |  | 
|  | EOF | 
|  | elif [ "$arch" = "riscv32" ] || [ "$arch" = "riscv64" ]; then | 
|  | cat <<EOF >> lib/config.h | 
|  |  | 
|  | #define CONFIG_UART_EARLY_BASE 0x10000000 | 
|  |  | 
|  | EOF | 
|  | fi | 
|  | echo "#endif" >> lib/config.h |