Run QEMU tests with TF-A.

Bug: 132429380
Change-Id: I967144caa066cb5f6957ff7999764c3d05f9d062
diff --git a/inc/hf/layout.h b/inc/hf/layout.h
index 4193abf..36c3490 100644
--- a/inc/hf/layout.h
+++ b/inc/hf/layout.h
@@ -18,6 +18,9 @@
 
 #include "hf/addr.h"
 
+#define LINUX_ALIGNMENT 0x200000
+#define LINUX_OFFSET 0x80000
+
 paddr_t layout_text_begin(void);
 paddr_t layout_text_end(void);
 
diff --git a/kokoro/test.sh b/kokoro/test.sh
index c35f5c2..300a2b1 100755
--- a/kokoro/test.sh
+++ b/kokoro/test.sh
@@ -27,6 +27,7 @@
 set -x
 
 USE_FVP=false
+USE_TFA=false
 SKIP_LONG_RUNNING_TESTS=false
 RUN_ALL_QEMU_CPUS=false
 
@@ -35,6 +36,8 @@
   case "$1" in
     --fvp) USE_FVP=true
       ;;
+    --tfa) USE_TFA=true
+      ;;
     --skip-long-running-tests) SKIP_LONG_RUNNING_TESTS=true
       ;;
     --run-all-qemu-cpus) RUN_ALL_QEMU_CPUS=true
@@ -62,6 +65,10 @@
   HFTEST+=(--out "$OUT/qemu_aarch64_clang")
   HFTEST+=(--out_initrd "$OUT/qemu_aarch64_vm_clang")
 fi
+if [ $USE_TFA == true ]
+then
+  HFTEST+=(--tfa)
+fi
 if [ $SKIP_LONG_RUNNING_TESTS == true ]
 then
   HFTEST+=(--skip-long-running-tests)
diff --git a/prebuilts b/prebuilts
index 6fcd301..cebbed6 160000
--- a/prebuilts
+++ b/prebuilts
@@ -1 +1 @@
-Subproject commit 6fcd30188886b8cdd7cdcd1de8be813914eb9015
+Subproject commit cebbed6846ccfef8ff5973f6590eaf89ef80d84b
diff --git a/project/reference b/project/reference
index b38816f..92627a8 160000
--- a/project/reference
+++ b/project/reference
@@ -1 +1 @@
-Subproject commit b38816f04b97fcd3ce9eb688ff8154b156d9fb38
+Subproject commit 92627a8f035cc11541eb4765f8e2d249b5a9b1b1
diff --git a/src/BUILD.gn b/src/BUILD.gn
index 9c76668..70eafd1 100644
--- a/src/BUILD.gn
+++ b/src/BUILD.gn
@@ -52,7 +52,6 @@
 # sharing.
 source_set("src_testable") {
   sources = [
-    "abort.c",
     "api.c",
     "cpu.c",
     "manifest.c",
@@ -64,6 +63,7 @@
   ]
 
   deps = [
+    ":abort",
     ":fdt",
     ":fdt_handler",
     ":memiter",
@@ -167,6 +167,12 @@
   ]
 }
 
+source_set("abort") {
+  sources = [
+    "abort.c",
+  ]
+}
+
 executable("unit_tests") {
   testonly = true
   sources = [
diff --git a/src/arch/aarch64/qemuloader/BUILD.gn b/src/arch/aarch64/qemuloader/BUILD.gn
new file mode 100644
index 0000000..fb6065b
--- /dev/null
+++ b/src/arch/aarch64/qemuloader/BUILD.gn
@@ -0,0 +1,74 @@
+# Copyright 2020 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")
+import("//build/toolchain/platform.gni")
+
+# The bootloader image.
+image_binary("qemuloader") {
+  image_name = "qemuloader"
+  deps = [
+    ":loader",
+  ]
+}
+
+source_set("loader") {
+  public_configs = [
+    "//src/arch/aarch64:config",
+    "//third_party/dtc:libfdt_config",
+  ]
+  sources = [
+    "entry.S",
+    "fwcfg.c",
+    "loader.c",
+  ]
+
+  deps = [
+    "//src:abort",
+    "//src:dlog",
+    "//src:layout",
+    "//src:panic",
+    "//src/arch/${plat_arch}:entry",
+    "//third_party/dtc:libfdt",
+  ]
+}
+
+copy("tfa_copy") {
+  sources = [
+    "//prebuilts/linux-aarch64/arm-trusted-firmware/qemu/bl2.bin",
+    "//prebuilts/linux-aarch64/arm-trusted-firmware/qemu/bl31.bin",
+  ]
+  outputs = [
+    "$root_out_dir/{{source_file_part}}",
+  ]
+}
+
+copy("qemuloader_copy") {
+  sources = [
+    "$root_out_dir/qemuloader.bin",
+  ]
+  deps = [
+    ":qemuloader",
+  ]
+  outputs = [
+    "$root_out_dir/bl33.bin",
+  ]
+}
+
+group("bl") {
+  deps = [
+    ":qemuloader_copy",
+    ":tfa_copy",
+  ]
+}
diff --git a/src/arch/aarch64/qemuloader/entry.S b/src/arch/aarch64/qemuloader/entry.S
new file mode 100644
index 0000000..47b83f5
--- /dev/null
+++ b/src/arch/aarch64/qemuloader/entry.S
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2020 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.
+ */
+
+.section .init.image_entry, "ax"
+.global image_entry
+image_entry:
+	/* Prepare the stack. */
+	adr x30, kstack + 4096
+	mov sp, x30
+
+	/* Call into C code. */
+	bl kmain
+
+	/* Loop forever waiting for interrupts. */
+0:	wfi
+	b 0b
diff --git a/src/arch/aarch64/qemuloader/fwcfg.c b/src/arch/aarch64/qemuloader/fwcfg.c
new file mode 100644
index 0000000..a1877b4
--- /dev/null
+++ b/src/arch/aarch64/qemuloader/fwcfg.c
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2020 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 "fwcfg.h"
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include "hf/arch/std.h"
+
+#include "hf/io.h"
+
+#define FW_CFG_CONTROL_ERROR htobe32(1 << 0)
+#define FW_CFG_CONTROL_READ htobe32(1 << 1)
+#define FW_CFG_CONTROL_SKIP htobe32(1 << 2)
+#define FW_CFG_CONTROL_SELECT htobe32(1 << 3)
+#define FW_CFG_CONTROL_WRITE htobe32(1 << 4)
+
+#define FW_CFG_BASE 0x09020000
+#define FW_CFG_DATA8 IO8_C(FW_CFG_BASE + 0)
+#define FW_CFG_DATA32 IO32_C(FW_CFG_BASE + 0)
+#define FW_CFG_SELECTOR IO16_C(FW_CFG_BASE + 8)
+#define FW_CFG_DMA IO64_C(FW_CFG_BASE + 16)
+
+struct fw_cfg_dma_access {
+	uint32_t control;
+	uint32_t length;
+	uint64_t address;
+};
+
+uint32_t fw_cfg_read_uint32(uint16_t key)
+{
+	io_write16(FW_CFG_SELECTOR, htobe16(key));
+	return io_read32(FW_CFG_DATA32);
+}
+
+void fw_cfg_read_bytes(uint16_t key, uintptr_t destination, uint32_t length)
+{
+	uint8_t *dest = (uint8_t *)destination;
+	size_t i;
+
+	io_write16(FW_CFG_SELECTOR, htobe16(key));
+	for (i = 0; i < length; ++i) {
+		dest[i] = io_read8(FW_CFG_DATA8);
+	}
+}
+
+bool fw_cfg_read_dma(uint16_t key, uintptr_t destination, uint32_t length)
+{
+	struct fw_cfg_dma_access access = {
+		.control = FW_CFG_CONTROL_READ,
+		.length = htobe32(length),
+		.address = htobe64(destination),
+	};
+	uint64_t access_address = (uint64_t)&access;
+
+	io_write16(FW_CFG_SELECTOR, htobe16(key));
+	io_write64(FW_CFG_DMA, htobe64(access_address));
+
+	return access.control != 0;
+}
diff --git a/src/arch/aarch64/qemuloader/fwcfg.h b/src/arch/aarch64/qemuloader/fwcfg.h
new file mode 100644
index 0000000..948314d
--- /dev/null
+++ b/src/arch/aarch64/qemuloader/fwcfg.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2020 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 <stdbool.h>
+#include <stdint.h>
+
+#define FW_CFG_ID 0x01
+#define FW_CFG_KERNEL_SIZE 0x08
+#define FW_CFG_INITRD_SIZE 0x0b
+#define FW_CFG_KERNEL_DATA 0x11
+#define FW_CFG_INITRD_DATA 0x12
+
+uint32_t fw_cfg_read_uint32(uint16_t key);
+void fw_cfg_read_bytes(uint16_t key, uintptr_t destination, uint32_t length);
+bool fw_cfg_read_dma(uint16_t key, uintptr_t destination, uint32_t length);
diff --git a/src/arch/aarch64/qemuloader/loader.c b/src/arch/aarch64/qemuloader/loader.c
new file mode 100644
index 0000000..84621bf
--- /dev/null
+++ b/src/arch/aarch64/qemuloader/loader.c
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2020 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 <stdnoreturn.h>
+
+#include "hf/arch/std.h"
+
+#include "hf/addr.h"
+#include "hf/dlog.h"
+#include "hf/layout.h"
+#include "hf/panic.h"
+
+#include "fwcfg.h"
+#include "libfdt.h"
+
+#define FDT_MAX_SIZE 0x10000
+
+alignas(4096) uint8_t kstack[4096];
+
+typedef void entry_point(struct fdt_header *, uint64_t, uint64_t, uint64_t);
+
+static noreturn void jump_to_kernel(struct fdt_header *fdt,
+				    uintptr_t kernel_start)
+{
+	entry_point *kernel_entry = (entry_point *)kernel_start;
+
+	kernel_entry(fdt, 0, 0, 0);
+
+	/* This should never be reached. */
+	for (;;) {
+	}
+}
+
+static bool update_fdt(struct fdt_header *fdt, uintptr_t initrd_start,
+		       uint32_t initrd_size)
+{
+	uintptr_t initrd_end = initrd_start + initrd_size;
+	int ret;
+	int chosen_offset;
+
+	ret = fdt_check_header(fdt);
+	if (ret != 0) {
+		dlog("FDT failed validation: %d\n", ret);
+		return false;
+	}
+	ret = fdt_open_into(fdt, fdt, FDT_MAX_SIZE);
+	if (ret != 0) {
+		dlog("FDT failed to open: %d\n", ret);
+		return false;
+	}
+
+	chosen_offset = fdt_path_offset(fdt, "/chosen");
+	if (chosen_offset <= 0) {
+		dlog("Unable to find '/chosen'\n");
+		return false;
+	}
+
+	/* Patch FDT to point to new ramdisk. */
+	ret = fdt_setprop_u64(fdt, chosen_offset, "linux,initrd-start",
+			      initrd_start);
+	if (ret != 0) {
+		dlog("Unable to write linux,initrd-start: %d\n", ret);
+		return false;
+	}
+
+	ret = fdt_setprop_u64(fdt, chosen_offset, "linux,initrd-end",
+			      initrd_end);
+	if (ret != 0) {
+		dlog("Unable to write linux,initrd-end\n");
+		return false;
+	}
+
+	ret = fdt_pack(fdt);
+	if (ret != 0) {
+		dlog("Failed to pack FDT.\n");
+		return false;
+	}
+
+	return true;
+}
+
+noreturn void kmain(struct fdt_header *fdt)
+{
+	uintptr_t kernel_start;
+	uint32_t kernel_size;
+
+	/* Load the initrd just after this bootloader. */
+	paddr_t image_end = layout_image_end();
+	uintptr_t initrd_start = align_up(pa_addr(image_end), LINUX_ALIGNMENT);
+	uint32_t initrd_size = fw_cfg_read_uint32(FW_CFG_INITRD_SIZE);
+
+	dlog("Initrd start %#x, size %#x\n", initrd_start, initrd_size);
+	fw_cfg_read_bytes(FW_CFG_INITRD_DATA, initrd_start, initrd_size);
+
+	/*
+	 * Load the kernel after the initrd. Follow Linux alignment conventions
+	 * just in case.
+	 */
+	kernel_start = align_up(initrd_start + initrd_size, LINUX_ALIGNMENT) +
+		       LINUX_OFFSET;
+	kernel_size = fw_cfg_read_uint32(FW_CFG_KERNEL_SIZE);
+	dlog("Kernel start %#x, size %#x\n", kernel_start, kernel_size);
+	fw_cfg_read_bytes(FW_CFG_KERNEL_DATA, kernel_start, kernel_size);
+
+	/* Update FDT to point to initrd. */
+	if (initrd_size > 0) {
+		if (update_fdt(fdt, initrd_start, initrd_size)) {
+			dlog("Updated FDT with initrd.\n");
+		} else {
+			panic("Failed to update FDT.");
+		}
+	}
+
+	/* Jump to the kernel. */
+	jump_to_kernel(fdt, kernel_start);
+}
diff --git a/src/layout.c b/src/layout.c
index edb583d..6cbecd8 100644
--- a/src/layout.c
+++ b/src/layout.c
@@ -144,5 +144,6 @@
 	 * the alignment from the header of the binary, or have a bootloader
 	 * within the VM do so.
 	 */
-	return pa_init(align_up(pa_addr(image_end), 0x200000) + 0x80000);
+	return pa_init(align_up(pa_addr(image_end), LINUX_ALIGNMENT) +
+		       LINUX_OFFSET);
 }
diff --git a/test/hftest/hftest.py b/test/hftest/hftest.py
index 46bbccd..54dc7df 100755
--- a/test/hftest/hftest.py
+++ b/test/hftest/hftest.py
@@ -158,14 +158,14 @@
         platform."""
         return DriverRunState(self.args.artifacts.create_file(run_name, ".log"))
 
-    def exec_logged(self, run_state, exec_args):
+    def exec_logged(self, run_state, exec_args, cwd=None):
         """Run a subprocess on behalf of a Driver subclass and append its
         stdout and stderr to the main log."""
         assert(run_state.ret_code == 0)
         with open(run_state.log_path, "a") as f:
             f.write("$ {}\r\n".format(" ".join(exec_args)))
             f.flush()
-            ret_code = subprocess.call(exec_args, stdout=f, stderr=f)
+            ret_code = subprocess.call(exec_args, stdout=f, stderr=f, cwd=cwd)
             if ret_code != 0:
                 run_state.set_ret_code(ret_code)
                 raise DriverRunException()
@@ -195,8 +195,10 @@
 class QemuDriver(Driver):
     """Driver which runs tests in QEMU."""
 
-    def __init__(self, args):
+    def __init__(self, args, qemu_wd, tfa):
         Driver.__init__(self, args)
+        self.qemu_wd = qemu_wd
+        self.tfa = tfa
 
     def gen_exec_args(self, test_args, is_long_running):
         """Generate command line arguments for QEMU."""
@@ -206,15 +208,22 @@
         cpu = self.args.cpu or "max"
         exec_args = [
             "timeout", "--foreground", time_limit,
-            "./prebuilts/linux-x64/qemu/qemu-system-aarch64",
+            os.path.abspath("prebuilts/linux-x64/qemu/qemu-system-aarch64"),
             "-machine", "virt,virtualization=on,gic_version=3",
-            "-cpu", cpu, "-smp", "4", "-m", "64M",
+            "-cpu", cpu, "-smp", "4", "-m", "1G",
             "-nographic", "-nodefaults", "-serial", "stdio",
-            "-d", "unimp", "-kernel", self.args.kernel,
+            "-d", "unimp", "-kernel", os.path.abspath(self.args.kernel),
         ]
 
+        if self.tfa:
+            exec_args += ["-bios",
+                os.path.abspath(
+                    "prebuilts/linux-aarch64/arm-trusted-firmware/qemu/bl1.bin"
+                ), "-machine", "secure=on", "-semihosting-config",
+                "enable,target=native"]
+
         if self.args.initrd:
-            exec_args += ["-initrd", self.args.initrd]
+            exec_args += ["-initrd", os.path.abspath(self.args.initrd)]
 
         vm_args = join_if_not_None(self.args.vm_args, test_args)
         if vm_args:
@@ -229,7 +238,8 @@
         try:
             # Execute test in QEMU..
             exec_args = self.gen_exec_args(test_args, is_long_running)
-            self.exec_logged(run_state, exec_args)
+            self.exec_logged(run_state, exec_args,
+                cwd=self.qemu_wd)
         except DriverRunException:
             pass
 
@@ -580,6 +590,7 @@
     parser.add_argument("--skip-long-running-tests", action="store_true")
     parser.add_argument("--cpu",
         help="Selects the CPU configuration for the run environment.")
+    parser.add_argument("--tfa", action="store_true")
     args = parser.parse_args()
 
     # Resolve some paths.
@@ -600,7 +611,7 @@
             args.serial_dev, args.serial_baudrate, not args.serial_no_init_wait)
 
     if args.driver == "qemu":
-        driver = QemuDriver(driver_args)
+        driver = QemuDriver(driver_args, args.out, args.tfa)
     elif args.driver == "fvp":
         driver = FvpDriver(driver_args)
     elif args.driver == "serial":
diff --git a/third_party/dtc b/third_party/dtc
index f4d9058..2cf0aa8 160000
--- a/third_party/dtc
+++ b/third_party/dtc
@@ -1 +1 @@
-Subproject commit f4d9058c550b8b85c760bd53c1f358671b635d5e
+Subproject commit 2cf0aa8298452c2179138ae11417a0cc2c2b3a63