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