Infrastructure for running tests under Linux in primary VM.

Includes an initial test of simply inserting and removing the Hafnium
kernel module.

Change-Id: I832a30d902f58ca71f89374300ab39b2ba3ab877
diff --git a/test/hftest/BUILD.gn b/test/hftest/BUILD.gn
index 561fc2d..9f1df7c 100644
--- a/test/hftest/BUILD.gn
+++ b/test/hftest/BUILD.gn
@@ -61,6 +61,23 @@
   ]
 }
 
+# Testing framework for tests running under Linux in the primary VM.
+source_set("hftest_linux") {
+  testonly = true
+  public_configs = [ ":hftest_config" ]
+
+  sources = [
+    "linux_main.c",
+  ]
+
+  deps = [
+    ":common",
+    "//src:dlog",
+    "//src:memiter",
+    "//src/arch/${plat_arch}/hftest:power_mgmt",
+  ]
+}
+
 source_set("hftest_standalone") {
   visibility = [ ":*" ]
   testonly = true
@@ -68,16 +85,31 @@
   public_configs = [ ":hftest_config" ]
 
   sources = [
-    "hftest.c",
+    "standalone_main.c",
   ]
 
   deps = [
+    ":common",
     "//src:dlog",
     "//src:fdt",
     "//src:memiter",
     "//src/arch/${plat_arch}:entry",
-    "//src/arch/${plat_arch}:std",
     "//src/arch/${plat_arch}/hftest:entry",
     "//src/arch/${plat_arch}/hftest:power_mgmt",
   ]
 }
+
+# Common code for hftest, whether it is running under Linux, under Hafnium in
+# the primary VM, or directly on the hardware.
+source_set("common") {
+  visibility = [ ":*" ]
+  testonly = true
+  public_configs = [ ":hftest_config" ]
+  sources = [
+    "hftest_common.c",
+  ]
+  deps = [
+    "//src:memiter",
+    "//src/arch/${plat_arch}:std",
+  ]
+}
diff --git a/test/hftest/hftest.py b/test/hftest/hftest.py
index bc17b70..a124682 100755
--- a/test/hftest/hftest.py
+++ b/test/hftest/hftest.py
@@ -34,9 +34,9 @@
 
 def qemu(image, initrd, args, log):
     qemu_args = [
-        "timeout", "--foreground", "5s",
+        "timeout", "--foreground", "10s",
         "./prebuilts/linux-x64/qemu/qemu-system-aarch64", "-M", "virt,gic_version=3",
-        "-cpu", "cortex-a57", "-smp", "4", "-m", "16M", "-machine", "virtualization=true",
+        "-cpu", "cortex-a57", "-smp", "4", "-m", "64M", "-machine", "virtualization=true",
         "-nographic", "-nodefaults", "-serial", "stdio", "-kernel", image,
     ]
     if initrd:
@@ -78,6 +78,7 @@
     parser.add_argument("--initrd")
     parser.add_argument("--suite")
     parser.add_argument("--test")
+    parser.add_argument("--vm_args")
     args = parser.parse_args()
     # Resolve some paths.
     image = os.path.join(args.out, args.image + ".bin")
@@ -86,13 +87,14 @@
     if args.initrd:
         initrd = os.path.join(args.out, "obj", args.initrd, "initrd.img")
         suite += "_" + args.initrd
+    vm_args = args.vm_args or ""
     log = os.path.join(args.log, suite)
     ensure_dir(log)
     print("Logs saved under", log)
     log_file = os.path.join(log, "sponge_log.log")
     with open(log_file, "w") as sponge_log:
         # Query the tests in the image.
-        out = qemu(image, initrd, "json", os.path.join(log, "json.log"))
+        out = qemu(image, initrd, vm_args + " json", os.path.join(log, "json.log"))
         sponge_log.write(out)
         sponge_log.write("\r\n\r\n")
         hftest_json = "\n".join(hftest_lines(out))
@@ -127,7 +129,7 @@
                 print("      RUN", test)
                 test_log = os.path.join(log,
                                         suite["name"] + "." + test + ".log")
-                out = qemu(image, initrd, "run {} {}".format(
+                out = qemu(image, initrd, vm_args + " run {} {}".format(
                     suite["name"], test), test_log)
                 sponge_log.write(out)
                 sponge_log.write("\r\n\r\n")
diff --git a/test/hftest/hftest.c b/test/hftest/hftest_common.c
similarity index 71%
rename from test/hftest/hftest.c
rename to test/hftest/hftest_common.c
index ac22d23..871aa54 100644
--- a/test/hftest/hftest.c
+++ b/test/hftest/hftest_common.c
@@ -14,23 +14,20 @@
  * limitations under the License.
  */
 
-#include "hftest.h"
-
-#include <stdalign.h>
-#include <stdint.h>
+#include "hftest_common.h"
 
 #include "hf/arch/std.h"
 #include "hf/arch/vm/power_mgmt.h"
 
-#include "hf/fdt.h"
 #include "hf/memiter.h"
 
-alignas(4096) uint8_t kstack[4096];
+#include "hftest.h"
 
 HFTEST_ENABLE();
 
-extern struct hftest_test hftest_begin[];
-extern struct hftest_test hftest_end[];
+static struct hftest_test hftest_constructed[HFTEST_MAX_TESTS];
+static size_t hftest_count;
+static struct hftest_test *hftest_list;
 
 static struct hftest_context global_context;
 
@@ -39,16 +36,51 @@
 	return &global_context;
 }
 
-static void json(void)
+/**
+ * Adds the given test information to the global list, to be used by
+ * `hftest_use_registered_list`.
+ */
+void hftest_register(struct hftest_test test)
 {
-	struct hftest_test *test;
+	if (hftest_count < HFTEST_MAX_TESTS) {
+		hftest_constructed[hftest_count++] = test;
+	} else {
+		HFTEST_FAIL("Too many tests", true);
+	}
+}
+
+/**
+ * Uses the list of tests registered by `hftest_register(...)` as the ones to
+ * run.
+ */
+void hftest_use_registered_list(void)
+{
+	hftest_list = hftest_constructed;
+}
+
+/**
+ * Uses the given list of tests as the ones to run.
+ */
+void hftest_use_list(struct hftest_test list[], size_t count)
+{
+	hftest_list = list;
+	hftest_count = count;
+}
+
+/**
+ * Writes out a JSON structure describing the available tests.
+ */
+void hftest_json(void)
+{
 	const char *suite = NULL;
+	size_t i;
 	size_t suites_in_image = 0;
 	size_t tests_in_suite = 0;
 
 	HFTEST_LOG("{");
 	HFTEST_LOG("  \"suites\": [");
-	for (test = hftest_begin; test < hftest_end; ++test) {
+	for (i = 0; i < hftest_count; ++i) {
+		struct hftest_test *test = &hftest_list[i];
 		if (test->suite != suite) {
 			/* Close out previously open suite. */
 			if (tests_in_suite) {
@@ -90,6 +122,9 @@
 	HFTEST_LOG("}");
 }
 
+/**
+ * Logs a failure message and shut down.
+ */
 static noreturn void abort(void)
 {
 	HFTEST_LOG("FAIL");
@@ -129,27 +164,19 @@
 	HFTEST_LOG("FINISHED");
 }
 
-static void run(struct memiter *args)
+/**
+ * Runs the given test case.
+ */
+void hftest_run(struct memiter suite_name, struct memiter test_name)
 {
-	struct memiter suite_name;
-	struct memiter test_name;
-	struct hftest_test *test;
+	size_t i;
 	bool found_suite = false;
 	const char *suite = NULL;
 	hftest_test_fn suite_set_up = NULL;
 	hftest_test_fn suite_tear_down = NULL;
 
-	if (!memiter_parse_str(args, &suite_name)) {
-		HFTEST_LOG("Unable to parse test suite.");
-		return;
-	}
-
-	if (!memiter_parse_str(args, &test_name)) {
-		HFTEST_LOG("Unable to parse test.");
-		return;
-	}
-
-	for (test = hftest_begin; test < hftest_end; ++test) {
+	for (i = 0; i < hftest_count; ++i) {
+		struct hftest_test *test = &hftest_list[i];
 		/* Find the test suite. */
 		if (found_suite) {
 			if (test->suite != suite) {
@@ -196,7 +223,10 @@
 	HFTEST_LOG("Unable to find requested tests.");
 }
 
-static void help(void)
+/**
+ * Writes out usage information.
+ */
+void hftest_help(void)
 {
 	HFTEST_LOG("usage:");
 	HFTEST_LOG("");
@@ -215,52 +245,3 @@
 	HFTEST_LOG("");
 	HFTEST_LOG("    Run the named test from the named test suite.");
 }
-
-void kmain(const struct fdt_header *fdt)
-{
-	struct fdt_node n;
-	const char *bootargs;
-	uint32_t bootargs_size;
-	struct memiter bootargs_iter;
-	struct memiter command;
-
-	if (!fdt_root_node(&n, fdt)) {
-		HFTEST_LOG("FDT failed validation.");
-		return;
-	}
-
-	if (!fdt_find_child(&n, "")) {
-		HFTEST_LOG("Unable to find root node in FDT.");
-		return;
-	}
-
-	if (!fdt_find_child(&n, "chosen")) {
-		HFTEST_LOG("Unable to find 'chosen' node in FDT.");
-		return;
-	}
-
-	if (!fdt_read_property(&n, "bootargs", &bootargs, &bootargs_size)) {
-		HFTEST_LOG("Unable to read bootargs.");
-		return;
-	}
-
-	/* Remove null terminator. */
-	memiter_init(&bootargs_iter, bootargs, bootargs_size - 1);
-
-	if (!memiter_parse_str(&bootargs_iter, &command)) {
-		HFTEST_LOG("Unable to parse command.");
-		return;
-	}
-
-	if (memiter_iseq(&command, "json")) {
-		json();
-		return;
-	}
-
-	if (memiter_iseq(&command, "run")) {
-		run(&bootargs_iter);
-		return;
-	}
-
-	help();
-}
diff --git a/test/hftest/inc/hftest_common.h b/test/hftest/inc/hftest_common.h
new file mode 100644
index 0000000..fa35aab
--- /dev/null
+++ b/test/hftest/inc/hftest_common.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2019 The Hafnium 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
+ *
+ *     https://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.
+ */
+
+#include "hf/memiter.h"
+
+#include "hftest_impl.h"
+
+void hftest_use_registered_list(void);
+void hftest_use_list(struct hftest_test list[], size_t count);
+
+void hftest_json(void);
+void hftest_run(struct memiter suite_name, struct memiter test_name);
+void hftest_help(void);
diff --git a/test/hftest/inc/hftest_impl.h b/test/hftest/inc/hftest_impl.h
index 0470261..c4df298 100644
--- a/test/hftest/inc/hftest_impl.h
+++ b/test/hftest/inc/hftest_impl.h
@@ -20,6 +20,8 @@
 
 #include "hf/arch/std.h"
 
+#define HFTEST_MAX_TESTS 50
+
 /*
  * Log with the HFTEST_LOG_PREFIX and a new line. The zero is added so there is
  * always at least one variadic argument.
@@ -58,6 +60,12 @@
 	hftest_test_fn_##suite_name##_##test_name
 #define HFTEST_SERVICE_FN(service_name) hftest_service_fn_##service_name
 
+#define HFTEST_SET_UP_CONSTRUCTOR(suite_name) hftest_set_up_ctor_##suite_name
+#define HFTEST_TEAR_DOWN_CONSTRUCTOR(suite_name) \
+	hftest_tear_down_ctor_##suite_name
+#define HFTEST_TEST_CONSTRUCTOR(suite_name, test_name) \
+	hftest_test_ctor_##suite_name##_##test_name
+
 /* Register test functions. */
 #define HFTEST_SET_UP(suite_name)                                           \
 	static void HFTEST_SET_UP_FN(suite_name)(void);                     \
@@ -68,6 +76,11 @@
 				.kind = HFTEST_KIND_SET_UP,                 \
 				.fn = HFTEST_SET_UP_FN(suite_name),         \
 	};                                                                  \
+	static void __attribute__((constructor))                            \
+		HFTEST_SET_UP_CONSTRUCTOR(suite_name)(void)                 \
+	{                                                                   \
+		hftest_register(HFTEST_SET_UP_STRUCT(suite_name));          \
+	}                                                                   \
 	static void HFTEST_SET_UP_FN(suite_name)(void)
 
 #define HFTEST_TEAR_DOWN(suite_name)                                           \
@@ -79,18 +92,28 @@
 				.kind = HFTEST_KIND_TEAR_DOWN,                 \
 				.fn = HFTEST_TEAR_DOWN_FN(suite_name),         \
 	};                                                                     \
+	static void __attribute__((constructor))                               \
+		HFTEST_TEAR_DOWN_CONSTRUCTOR(suite_name)(void)                 \
+	{                                                                      \
+		hftest_register(HFTEST_TEAR_DOWN_STRUCT(suite_name));          \
+	}                                                                      \
 	static void HFTEST_TEAR_DOWN_FN(suite_name)(void)
 
-#define HFTEST_TEST(suite_name, test_name)                             \
-	static void HFTEST_TEST_FN(suite_name, test_name)(void);       \
-	const struct hftest_test __attribute__((used)) __attribute__(  \
-		(section(HFTEST_TEST_SECTION(suite_name, test_name)))) \
-		HFTEST_TEST_STRUCT(suite_name, test_name) = {          \
-			.suite = #suite_name,                          \
-			.kind = HFTEST_KIND_TEST,                      \
-			.name = #test_name,                            \
-			.fn = HFTEST_TEST_FN(suite_name, test_name),   \
-	};                                                             \
+#define HFTEST_TEST(suite_name, test_name)                                  \
+	static void HFTEST_TEST_FN(suite_name, test_name)(void);            \
+	const struct hftest_test __attribute__((used)) __attribute__(       \
+		(section(HFTEST_TEST_SECTION(suite_name, test_name))))      \
+		HFTEST_TEST_STRUCT(suite_name, test_name) = {               \
+			.suite = #suite_name,                               \
+			.kind = HFTEST_KIND_TEST,                           \
+			.name = #test_name,                                 \
+			.fn = HFTEST_TEST_FN(suite_name, test_name),        \
+	};                                                                  \
+	static void __attribute__((constructor))                            \
+		HFTEST_TEST_CONSTRUCTOR(suite_name, test_name)(void)        \
+	{                                                                   \
+		hftest_register(HFTEST_TEST_STRUCT(suite_name, test_name)); \
+	}                                                                   \
 	static void HFTEST_TEST_FN(suite_name, test_name)(void)
 
 #define HFTEST_TEST_SERVICE(service_name)                                      \
@@ -264,3 +287,5 @@
 
 #define HFTEST_SERVICE_SEND_BUFFER() hftest_get_context()->send
 #define HFTEST_SERVICE_RECV_BUFFER() hftest_get_context()->recv
+
+void hftest_register(struct hftest_test test);
diff --git a/test/hftest/linux_main.c b/test/hftest/linux_main.c
new file mode 100644
index 0000000..450dc1c
--- /dev/null
+++ b/test/hftest/linux_main.c
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2019 The Hafnium 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
+ *
+ *     https://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.
+ */
+
+#include <stdalign.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "hf/memiter.h"
+
+#include "hftest.h"
+#include "hftest_common.h"
+#include <sys/reboot.h>
+
+void test_main(int argc, const char *argv[])
+{
+	const char *command;
+
+	if (argc < 2) {
+		HFTEST_LOG("Unable to parse command.");
+		return;
+	}
+	command = argv[1];
+
+	hftest_use_registered_list();
+
+	if (strcmp(command, "json") == 0) {
+		hftest_json();
+		return;
+	}
+
+	if (strcmp(command, "run") == 0) {
+		struct memiter suite_name;
+		struct memiter test_name;
+
+		if (argc != 4) {
+			HFTEST_LOG("Unable to parse test.");
+			return;
+		}
+
+		memiter_init(&suite_name, argv[2], strlen(argv[2]));
+		memiter_init(&test_name, argv[3], strlen(argv[3]));
+		hftest_run(suite_name, test_name);
+		return;
+	}
+
+	hftest_help();
+}
+
+int main(int argc, const char *argv[])
+{
+	test_main(argc, argv);
+	reboot(RB_POWER_OFF);
+	return 0;
+}
diff --git a/test/hftest/standalone_main.c b/test/hftest/standalone_main.c
new file mode 100644
index 0000000..b4938e6
--- /dev/null
+++ b/test/hftest/standalone_main.c
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2019 The Hafnium 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
+ *
+ *     https://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.
+ */
+
+#include <stdalign.h>
+#include <stdint.h>
+
+#include "hf/fdt.h"
+#include "hf/memiter.h"
+
+#include "hftest.h"
+#include "hftest_common.h"
+
+alignas(4096) uint8_t kstack[4096];
+
+extern struct hftest_test hftest_begin[];
+extern struct hftest_test hftest_end[];
+
+void kmain(const struct fdt_header *fdt)
+{
+	struct fdt_node n;
+	const char *bootargs;
+	uint32_t bootargs_size;
+	struct memiter bootargs_iter;
+	struct memiter command;
+
+	hftest_use_list(hftest_begin, hftest_end - hftest_begin);
+
+	if (!fdt_root_node(&n, fdt)) {
+		HFTEST_LOG("FDT failed validation.");
+		return;
+	}
+
+	if (!fdt_find_child(&n, "")) {
+		HFTEST_LOG("Unable to find root node in FDT.");
+		return;
+	}
+
+	if (!fdt_find_child(&n, "chosen")) {
+		HFTEST_LOG("Unable to find 'chosen' node in FDT.");
+		return;
+	}
+
+	if (!fdt_read_property(&n, "bootargs", &bootargs, &bootargs_size)) {
+		HFTEST_LOG("Unable to read bootargs.");
+		return;
+	}
+
+	/* Remove null terminator. */
+	memiter_init(&bootargs_iter, bootargs, bootargs_size - 1);
+
+	if (!memiter_parse_str(&bootargs_iter, &command)) {
+		HFTEST_LOG("Unable to parse command.");
+		return;
+	}
+
+	if (memiter_iseq(&command, "json")) {
+		hftest_json();
+		return;
+	}
+
+	if (memiter_iseq(&command, "run")) {
+		struct memiter suite_name;
+		struct memiter test_name;
+
+		if (!memiter_parse_str(&bootargs_iter, &suite_name)) {
+			HFTEST_LOG("Unable to parse test suite.");
+			return;
+		}
+
+		if (!memiter_parse_str(&bootargs_iter, &test_name)) {
+			HFTEST_LOG("Unable to parse test.");
+			return;
+		}
+		hftest_run(suite_name, test_name);
+		return;
+	}
+
+	hftest_help();
+}