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