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/BUILD.gn b/BUILD.gn
index 3cbad6e..21d1869 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -30,3 +30,17 @@
"//project/${project}:test_root",
]
}
+
+group("update_prebuilts") {
+ deps = [
+ "//third_party:linux",
+ ]
+}
+
+group("default") {
+ testonly = true
+ deps = [
+ ":root",
+ ":test_root",
+ ]
+}
diff --git a/Makefile b/Makefile
index 56cfcb8..619ac1c 100644
--- a/Makefile
+++ b/Makefile
@@ -86,3 +86,10 @@
@find build/ -name \*.py| xargs -n1 python build/license.py --style hash
@find test/ -name \*.py| xargs -n1 python build/license.py --style hash
@find . \( -name \*.gn -o -name \*.gni \) | xargs -n1 python build/license.py --style hash
+
+.PHONY: update-prebuilts
+update-prebuilts: prebuilts/linux-aarch64/linux/vmlinuz
+
+prebuilts/linux-aarch64/linux/vmlinuz: $(OUT_DIR)/build.ninja
+ @$(NINJA) -C $(OUT_DIR) "third_party:linux"
+ cp out/reference/obj/third_party/linux.bin $@
diff --git a/build/image/generate_initrd.py b/build/image/generate_initrd.py
index a49865a..6fb76de 100644
--- a/build/image/generate_initrd.py
+++ b/build/image/generate_initrd.py
@@ -39,15 +39,14 @@
parser.add_argument("--staging", required=True)
parser.add_argument("--output", required=True)
args = parser.parse_args()
+ staged_files = ["vmlinuz", "initrd.img"]
# Prepare the primary VM image.
- staged_files = ["vmlinuz"]
shutil.copyfile(args.primary_vm, os.path.join(args.staging, "vmlinuz"))
- # Prepare the primary VM's initrd. Currently, it just makes an empty one.
+ # Prepare the primary VM's initrd.
if args.primary_vm_initrd:
- raise NotImplementedError(
- "This doesn't copy the primary VM's initrd yet")
- with open(os.path.join(args.staging, "initrd.img"), "w") as vms_txt:
- staged_files.append("initrd.img")
+ shutil.copyfile(args.primary_vm_initrd, os.path.join(args.staging, "initrd.img"))
+ else:
+ open(os.path.join(args.staging, "initrd.img"), "w").close()
# Prepare the secondary VMs.
with open(os.path.join(args.staging, "vms.txt"), "w") as vms_txt:
staged_files.append("vms.txt")
diff --git a/build/image/generate_linux_initrd.py b/build/image/generate_linux_initrd.py
new file mode 100644
index 0000000..06f6502
--- /dev/null
+++ b/build/image/generate_linux_initrd.py
@@ -0,0 +1,47 @@
+#!/usr/bin/env python
+#
+# 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.
+
+"""Generate an initial RAM disk for a Linux VM."""
+
+import argparse
+import os
+import shutil
+import subprocess
+import sys
+
+def Main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument("--staging", required=True)
+ parser.add_argument("--output", required=True)
+ args = parser.parse_args()
+ # Package files into an initial RAM disk.
+ with open(args.output, "w") as initrd:
+ # Move into the staging directory so the file names taken by cpio don't
+ # include the path.
+ os.chdir(args.staging)
+ staged_files = [os.path.join(root, filename)
+ for (root, dirs, files) in os.walk(".") for filename in files + dirs]
+ cpio = subprocess.Popen(
+ ["cpio", "--create", "--format=newc"],
+ stdin=subprocess.PIPE,
+ stdout=initrd,
+ stderr=subprocess.PIPE)
+ cpio.communicate(input="\n".join(staged_files).encode("utf-8"))
+ return 0
+
+
+if __name__ == "__main__":
+ sys.exit(Main())
diff --git a/build/image/image.gni b/build/image/image.gni
index 67af5a3..61a407a 100644
--- a/build/image/image.gni
+++ b/build/image/image.gni
@@ -112,10 +112,47 @@
}
}
+# Build the initial RAM disk for the Linux VM.
+template("linux_initrd") {
+ initrd_base = "${target_out_dir}/${target_name}/initrd"
+ initrd_file = "${initrd_base}.img"
+ initrd_staging = "${initrd_base}"
+
+ copy("${target_name}__staging") {
+ forward_variables_from(invoker,
+ [
+ "testonly",
+ "sources",
+ "deps",
+ ])
+ outputs = [
+ "${initrd_staging}/{{source_file_part}}",
+ ]
+ }
+
+ action(target_name) {
+ forward_variables_from(invoker, [ "testonly" ])
+ script = "//build/image/generate_linux_initrd.py"
+ args = [
+ "--staging",
+ rebase_path(initrd_staging),
+ "--output",
+ rebase_path(initrd_file),
+ ]
+ deps = [
+ ":${target_name}__staging",
+ ]
+ outputs = [
+ initrd_file,
+ ]
+ }
+}
+
# Build the initial RAM disk for the hypervisor.
template("initrd") {
- assert(defined(invoker.primary_vm),
- "initrd() must specify a \"primary_vm\" value")
+ assert(
+ defined(invoker.primary_vm) || defined(invoker.primary_vm_prebuilt),
+ "initrd() must specify a \"primary_vm\" or \"primary_vm_prebuilt\" value")
action(target_name) {
forward_variables_from(invoker, [ "testonly" ])
@@ -125,19 +162,32 @@
initrd_file = "${initrd_base}.img"
initrd_staging = "${initrd_base}"
- deps = [
- invoker.primary_vm,
- ]
+ deps = []
- primary_vm_outputs = get_target_outputs(invoker.primary_vm)
+ if (defined(invoker.primary_vm_prebuilt)) {
+ primary_vm_output = invoker.primary_vm_prebuilt
+ } else {
+ primary_vm_output =
+ get_label_info(invoker.primary_vm, "target_out_dir") + "/" +
+ get_label_info(invoker.primary_vm, "name") + ".bin"
+ deps += [ invoker.primary_vm ]
+ }
args = [
"--primary_vm",
- rebase_path(primary_vm_outputs[0]),
+ rebase_path(primary_vm_output),
"--staging",
rebase_path(initrd_staging),
"--output",
rebase_path(initrd_file),
]
+ if (defined(invoker.primary_initrd)) {
+ deps += [ invoker.primary_initrd ]
+ primary_initrd_outputs = get_target_outputs(invoker.primary_initrd)
+ args += [
+ "--primary_vm_initrd",
+ rebase_path(primary_initrd_outputs[0]),
+ ]
+ }
# Add the info about the secondary VMs. The information about the VMs is
# encoded in lists with the following elements:
diff --git a/build/make.py b/build/make.py
new file mode 100644
index 0000000..d4cc0d8
--- /dev/null
+++ b/build/make.py
@@ -0,0 +1,44 @@
+#!/usr/bin/env python
+#
+# 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.
+
+"""Runs make to build a target."""
+
+import argparse
+import os
+import shutil
+import subprocess
+import sys
+
+
+def Main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument("--directory", required=True)
+ parser.add_argument("--out_file", required=True)
+ parser.add_argument("--copy_out_file", required=True)
+ args, make_args = parser.parse_known_args()
+
+ os.chdir(args.directory)
+ os.environ["PWD"] = args.directory
+ status = subprocess.call(["make"] + make_args)
+ if status != 0:
+ return status
+
+ shutil.copyfile(args.out_file, args.copy_out_file)
+ return 0
+
+
+if __name__ == "__main__":
+ sys.exit(Main())
diff --git a/build/toolchain/BUILD.gn b/build/toolchain/BUILD.gn
index 861bd18..0bfc56b 100644
--- a/build/toolchain/BUILD.gn
+++ b/build/toolchain/BUILD.gn
@@ -12,8 +12,31 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+import("//build/toolchain/embedded.gni")
import("//build/toolchain/host.gni")
host_toolchain("host") {
use_platform = false
}
+
+embedded_clang_toolchain("aarch64_linux_clang") {
+ target = "aarch64-linux-musleabi"
+
+ # TODO: Remove //inc/system if we can stop using the version of stdatomic.h
+ # from the Android prebuilt Clang.
+ extra_cflags =
+ "-nostdinc -isystem" +
+ rebase_path("//prebuilts/linux-aarch64/musl/include") + " -isystem" +
+ rebase_path("//prebuilts/linux-x64/clang/lib64/clang/8.0.4/include") +
+ " -isystem" + rebase_path("//inc/system")
+ extra_defines = "-D_LIBCPP_HAS_MUSL_LIBC=1 -D_GNU_SOURCE=1"
+ extra_ldflags = "-no-pie -lc --library-path=" +
+ rebase_path("//prebuilts/linux-aarch64/musl/lib/") + " " +
+ rebase_path("//prebuilts/linux-aarch64/musl/lib/crt1.o") +
+ " " + rebase_path(
+ "//prebuilts/linux-x64/clang/lib64/clang/8.0.4/lib/linux/libclang_rt.builtins-aarch64-android.a")
+ toolchain_args = {
+ use_platform = true
+ plat_arch = "fake"
+ }
+}
diff --git a/driver/BUILD.gn b/driver/BUILD.gn
new file mode 100644
index 0000000..f6f4119
--- /dev/null
+++ b/driver/BUILD.gn
@@ -0,0 +1,39 @@
+# 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.
+
+action("linux") {
+ script = "//build/make.py"
+ args = [
+ "--directory",
+ rebase_path("linux"),
+ "--out_file",
+ "hafnium.ko",
+ "--copy_out_file",
+ rebase_path("${target_out_dir}/linux/hafnium.ko"),
+ "CC=" + rebase_path("//prebuilts/linux-x64/clang/bin/clang"),
+ "ARCH=arm64",
+ "CROSS_COMPILE=aarch64-linux-gnu-",
+ ]
+ sources = [
+ "linux/Makefile",
+ "linux/hf_call.S",
+ "linux/main.c",
+ ]
+ outputs = [
+ "${target_out_dir}/linux/hafnium.ko",
+ ]
+ deps = [
+ "//third_party:linux_defconfig",
+ ]
+}
diff --git a/inc/system/sys/cdefs.h b/inc/system/sys/cdefs.h
new file mode 100644
index 0000000..ef2bc8f
--- /dev/null
+++ b/inc/system/sys/cdefs.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.
+ */
+
+#pragma once
+
+/*
+ * Empty file to make Android Clang stdatomic.h happy. It includes this internal
+ * glibc header which we don't have, but doesn't actually need it.
+ * TODO: Investigate why Android have replaced the upstream Clang version of
+ * stdatomic.h with one that appears to be from FreeBSD, possibly via Bionic, in
+ * their prebuilt version of Clang. If we can just use the upstream Clang we can
+ * probably remove this workaround.
+ */
diff --git a/kokoro/ubuntu/build.sh b/kokoro/ubuntu/build.sh
index 0d3a909..855a849 100755
--- a/kokoro/ubuntu/build.sh
+++ b/kokoro/ubuntu/build.sh
@@ -36,6 +36,15 @@
CLANG=${PWD}/prebuilts/linux-x64/clang/bin/clang
+# Kokoro does something weird that makes all files look dirty to git diff-index;
+# this fixes it so that the Linux build doesn't think it has a dirty tree for
+# building the Hafnium kernel module (and so end up with a version magic string
+# that doesn't match the prebuilt kernel).
+(
+ cd third_party/linux &&
+ git status
+)
+
#
# Step 1: make sure it builds.
#
@@ -95,12 +104,11 @@
exit 1
fi
-# Step 7: make sure the Linux driver builds and maintains style.
+# Step 7: make sure the Linux driver maintains style. It's already built as part
+# of the tests.
(
export ARCH=arm64 &&
export CROSS_COMPILE=aarch64-linux-gnu- &&
-make CC=${CLANG} -C third_party/linux defconfig modules_prepare &&
cd driver/linux &&
-make CC=${CLANG} &&
make checkpatch
)
diff --git a/kokoro/ubuntu/test.sh b/kokoro/ubuntu/test.sh
index 8940e0b..58bc932 100755
--- a/kokoro/ubuntu/test.sh
+++ b/kokoro/ubuntu/test.sh
@@ -45,3 +45,4 @@
$HFTEST hafnium --initrd test/vmapi/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 --"
diff --git a/prebuilts b/prebuilts
index aecc66e..1521f63 160000
--- a/prebuilts
+++ b/prebuilts
@@ -1 +1 @@
-Subproject commit aecc66e960abf4c22c98b7c3f4cd27a829d64e9e
+Subproject commit 1521f631f051a02d4914d585e4bda39d97deb9af
diff --git a/project/reference b/project/reference
index dc8ad20..bf3991c 160000
--- a/project/reference
+++ b/project/reference
@@ -1 +1 @@
-Subproject commit dc8ad20586c6419ee62cc9f4b02b1dab4b9f240c
+Subproject commit bf3991cb9e86b2b91775365b2cb634a169b3dbd6
diff --git a/src/arch/fake/hftest/BUILD.gn b/src/arch/fake/hftest/BUILD.gn
new file mode 100644
index 0000000..c69041f
--- /dev/null
+++ b/src/arch/fake/hftest/BUILD.gn
@@ -0,0 +1,23 @@
+# 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.
+
+# These components are only used by tests running until Linux VMs.
+
+# Shutdown the system.
+source_set("power_mgmt") {
+ testonly = true
+ sources = [
+ "power_mgmt.c",
+ ]
+}
diff --git a/src/arch/fake/hftest/power_mgmt.c b/src/arch/fake/hftest/power_mgmt.c
new file mode 100644
index 0000000..a171036
--- /dev/null
+++ b/src/arch/fake/hftest/power_mgmt.c
@@ -0,0 +1,27 @@
+/*
+ * 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/arch/vm/power_mgmt.h"
+
+#include <sys/reboot.h>
+
+noreturn void arch_power_off(void)
+{
+ reboot(RB_POWER_OFF);
+ for (;;) {
+ /* This should never be reached. */
+ }
+}
diff --git a/src/arch/fake/inc/hf/arch/vm/power_mgmt.h b/src/arch/fake/inc/hf/arch/vm/power_mgmt.h
new file mode 100644
index 0000000..607705e
--- /dev/null
+++ b/src/arch/fake/inc/hf/arch/vm/power_mgmt.h
@@ -0,0 +1,21 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <stdnoreturn.h>
+
+noreturn void arch_power_off(void);
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();
+}
diff --git a/test/linux/BUILD.gn b/test/linux/BUILD.gn
new file mode 100644
index 0000000..0c8a9c6
--- /dev/null
+++ b/test/linux/BUILD.gn
@@ -0,0 +1,54 @@
+# 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.
+
+import("//build/image/image.gni")
+
+executable("test_binary") {
+ testonly = true
+ sources = [
+ "linux.c",
+ ]
+ deps = [
+ "//test/hftest:hftest_linux",
+ ]
+ output_name = "test_binary"
+}
+
+linux_initrd("linux_test_initrd") {
+ testonly = true
+ sources = [
+ get_label_info(":test_binary(//build/toolchain:aarch64_linux_clang)",
+ "root_out_dir") + "/test_binary",
+ get_label_info("//driver:linux", "target_out_dir") + "/linux/hafnium.ko",
+ ]
+ deps = [
+ ":test_binary(//build/toolchain:aarch64_linux_clang)",
+ "//driver:linux",
+ ]
+}
+
+initrd("linux_test") {
+ testonly = true
+
+ primary_vm_prebuilt = "//prebuilts/linux-aarch64/linux/vmlinuz"
+ primary_initrd = ":linux_test_initrd"
+}
+
+group("linux") {
+ testonly = true
+
+ deps = [
+ ":linux_test",
+ ]
+}
diff --git a/test/linux/linux.c b/test/linux/linux.c
new file mode 100644
index 0000000..3300799
--- /dev/null
+++ b/test/linux/linux.c
@@ -0,0 +1,55 @@
+/*
+ * 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 <fcntl.h>
+#include <unistd.h>
+
+#include "hf/dlog.h"
+
+#include "hftest.h"
+#include <sys/syscall.h>
+
+static int finit_module(int fd, const char *param_values, int flags)
+{
+ return syscall(SYS_finit_module, fd, param_values, flags);
+}
+
+static int delete_module(const char *name, int flags)
+{
+ return syscall(SYS_delete_module, name, flags);
+}
+
+static void insmod_hafnium(void)
+{
+ int module_file = open("/hafnium.ko", O_RDONLY);
+ if (module_file < 0) {
+ FAIL("Failed to load Hafnium kernel module from /hafnium.ko");
+ return;
+ }
+ EXPECT_EQ(finit_module(module_file, "", 0), 0);
+ close(module_file);
+}
+
+static void rmmod_hafnium(void)
+{
+ EXPECT_EQ(delete_module("hafnium", 0), 0);
+}
+
+TEST(linux, load_hafnium)
+{
+ insmod_hafnium();
+ rmmod_hafnium();
+}
diff --git a/third_party/BUILD.gn b/third_party/BUILD.gn
index 7a9ad02..52ae5e9 100644
--- a/third_party/BUILD.gn
+++ b/third_party/BUILD.gn
@@ -50,3 +50,47 @@
":gtest",
]
}
+
+action("linux_defconfig") {
+ script = "//build/make.py"
+ args = [
+ "--directory",
+ rebase_path("linux"),
+ "--out_file",
+ ".config",
+ "--copy_out_file",
+ rebase_path("${target_out_dir}/.config"),
+ "CC=" + rebase_path("//prebuilts/linux-x64/clang/bin/clang"),
+ "ARCH=arm64",
+ "CROSS_COMPILE=aarch64-linux-gnu-",
+ "-j24",
+ "defconfig",
+ "modules_prepare",
+ ]
+ outputs = [
+ # We don't actually care about this, but GN requires us to have some output.
+ "${target_out_dir}/.config",
+ ]
+}
+
+action("linux") {
+ script = "//build/make.py"
+ args = [
+ "--directory",
+ rebase_path("linux"),
+ "--out_file",
+ "arch/arm64/boot/Image",
+ "--copy_out_file",
+ rebase_path("${target_out_dir}/linux.bin"),
+ "CC=" + rebase_path("//prebuilts/linux-x64/clang/bin/clang"),
+ "ARCH=arm64",
+ "CROSS_COMPILE=aarch64-linux-gnu-",
+ "-j24",
+ ]
+ outputs = [
+ "${target_out_dir}/linux.bin",
+ ]
+ deps = [
+ ":linux_defconfig",
+ ]
+}