Run QEMU tests with TF-A.

Bug: 132429380
Change-Id: I967144caa066cb5f6957ff7999764c3d05f9d062
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);
 }