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