feat(qemu): implement firmware handoff on qemu

Implement firmware handoff from BL2 to BL33 on qemu platform
compliant to Firmware handoff specification v0.9.

Change-Id: Id8d5206a71ef6ec97cf3c97995de328ebf0600cc
Signed-off-by: Raymond Mao <raymond.mao@linaro.org>
diff --git a/plat/qemu/common/qemu_bl2_setup.c b/plat/qemu/common/qemu_bl2_setup.c
index c4d235e..231f23a 100644
--- a/plat/qemu/common/qemu_bl2_setup.c
+++ b/plat/qemu/common/qemu_bl2_setup.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015-2022, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2015-2023, Arm Limited and Contributors. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -18,6 +18,9 @@
 #include <common/fdt_fixup.h>
 #include <common/fdt_wrappers.h>
 #include <lib/optee_utils.h>
+#if TRANSFER_LIST
+#include <lib/transfer_list.h>
+#endif
 #include <lib/utils.h>
 #include <plat/common/platform.h>
 
@@ -48,6 +51,9 @@
 
 /* Data structure which holds the extents of the trusted SRAM for BL2 */
 static meminfo_t bl2_tzram_layout __aligned(CACHE_WRITEBACK_GRANULE);
+#if TRANSFER_LIST
+static struct transfer_list_header *bl2_tl;
+#endif
 
 void bl2_early_platform_setup2(u_register_t arg0, u_register_t arg1,
 			       u_register_t arg2, u_register_t arg3)
@@ -73,6 +79,9 @@
 
 static void update_dt(void)
 {
+#if TRANSFER_LIST
+	struct transfer_list_entry *te;
+#endif
 	int ret;
 	void *fdt = (void *)(uintptr_t)ARM_PRELOADED_DTB_BASE;
 
@@ -95,16 +104,40 @@
 	ret = fdt_pack(fdt);
 	if (ret < 0)
 		ERROR("Failed to pack Device Tree at %p: error %d\n", fdt, ret);
+
+#if TRANSFER_LIST
+	// create a TE
+	te = transfer_list_add(bl2_tl, TL_TAG_FDT, fdt_totalsize(fdt), fdt);
+	if (!te) {
+		ERROR("Failed to add FDT entry to Transfer List\n");
+		return;
+	}
+#endif
 }
 
 void bl2_platform_setup(void)
 {
+#if TRANSFER_LIST
+	bl2_tl = transfer_list_init((void *)(uintptr_t)FW_HANDOFF_BASE,
+				    FW_HANDOFF_SIZE);
+	if (!bl2_tl) {
+		ERROR("Failed to initialize Transfer List at 0x%lx\n",
+		      (unsigned long)FW_HANDOFF_BASE);
+	}
+#endif
 	security_setup();
 	update_dt();
 
 	/* TODO Initialize timer */
 }
 
+void qemu_bl2_sync_transfer_list(void)
+{
+#if TRANSFER_LIST
+	transfer_list_update_checksum(bl2_tl);
+#endif
+}
+
 void bl2_plat_arch_setup(void)
 {
 	const mmap_region_t bl_regions[] = {
@@ -221,6 +254,10 @@
 #if defined(SPD_spmd)
 	bl_mem_params_node_t *bl32_mem_params = NULL;
 #endif
+#if TRANSFER_LIST
+	struct transfer_list_header *ns_tl = NULL;
+	struct transfer_list_entry *te = NULL;
+#endif
 
 	assert(bl_mem_params);
 
@@ -275,6 +312,8 @@
 		pager_mem_params->ep_info.lr_svc = bl_mem_params->ep_info.pc;
 #endif
 
+		bl_mem_params->ep_info.spsr = qemu_get_spsr_for_bl33_entry();
+
 #if ARM_LINUX_KERNEL_AS_BL33
 		/*
 		 * According to the file ``Documentation/arm64/booting.txt`` of
@@ -287,12 +326,49 @@
 		bl_mem_params->ep_info.args.arg1 = 0U;
 		bl_mem_params->ep_info.args.arg2 = 0U;
 		bl_mem_params->ep_info.args.arg3 = 0U;
+#elif TRANSFER_LIST
+		if (bl2_tl) {
+			// relocate the tl to pre-allocate NS memory
+			ns_tl = transfer_list_relocate(bl2_tl,
+					(void *)(uintptr_t)FW_NS_HANDOFF_BASE,
+					bl2_tl->max_size);
+			if (!ns_tl) {
+				ERROR("Relocate TL to 0x%lx failed\n",
+					(unsigned long)FW_NS_HANDOFF_BASE);
+				return -1;
+			}
+			NOTICE("Transfer list handoff to BL33\n");
+			transfer_list_dump(ns_tl);
+
+			te = transfer_list_find(ns_tl, TL_TAG_FDT);
+
+			bl_mem_params->ep_info.args.arg1 =
+				TRANSFER_LIST_SIGNATURE |
+				REGISTER_CONVENTION_VERSION_MASK;
+			bl_mem_params->ep_info.args.arg3 = (uintptr_t)ns_tl;
+
+			if (GET_RW(bl_mem_params->ep_info.spsr) == MODE_RW_32) {
+				// aarch32
+				bl_mem_params->ep_info.args.arg0 = 0;
+				bl_mem_params->ep_info.args.arg2 = te ?
+					(uintptr_t)transfer_list_entry_data(te)
+					: 0;
+			} else {
+				// aarch64
+				bl_mem_params->ep_info.args.arg0 = te ?
+					(uintptr_t)transfer_list_entry_data(te)
+					: 0;
+				bl_mem_params->ep_info.args.arg2 = 0;
+			}
+		} else {
+			// Legacy handoff
+			bl_mem_params->ep_info.args.arg0 = 0xffff & read_mpidr();
+		}
 #else
 		/* BL33 expects to receive the primary CPU MPID (through r0) */
 		bl_mem_params->ep_info.args.arg0 = 0xffff & read_mpidr();
-#endif
+#endif // ARM_LINUX_KERNEL_AS_BL33
 
-		bl_mem_params->ep_info.spsr = qemu_get_spsr_for_bl33_entry();
 		break;
 #ifdef SPD_spmd
 #if SPMD_SPM_AT_SEL2