Scripts for automating the running of kvm-unit-tests

Scripts that run the kvm-unit-tests using a custom kernel. Details on
running the scripts are in the README and the help for run_tests.sh

These scripts can be used standalone, and are also meant to be used as
part of the repo automated makefile, and to aid in future continuous
integration work.

Signed-off-by: Fuad Tabba <tabba@google.com>
diff --git a/aarch64-unit-tests/README b/aarch64-unit-tests/README
new file mode 100644
index 0000000..fbd72bf
--- /dev/null
+++ b/aarch64-unit-tests/README
@@ -0,0 +1,58 @@
+# AArch64 KVM Unit Test Driver
+
+This directory contains the driver script for the AArch64 unit tests. These
+scripts run the kvm-unit-tests inside an AArch64 emulator (qemu-system-aarch64).
+This to enable testing KVM with virtual machines that are running natively on
+the underlying system, as opposed to emulated VMs. We need this to be able to
+test Protected KVM, which would be running VMs on the same architecture as the
+host/hypervisor (i.e., AAarch64).
+
+
+## Requirements:
+
+- A Linux kernel image cross-compiled for AArch64. See ANDROID-KVM_DIR/linux
+
+An example on how to build one, in the linux source:
+cd ANDROID-KVM_DIR/linux
+make ARCH=arm64 CROSS_COMPILE="aarch64-linux-gnu-" -j`nproc` defconfig
+make ARCH=arm64 CROSS_COMPILE="aarch64-linux-gnu-" -j`nproc`
+
+The image will be built at:
+ANDROID-KVM_DIR/linux/arch/arm64/boot/Image
+
+
+- KVM unit tests built as standalone. See ANDROID-KVM_DIR/kvm-unit-tests
+
+To build these standalone tests, in the kvm-unit-tests source directory:
+
+cd ANDROID-KVM_DIR/kvm-unit-tests
+./configure --arch=arm64 --cross-prefix="aarch64-linux-gnu-"
+make -j`nproc` standalone
+
+The standalone tests will be built at:
+ANDROID-KVM_DIR/kvm-unit-tests/tests
+
+
+- A root file system image to boot. An image will be uploaded to
+ANDROID-KVM_DIR/prebuilts.  In the meantime, you can generate such an image
+using Buildroot.  See ANDROID-KVM_DIR/buildroot.
+
+cd ANDROID-KVM_DIR/buildroot
+make qemu_aarch64_virt_kvmunittests_defconfig
+make clean all
+
+The generated image will be at:
+ANDROID-KVM_DIR/buildroot/output/images/rootfs.ext4
+
+
+- QEMU with aarch64-softmmu, v5.0.0 or later
+
+Buildroot creates one in the buildprocess.  It's available at
+ANDROID-KVM_DIR/buildroot/output/host/bin/qemu-system-aarch64
+
+## Running the tests
+
+Assuming you are using the binaries, tests, and images from above, you can run
+the tests as follows:
+
+./run_tests.sh -j `nproc` -t ANDROID-KVM_DIR/kvm-unit-tests/tests -l ANDROID-KVM_DIR/linux/arch/arm64/boot/Image -r ANDROID-KVM_DIR/buildroot/output/images/rootfs.ext4 -e ANDROID-KVM_DIR/buildroot/output/host/bin/qemu-system-aarch64
diff --git a/aarch64-unit-tests/run_emu.sh b/aarch64-unit-tests/run_emu.sh
new file mode 100755
index 0000000..e44a0cd
--- /dev/null
+++ b/aarch64-unit-tests/run_emu.sh
@@ -0,0 +1,76 @@
+#!/usr/bin/env bash
+
+# Copyright 2020 The Android KVM Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+TIMEOUT=180s
+
+# QEMU CPUs to use for VHE and nVHE runs
+VHE_CPU="max,sve=off"
+NVHE_CPU="cortex-a53"
+
+TEST_FILE=$1
+LINUX_KERNEL=$2
+ROOTFS=$3
+QEMU=$4
+VHE=$5
+CPU=""
+
+# Assume that the roms are located with the disk image.
+ROM_PATH=$(dirname ${ROOTFS})
+
+echo $ROM_PATH
+
+if [ ! -f ${TEST_FILE} ]; then
+    echo "Standalone kvmunittest file not found"
+    echo "Run make standalone first to generate the standalone test files."
+    exit 1
+fi
+
+if [ ! -f ${LINUX_KERNEL} ]; then
+    echo "Linux kernel image file not found."
+    exit 1
+fi
+
+if [ ! -f ${ROOTFS} ]; then
+    echo "Root filesystem image file not found."
+    exit 1
+fi
+
+if [ ! -x ${QEMU} ]; then
+    echo "QEMU executable not found."
+    exit 1
+fi
+
+if [ ${VHE} = 1 ]; then
+    CPU=${VHE_CPU}
+else
+    CPU=${NVHE_CPU}
+fi
+
+timeout -k 1s --foreground ${TIMEOUT} ${QEMU} \
+   -M virt \
+   -machine virtualization=true -machine virt,gic-version=3  \
+   -cpu "${CPU}" \
+   -nographic \
+   -smp 2 \
+   -m 512 \
+   -kernel "${LINUX_KERNEL}" \
+   -L ${ROM_PATH} \
+   -append "rootwait root=/dev/vda" \
+   -drive file="${ROOTFS}",readonly,if=virtio,format=raw \
+   -drive file="${TEST_FILE}",readonly,if=virtio,format=raw \
+   -object rng-random,filename=/dev/urandom,id=rng0      \
+   -device virtio-rng-pci,rng=rng0
+
diff --git a/aarch64-unit-tests/run_tests.sh b/aarch64-unit-tests/run_tests.sh
new file mode 100755
index 0000000..2633451
--- /dev/null
+++ b/aarch64-unit-tests/run_tests.sh
@@ -0,0 +1,180 @@
+#!/usr/bin/env bash
+
+# Copyright 2020 The Android KVM Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Variables to represent VHE and nVHE emulation parameter
+VHE=1
+NVHE=0
+
+function get_qemu_binary()
+{
+    local qemucmd
+    local qemu
+
+    qemucmd=${QEMU:-qemu-system-aarch64}
+    if $qemucmd --help 2>/dev/null | grep -q 'QEMU'; then
+        qemu="$qemucmd"
+    fi
+
+    if [ -z "${qemu}" ]; then
+        return
+    fi
+
+    command -v "${qemu}"
+}
+
+function get_outcome()
+{
+    grep -E -h --color=never "[^ ](PASS|FAIL|SKIP)[^:]"
+}
+
+function usage()
+{
+cat <<EOF
+
+Usage: $0 [-h] [-t TEST-FILES] [-j NUM-TASKS] [-l KERNEL] [-e QEMU] [-r ROOT]
+
+    -h    Output this help text
+    -t    kvm-unit-tests standalone directory
+    -x    tests to exclude (regex), can be specified more than once
+    -j    Execute tests in parallel
+    -l    Linux kernel image to use
+    -e    QEMU emulator binary
+    -r    Root file system image to use
+
+EOF
+}
+
+function usage_abort()
+{
+    usage
+    exit 1
+}
+
+function run_job()
+{
+	local job="$1"
+	if [ -z "$job" ]; then
+		return
+	fi
+
+	while (( $(jobs | wc -l) == $j )); do
+		# Wait for background jobs
+		wait -n 2>/dev/null
+	done
+
+	if [ $j = 1 ]; then
+		bash -c "$@"
+	else
+		bash -c "$@" &
+	fi
+}
+
+script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
+test_dir=""
+exclude_regex=""
+j=1
+linux=""
+rootfs=""
+verbose=${VERBOSE:-0}
+qemu=$(get_qemu_binary)
+
+while getopts ":t:x:j:l:r:e:hv" opt; do
+    case "${opt}" in
+        t)
+            test_dir=${OPTARG}
+            ;;
+        x)
+            # OR with the previous regex if specified
+            if [ ! -z $exclude_regex ]; then
+              exclude_regex+="|"
+            fi
+            exclude_regex+="(^${OPTARG}$)"
+            ;;
+        j)
+            j=${OPTARG}
+            ;;
+        l)
+            linux=${OPTARG}
+            ;;
+        r)
+            rootfs=${OPTARG}
+            ;;
+        e)
+            qemu=${OPTARG}
+            ;;
+        h)
+            usage
+            exit 0
+            ;;
+        v)
+            verbose=1
+            ;;
+        *)
+            usage_abort
+            ;;
+    esac
+done
+if ((OPTIND == 1)); then
+    usage_abort
+fi
+shift $((OPTIND-1))
+
+if [[ ! -d ${test_dir} ]]; then
+    echo "No test directory specified."
+    usage_abort
+fi
+
+if [[ ! -f ${linux} ]]; then
+	echo "Linux kernel image file not found."
+	usage_abort
+fi
+
+if [[ ! -f ${rootfs} ]]; then
+	echo "Root filesystem image file not found."
+	usage_abort
+fi
+
+if [[ ! -x ${qemu} ]]; then
+	echo "QEMU executable not found."
+	usage_abort
+fi
+
+rm -rf logs.old
+if [ -d logs ]; then
+    mv logs logs.old
+fi
+mkdir -p logs/vhe || exit 2
+mkdir -p logs/nvhe || exit 2
+
+for test in ${test_dir}/*; do
+    test_name="$(basename ${test})"
+
+    if [[ ! -z $exclude_regex && "${test_name}" =~ ${exclude_regex} ]]; then
+        if [ "$verbose" != "0" ]; then
+            echo "Excluding ${test_name}"
+        fi
+        continue
+    fi
+
+    if [ "$verbose" != "0" ]; then
+        echo "Running ${test_name}"
+    fi
+
+    # Redirect output instead of pipe to preserve exit status.
+    run_job "${script_dir}/run_emu.sh ${test} ${linux} ${rootfs} ${qemu} ${VHE} &> >(tee logs/vhe/${test_name}.log | grep -E -h --color=never \"[^ ](PASS|FAIL|SKIP)[^:]\")"
+    run_job "${script_dir}/run_emu.sh ${test} ${linux} ${rootfs} ${qemu} ${NVHE} &> >(tee logs/nvhe/${test_name}.log | grep -E -h --color=never \"[^ ](PASS|FAIL|SKIP)[^:]\")"
+done
+wait