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();
+}