QEMU continuous integration tests wide range of CPUs

Run QEMU continuous integration tests with CPU configurations representative
of the maximum features Hafnium supports, as well as one with the minimum
features that Hafnium supports.

When running locally, run QEMU with the maximum features, for more coverage.

Bug: 143876265
Change-Id: I5b54aae4bdf50cde8f846f7a683e2c3a4bd3d6b2
diff --git a/kokoro/ubuntu/build.sh b/kokoro/ubuntu/build.sh
index 4277d17..553b687 100755
--- a/kokoro/ubuntu/build.sh
+++ b/kokoro/ubuntu/build.sh
@@ -54,10 +54,12 @@
 	# Default config for Kokoro builds.
 	default_value HAFNIUM_HERMETIC_BUILD true
 	default_value HAFNIUM_SKIP_LONG_RUNNING_TESTS false
+	default_value HAFNIUM_RUN_ALL_QEMU_CPUS true
 else
 	# Default config for local builds.
 	default_value HAFNIUM_HERMETIC_BUILD false
 	default_value HAFNIUM_SKIP_LONG_RUNNING_TESTS true
+	default_value HAFNIUM_RUN_ALL_QEMU_CPUS false
 fi
 
 # If HAFNIUM_HERMETIC_BUILD is "true", relaunch this script inside a container.
@@ -79,6 +81,9 @@
 	--skip-long-running-tests)
 		HAFNIUM_SKIP_LONG_RUNNING_TESTS=true
 		;;
+	--run-all-qemu-cpus)
+		HAFNIUM_RUN_ALL_QEMU_CPUS=true
+		;;
 	*)
 		echo "Unexpected argument $1"
 		exit 1
@@ -120,6 +125,10 @@
 then
 	TEST_ARGS+=(--skip-long-running-tests)
 fi
+if [ "${HAFNIUM_RUN_ALL_QEMU_CPUS}" == "true" ]
+then
+	TEST_ARGS+=(--run-all-qemu-cpus)
+fi
 ./kokoro/ubuntu/test.sh ${TEST_ARGS[@]}
 
 #
diff --git a/kokoro/ubuntu/test.sh b/kokoro/ubuntu/test.sh
index 80e5b6f..0dcd187 100755
--- a/kokoro/ubuntu/test.sh
+++ b/kokoro/ubuntu/test.sh
@@ -28,6 +28,7 @@
 
 USE_FVP=false
 SKIP_LONG_RUNNING_TESTS=false
+RUN_ALL_QEMU_CPUS=false
 
 while test $# -gt 0
 do
@@ -36,6 +37,8 @@
       ;;
     --skip-long-running-tests) SKIP_LONG_RUNNING_TESTS=true
       ;;
+    --run-all-qemu-cpus) RUN_ALL_QEMU_CPUS=true
+      ;;
     *) echo "Unexpected argument $1"
       exit 1
       ;;
@@ -46,9 +49,10 @@
 TIMEOUT=(timeout --foreground)
 PROJECT="${PROJECT:-reference}"
 OUT="out/${PROJECT}"
+LOG_DIR_BASE="${OUT}/kokoro_log"
 
 # Run the tests with a timeout so they can't loop forever.
-HFTEST=(${TIMEOUT[@]} 300s ./test/hftest/hftest.py --log "$OUT/kokoro_log")
+HFTEST=(${TIMEOUT[@]} 300s ./test/hftest/hftest.py)
 if [ $USE_FVP == true ]
 then
   HFTEST+=(--fvp)
@@ -67,14 +71,32 @@
 export LD_LIBRARY_PATH="$PWD/prebuilts/linux-x64/clang/lib64"
 
 # Run the host unit tests.
-mkdir -p "$OUT/kokoro_log/unit_tests"
+mkdir -p "${LOG_DIR_BASE}/unit_tests"
 ${TIMEOUT[@]} 30s "$OUT/host_fake_clang/unit_tests" \
-  --gtest_output="xml:$OUT/kokoro_log/unit_tests/sponge_log.xml" \
-  | tee "$OUT/kokoro_log/unit_tests/sponge_log.log"
+  --gtest_output="xml:${LOG_DIR_BASE}/unit_tests/sponge_log.xml" \
+  | tee "${LOG_DIR_BASE}/unit_tests/sponge_log.log"
 
-${HFTEST[@]} arch_test
-${HFTEST[@]} hafnium --initrd test/vmapi/arch/aarch64/aarch64_test
-${HFTEST[@]} hafnium --initrd test/vmapi/arch/aarch64/gicv3/gicv3_test
-${HFTEST[@]} hafnium --initrd test/vmapi/primary_only/primary_only_test
-${HFTEST[@]} hafnium --initrd test/vmapi/primary_with_secondaries/primary_with_secondaries_test
-${HFTEST[@]} hafnium --initrd test/linux/linux_test --vm_args "rdinit=/test_binary --"
+CPUS=("")
+
+if [ $RUN_ALL_QEMU_CPUS == true ]
+then
+  CPUS=("cortex-a53" "max")
+fi
+
+for CPU in "${CPUS[@]}"
+do
+  HFTEST_CPU=("${HFTEST[@]}")
+  if [ -n "$CPU" ]
+  then
+    # Per-CPU log directory to avoid filename conflicts.
+    HFTEST_CPU+=(--cpu "$CPU" --log "$LOG_DIR_BASE/$CPU")
+  else
+    HFTEST_CPU+=(--log "$LOG_DIR_BASE")
+  fi
+  "${HFTEST_CPU[@]}" arch_test
+  "${HFTEST_CPU[@]}" hafnium --initrd test/vmapi/arch/aarch64/aarch64_test
+  "${HFTEST_CPU[@]}" hafnium --initrd test/vmapi/arch/aarch64/gicv3/gicv3_test
+  "${HFTEST_CPU[@]}" hafnium --initrd test/vmapi/primary_only/primary_only_test
+  "${HFTEST_CPU[@]}" hafnium --initrd test/vmapi/primary_with_secondaries/primary_with_secondaries_test
+  "${HFTEST_CPU[@]}" hafnium --initrd test/linux/linux_test --vm_args "rdinit=/test_binary --"
+done
diff --git a/test/hftest/hftest.py b/test/hftest/hftest.py
index c588c62..3d5bc0b 100755
--- a/test/hftest/hftest.py
+++ b/test/hftest/hftest.py
@@ -117,6 +117,7 @@
         "initrd",
         "manifest",
         "vm_args",
+        "cpu"
     ])
 
 
@@ -203,11 +204,14 @@
             dumpdtb_path=None):
         """Generate command line arguments for QEMU."""
         time_limit = "120s" if is_long_running else "10s"
+        # If no CPU configuration is selected, then test against the maximum
+        # configuration, "max", supported by QEMU.
+        cpu = self.args.cpu or "max"
         exec_args = [
             "timeout", "--foreground", time_limit,
             "./prebuilts/linux-x64/qemu/qemu-system-aarch64",
             "-machine", "virt,virtualization=on,gic_version=3",
-            "-cpu", "cortex-a57", "-smp", "4", "-m", "64M",
+            "-cpu", cpu, "-smp", "4", "-m", "64M",
             "-nographic", "-nodefaults", "-serial", "stdio",
             "-d", "unimp", "-kernel", self.args.kernel,
         ]
@@ -261,6 +265,8 @@
     """Driver which runs tests in ARM FVP emulator."""
 
     def __init__(self, args):
+        if args.cpu:
+            raise ValueError("FVP emulator does not support the --cpu option.")
         Driver.__init__(self, args)
 
     def gen_dts(self, dts_path, test_args, initrd_start, initrd_end):
@@ -433,6 +439,18 @@
             test_out[-1] == HFTEST_LOG_FINISHED and \
             not any(l.startswith(HFTEST_LOG_FAILURE_PREFIX) for l in test_out)
 
+    def get_log_name(self, suite, test):
+        """Returns a string with a generated log name for the test."""
+        log_name = ""
+
+        cpu = self.driver.args.cpu
+        if cpu:
+            log_name += cpu + "."
+
+        log_name += suite["name"] + "." + test["name"]
+
+        return log_name
+
     def run_test(self, suite, test, suite_xml):
         """Invoke the test platform and request to run a given `test` in given
         `suite`. Create a new XML node with results under `suite_xml`.
@@ -445,7 +463,7 @@
             return TestRunnerResult(tests_run=0, tests_failed=0)
 
         print("      RUN", test["name"])
-        log_name = suite["name"] + "." + test["name"]
+        log_name = self.get_log_name(suite, test)
 
         test_xml = ET.SubElement(suite_xml, "testcase")
         test_xml.set("name", test["name"])
@@ -524,6 +542,8 @@
     parser.add_argument("--vm_args")
     parser.add_argument("--fvp", action="store_true")
     parser.add_argument("--skip-long-running-tests", action="store_true")
+    parser.add_argument("--cpu",
+        help="Selects the CPU configuration for the run environment.")
     args = parser.parse_args()
 
     # Resolve some paths.
@@ -542,7 +562,8 @@
     artifacts = ArtifactsManager(os.path.join(args.log, image_name))
 
     # Create a driver for the platform we want to test on.
-    driver_args = DriverArgs(artifacts, image, initrd, manifest, vm_args)
+    driver_args = DriverArgs(artifacts, image, initrd, manifest, vm_args,
+        args.cpu)
     if args.fvp:
         driver = FvpDriver(driver_args)
     else: