| #!/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" | "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 and riscv32/riscv64 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" != "arm" ] && [ "$arch" != "arm64" ] && | |
| [ "$arch" != "riscv32" ] && [ "$arch" != "riscv64" ]; 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}" | |
| 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}" | |
| if [ "$target" != "qemu" ] && [ "$target" != "kvmtool" ]; then | |
| echo "--target must be one of 'qemu' or 'kvmtool'!" | |
| usage | |
| fi | |
| 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 <<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 | |
| 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" ] || | |
| [ "$arch" = "riscv32" ] || [ "$arch" = "riscv64" ]; 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 ${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 | |
| 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 |