Initial tests of the arch interface.

These tests use hftest running as the hypervisor by making hftest
retargetable to this new context.

Change-Id: Ie0472932b8156d57913286cc30de7a2d2db32149
diff --git a/test/vm/secondaries/BUILD.gn b/test/arch/BUILD.gn
similarity index 71%
copy from test/vm/secondaries/BUILD.gn
copy to test/arch/BUILD.gn
index cd0ad69..f3496a9 100644
--- a/test/vm/secondaries/BUILD.gn
+++ b/test/arch/BUILD.gn
@@ -13,39 +13,25 @@
 # limitations under the License.
 
 import("//build/image/image.gni")
+import("//build/toolchain/platform.gni")
 
-vm_kernel("echo") {
+group("arch") {
   testonly = true
 
-  sources = [
-    "echo.c",
-  ]
-
   deps = [
-    "//test/vm:secondary_vm",
+    ":arch_test",
   ]
 }
 
-vm_kernel("relay_a") {
+hypervisor("arch_test") {
   testonly = true
 
   sources = [
-    "relay_a.c",
+    "mm_test.c",
   ]
 
   deps = [
-    "//test/vm:secondary_vm",
-  ]
-}
-
-vm_kernel("relay_b") {
-  testonly = true
-
-  sources = [
-    "relay_b.c",
-  ]
-
-  deps = [
-    "//test/vm:secondary_vm",
+    "//src/arch/${plat_arch}:arch",
+    "//test/hftest:hftest_hypervisor",
   ]
 }
diff --git a/test/arch/mm_test.c b/test/arch/mm_test.c
new file mode 100644
index 0000000..4af0c7a
--- /dev/null
+++ b/test/arch/mm_test.c
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2018 Google LLC
+ *
+ * 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/mm.h"
+
+#include "hf/arch/mm.h"
+
+#include "hftest.h"
+
+/** There must be at least two levels in the page table.  */
+#define MAX_LEVEL_LOWER_BOUND 1
+
+/**
+ * This is the number of levels that are tested and is constrained as it
+ * controls the depth of recursion in the memory management code.
+ */
+#define MAX_LEVEL_UPPER_BOUND 3
+
+/** X macro to expand tests for all levels. */
+#define EXPAND_LEVEL_TESTS \
+	LEVEL_TEST(0)      \
+	LEVEL_TEST(1)      \
+	LEVEL_TEST(2)      \
+	LEVEL_TEST(3)
+
+/* TODO: work out how to run these test against the host fake arch. */
+
+/**
+ * A block must be allowed at level 0 as this is the level which represents
+ * pages.
+ */
+TEST(arch_mm, block_allowed_at_level0)
+{
+	ASSERT_TRUE(arch_mm_is_block_allowed(0));
+}
+
+/**
+ * The maximum level must be within acceptable bounds.
+ */
+TEST(arch_mm, max_level_stage1)
+{
+	int mode = MM_MODE_STAGE1;
+	uint8_t max_level = arch_mm_max_level(mode);
+	EXPECT_GE(max_level, MAX_LEVEL_LOWER_BOUND);
+	EXPECT_LE(max_level, MAX_LEVEL_UPPER_BOUND);
+}
+
+/* TODO: initialize arch_mm and check max level of stage-2. */
+
+/**
+ * A block is present and mutually exclusive from a table.
+ */
+#define LEVEL_TEST(lvl)                                                      \
+	TEST(arch_mm, block_properties_level##lvl)                           \
+	{                                                                    \
+		uint8_t level = lvl;                                         \
+		uint64_t attrs = arch_mm_mode_to_attrs(0);                   \
+		pte_t block_pte;                                             \
+                                                                             \
+		/* Test doesn't apply if a block is not allowed. */          \
+		if (!arch_mm_is_block_allowed(level)) {                      \
+			return;                                              \
+		}                                                            \
+                                                                             \
+		block_pte = arch_mm_block_pte(level, pa_init(0x12345678000), \
+					      attrs);                        \
+                                                                             \
+		EXPECT_TRUE(arch_mm_pte_is_present(block_pte, level));       \
+		EXPECT_TRUE(arch_mm_pte_is_block(block_pte, level));         \
+		EXPECT_FALSE(arch_mm_pte_is_table(block_pte, level));        \
+	}
+EXPAND_LEVEL_TESTS
+#undef LEVEL_TEST
diff --git a/test/hftest/BUILD.gn b/test/hftest/BUILD.gn
new file mode 100644
index 0000000..bc956f8
--- /dev/null
+++ b/test/hftest/BUILD.gn
@@ -0,0 +1,77 @@
+# Copyright 2018 Google LLC
+#
+# 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/toolchain/platform.gni")
+
+config("hftest_config") {
+  include_dirs = [ "inc" ]
+}
+
+# Testing framework for a primary vm.
+source_set("hftest_primary_vm") {
+  testonly = true
+
+  public_configs = [ ":hftest_config" ]
+
+  sources = [
+    "hftest.c",
+  ]
+
+  deps = [
+    "//src:common",
+    "//src:dlog",
+    "//src:fdt",
+    "//src:memiter",
+    "//src/arch/${plat_arch}:entry",
+    "//src/arch/${plat_arch}/hftest:entry",
+    "//src/arch/${plat_arch}/hftest:hf_call",
+    "//src/arch/${plat_arch}/hftest:power_mgmt",
+  ]
+}
+
+# Testing framework for a secondary vm. It's currently just a slave VM and
+# can't affect the tests directly.
+source_set("hftest_secondary_vm") {
+  testonly = true
+
+  deps = [
+    "//src:common",
+    "//src:dlog",
+    "//src/arch/${plat_arch}:entry",
+    "//src/arch/${plat_arch}/hftest:entry",
+    "//src/arch/${plat_arch}/hftest:hf_call",
+    "//src/arch/${plat_arch}/hftest:power_mgmt",
+  ]
+}
+
+# Testing framework for a hypervisor.
+source_set("hftest_hypervisor") {
+  testonly = true
+
+  public_configs = [ ":hftest_config" ]
+
+  sources = [
+    "hftest.c",
+  ]
+
+  deps = [
+    "//src:common",
+    "//src:dlog",
+    "//src:fdt",
+    "//src:memiter",
+    "//src/arch/${plat_arch}:entry",
+    "//src/arch/${plat_arch}/hftest:entry",
+    "//src/arch/${plat_arch}/hftest:power_mgmt",
+  ]
+}
diff --git a/test/vm/hftest.c b/test/hftest/hftest.c
similarity index 100%
rename from test/vm/hftest.c
rename to test/hftest/hftest.c
diff --git a/test/vm/hftest.py b/test/hftest/hftest.py
similarity index 89%
rename from test/vm/hftest.py
rename to test/hftest/hftest.py
index d80d26b..4a00a52 100755
--- a/test/vm/hftest.py
+++ b/test/hftest/hftest.py
@@ -32,14 +32,15 @@
 import sys
 
 
-def qemu(hafnium, initrd, args, log):
+def qemu(image, initrd, args, log):
     qemu_args = [
         "timeout", "--foreground", "5s",
         "./prebuilts/linux-x64/qemu/qemu-system-aarch64", "-M", "virt,gic_version=3", "-cpu",
         "cortex-a57", "-smp", "4", "-m", "16M", "-machine", "virtualization=true",
-        "-nographic", "-nodefaults", "-serial", "stdio", "-kernel", hafnium,
-        "-initrd", initrd
+        "-nographic", "-nodefaults", "-serial", "stdio", "-kernel", image,
     ]
+    if initrd:
+      qemu_args += ["-initrd", initrd]
     if args:
         qemu_args += ["-append", args]
     # Save the log to a file.
@@ -71,22 +72,27 @@
 
 def Main():
     parser = argparse.ArgumentParser()
+    parser.add_argument("image")
     parser.add_argument("--out", required=True)
     parser.add_argument("--log", required=True)
-    parser.add_argument("--initrd", required=True)
+    parser.add_argument("--initrd")
     parser.add_argument("--suite")
     parser.add_argument("--test")
     args = parser.parse_args()
     # Resolve some paths.
-    hafnium = os.path.join(args.out, "hafnium.bin")
-    initrd = os.path.join(args.out, "initrd", args.initrd + ".img")
-    log = os.path.join(args.log, args.initrd)
+    image = os.path.join(args.out, args.image + ".bin")
+    initrd = None
+    suite = args.image
+    if args.initrd:
+        initrd = os.path.join(args.out, "initrd", args.initrd + ".img")
+        suite += "_" + args.initrd
+    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(hafnium, initrd, "json", os.path.join(log, "json.log"))
+        out = qemu(image, initrd, "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))
@@ -97,7 +103,7 @@
         suite_re = re.compile(args.suite or ".*")
         test_re = re.compile(args.test or ".*")
         sponge = ET.Element("testsuites")
-        sponge.set("name", args.initrd)
+        sponge.set("name", suite)
         sponge.set(
             "timestamp",
             datetime.datetime.now().replace(microsecond=0).isoformat())
@@ -121,7 +127,7 @@
                 print("      RUN", test)
                 test_log = os.path.join(log,
                                         suite["name"] + "." + test + ".log")
-                out = qemu(hafnium, initrd, "run {} {}".format(
+                out = qemu(image, initrd, "run {} {}".format(
                     suite["name"], test), test_log)
                 sponge_log.write(out)
                 sponge_log.write("\r\n\r\n")
diff --git a/test/vm/hftest.h b/test/hftest/inc/hftest.h
similarity index 97%
rename from test/vm/hftest.h
rename to test/hftest/inc/hftest.h
index 0f10eca..290b467 100644
--- a/test/vm/hftest.h
+++ b/test/hftest/inc/hftest.h
@@ -45,6 +45,9 @@
 #define ASSERT_GE(x, y) ASSERT_OP(x, y, >=, true)
 #define ASSERT_GT(x, y) ASSERT_OP(x, y, >, true)
 
+#define ASSERT_TRUE(x) ASSERT_EQ(x, true);
+#define ASSERT_FALSE(x) ASSERT_EQ(x, false);
+
 #define EXPECT_EQ(x, y) ASSERT_OP(x, y, ==, false)
 #define EXPECT_NE(x, y) ASSERT_OP(x, y, !=, false)
 #define EXPECT_LE(x, y) ASSERT_OP(x, y, <=, false)
@@ -52,6 +55,9 @@
 #define EXPECT_GE(x, y) ASSERT_OP(x, y, >=, false)
 #define EXPECT_GT(x, y) ASSERT_OP(x, y, >, false)
 
+#define EXPECT_TRUE(x) EXPECT_EQ(x, true);
+#define EXPECT_FALSE(x) EXPECT_EQ(x, false);
+
 /*
  * This must be used exactly once in a test image to signal to the linker that
  * the .hftest section is allowed to be included in the generated image.
diff --git a/test/vm/BUILD.gn b/test/vmapi/BUILD.gn
similarity index 65%
rename from test/vm/BUILD.gn
rename to test/vmapi/BUILD.gn
index 7fe6e6a..84feced 100644
--- a/test/vm/BUILD.gn
+++ b/test/vmapi/BUILD.gn
@@ -15,7 +15,7 @@
 import("//build/image/image.gni")
 import("//build/toolchain/platform.gni")
 
-group("vm_tests") {
+group("vmapi") {
   testonly = true
 
   deps = [
@@ -24,41 +24,6 @@
   ]
 }
 
-# Primary VMs host the test framework.
-source_set("hftest_vm") {
-  testonly = true
-
-  sources = [
-    "hftest.c",
-    "hftest.h",
-  ]
-
-  deps = [
-    "//src:common",
-    "//src:dlog",
-    "//src:fdt",
-    "//src:memiter",
-    "//src/arch/${plat_arch}:entry",
-    "//src/arch/${plat_arch}/vm:hf_call",
-    "//src/arch/${plat_arch}/vm:power_mgmt",
-    "//src/arch/${plat_arch}/vm:vm_entry",
-  ]
-}
-
-# Secondary VMs can be used as part of a test in the primary.
-source_set("secondary_vm") {
-  testonly = true
-
-  deps = [
-    "//src:common",
-    "//src:dlog",
-    "//src/arch/${plat_arch}:entry",
-    "//src/arch/${plat_arch}/vm:hf_call",
-    "//src/arch/${plat_arch}/vm:power_mgmt",
-    "//src/arch/${plat_arch}/vm:vm_entry",
-  ]
-}
-
 # Tests with no secondary VMs.
 vm_kernel("primary_only_test_vm") {
   testonly = true
@@ -68,7 +33,7 @@
   ]
 
   deps = [
-    ":hftest_vm",
+    "//test/hftest:hftest_primary_vm",
   ]
 }
 
@@ -89,7 +54,7 @@
   ]
 
   deps = [
-    ":hftest_vm",
+    "//test/hftest:hftest_primary_vm",
   ]
 }
 
@@ -108,7 +73,7 @@
   ]
 
   deps = [
-    ":hftest_vm",
+    "//test/hftest:hftest_primary_vm",
   ]
 }
 
@@ -121,19 +86,19 @@
       "1048576",
       "1",
       "relay_a",
-      "//test/vm/secondaries:relay_a",
+      "//test/vmapi/secondaries:relay_a",
     ],
     [
       "1048576",
       "1",
       "relay_b",
-      "//test/vm/secondaries:relay_b",
+      "//test/vmapi/secondaries:relay_b",
     ],
     [
       "1048576",
       "1",
       "echo",
-      "//test/vm/secondaries:echo",
+      "//test/vmapi/secondaries:echo",
     ],
   ]
 }
diff --git a/test/vm/exceptions.S b/test/vmapi/exceptions.S
similarity index 100%
rename from test/vm/exceptions.S
rename to test/vmapi/exceptions.S
diff --git a/test/vm/gicv3.c b/test/vmapi/gicv3.c
similarity index 100%
rename from test/vm/gicv3.c
rename to test/vmapi/gicv3.c
diff --git a/test/vm/interrupts.c b/test/vmapi/interrupts.c
similarity index 100%
rename from test/vm/interrupts.c
rename to test/vmapi/interrupts.c
diff --git a/test/vm/interrupts.h b/test/vmapi/interrupts.h
similarity index 100%
rename from test/vm/interrupts.h
rename to test/vmapi/interrupts.h
diff --git a/test/vm/primary_only.c b/test/vmapi/primary_only.c
similarity index 100%
rename from test/vm/primary_only.c
rename to test/vmapi/primary_only.c
diff --git a/test/vm/primary_with_secondaries.c b/test/vmapi/primary_with_secondaries.c
similarity index 100%
rename from test/vm/primary_with_secondaries.c
rename to test/vmapi/primary_with_secondaries.c
diff --git a/test/vm/secondaries/BUILD.gn b/test/vmapi/secondaries/BUILD.gn
similarity index 87%
rename from test/vm/secondaries/BUILD.gn
rename to test/vmapi/secondaries/BUILD.gn
index cd0ad69..577fe93 100644
--- a/test/vm/secondaries/BUILD.gn
+++ b/test/vmapi/secondaries/BUILD.gn
@@ -22,7 +22,7 @@
   ]
 
   deps = [
-    "//test/vm:secondary_vm",
+    "//test/hftest:hftest_secondary_vm",
   ]
 }
 
@@ -34,7 +34,7 @@
   ]
 
   deps = [
-    "//test/vm:secondary_vm",
+    "//test/hftest:hftest_secondary_vm",
   ]
 }
 
@@ -46,6 +46,6 @@
   ]
 
   deps = [
-    "//test/vm:secondary_vm",
+    "//test/hftest:hftest_secondary_vm",
   ]
 }
diff --git a/test/vm/secondaries/echo.c b/test/vmapi/secondaries/echo.c
similarity index 100%
rename from test/vm/secondaries/echo.c
rename to test/vmapi/secondaries/echo.c
diff --git a/test/vm/secondaries/relay_a.c b/test/vmapi/secondaries/relay_a.c
similarity index 100%
rename from test/vm/secondaries/relay_a.c
rename to test/vmapi/secondaries/relay_a.c
diff --git a/test/vm/secondaries/relay_b.c b/test/vmapi/secondaries/relay_b.c
similarity index 100%
rename from test/vm/secondaries/relay_b.c
rename to test/vmapi/secondaries/relay_b.c