Merge changes from topic "xilinx-console-sync" into integration

* changes:
  fix(xilinx): remove console error message
  feat(xilinx): sync macro names
  feat(xilinx): remove crash console unused macros
diff --git a/Makefile b/Makefile
index 1cce234..104d84d 100644
--- a/Makefile
+++ b/Makefile
@@ -1027,6 +1027,10 @@
         $(info DRTM_SUPPORT is an experimental feature)
 endif
 
+ifeq (${TRANSFER_LIST},1)
+        $(info TRANSFER_LIST is an experimental feature)
+endif
+
 ifeq (${ENABLE_RME},1)
 	ifneq (${SEPARATE_CODE_AND_RODATA},1)
                 $(error `ENABLE_RME=1` requires `SEPARATE_CODE_AND_RODATA=1`)
@@ -1191,6 +1195,7 @@
 	SPMC_AT_EL3 \
 	SPMD_SPM_AT_SEL2 \
 	ENABLE_SPMD_LP \
+	TRANSFER_LIST \
 	TRUSTED_BOARD_BOOT \
 	USE_COHERENT_MEM \
 	USE_DEBUGFS \
@@ -1351,6 +1356,7 @@
 	SPM_MM \
 	SPMC_AT_EL3 \
 	SPMD_SPM_AT_SEL2 \
+	TRANSFER_LIST \
 	TRUSTED_BOARD_BOOT \
 	CRYPTO_SUPPORT \
 	TRNG_SUPPORT \
diff --git a/bl31/ehf.c b/bl31/ehf.c
index b328380..6f3d941 100644
--- a/bl31/ehf.c
+++ b/bl31/ehf.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017-2022, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2017-2023, Arm Limited and Contributors. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -458,7 +458,7 @@
 	int ret __unused;
 
 	/* Ensure EL3 interrupts are supported */
-	assert(plat_ic_has_interrupt_type(INTR_TYPE_EL3) != 0);
+	assert(plat_ic_has_interrupt_type(INTR_TYPE_EL3));
 
 	/*
 	 * Make sure that priority water mark has enough bits to represent the
diff --git a/bl31/interrupt_mgmt.c b/bl31/interrupt_mgmt.c
index b8cc3de..68c7f10 100644
--- a/bl31/interrupt_mgmt.c
+++ b/bl31/interrupt_mgmt.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014-2020, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2014-2023, Arm Limited and Contributors. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -47,9 +47,9 @@
  ******************************************************************************/
 static int32_t validate_interrupt_type(uint32_t type)
 {
-	if ((type == INTR_TYPE_S_EL1) || (type == INTR_TYPE_NS) ||
-	    (type == INTR_TYPE_EL3))
+	if (plat_ic_has_interrupt_type(type)) {
 		return 0;
+	}
 
 	return -EINVAL;
 }
diff --git a/changelog.yaml b/changelog.yaml
index 7e75832..3f7979e 100644
--- a/changelog.yaml
+++ b/changelog.yaml
@@ -787,6 +787,9 @@
       - title: Semihosting
         scope: semihosting
 
+      - title: Firmware Handoff
+        scope: handoff
+
   - title: Drivers
 
     subsections:
@@ -902,6 +905,9 @@
                   - title: GIC-600AE
                     scope: gic600ae
 
+              - title: GICv2
+                scope: gicv2
+
           - title: SMMU
             scope: smmu
 
diff --git a/docs/about/release-information.rst b/docs/about/release-information.rst
index 0768e1f..9b51dab 100644
--- a/docs/about/release-information.rst
+++ b/docs/about/release-information.rst
@@ -54,7 +54,7 @@
 +-----------------+---------------------------+------------------------------+
 | v2.9            | 4th week of May '23       | 2nd week of May '23          |
 +-----------------+---------------------------+------------------------------+
-| v3.0            | 2nd week of Nov '23       | 2nd week of Oct '23          |
+| v2.10           | 4th week of Nov '23       | 2nd week of Nov '23          |
 +-----------------+---------------------------+------------------------------+
 
 Removal of Deprecated Interfaces
@@ -84,9 +84,9 @@
 |                                | Date        | after   |                                                         |
 |                                |             | Release |                                                         |
 +================================+=============+=========+=========================================================+
-| CryptoCell-712                 |     2.9     |   3.0   | No longer maintained.                                   |
+| CryptoCell-712                 |     2.9     |   2.10  | No longer maintained.                                   |
 +--------------------------------+-------------+---------+---------------------------------------------------------+
-| CryptoCell-713                 |     2.9     |   3.0   | No longer maintained.                                   |
+| CryptoCell-713                 |     2.9     |   2.10  | No longer maintained.                                   |
 +--------------------------------+-------------+---------+---------------------------------------------------------+
 
 --------------
diff --git a/docs/components/platform-interrupt-controller-API.rst b/docs/components/platform-interrupt-controller-API.rst
index 069c87b..4de39d1 100644
--- a/docs/components/platform-interrupt-controller-API.rst
+++ b/docs/components/platform-interrupt-controller-API.rst
@@ -120,39 +120,39 @@
 In case of Arm standard platforms using GIC, the implementation of the API
 writes to GIC *Priority Register* set interrupt priority.
 
-Function: int plat_ic_has_interrupt_type(unsigned int type); [optional]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Function: bool plat_ic_has_interrupt_type(unsigned int type); [optional]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 ::
 
     Argument : unsigned int
-    Return   : int
+    Return   : bool
 
 This API should return whether the platform supports a given interrupt type. The
 parameter ``type`` shall be one of ``INTR_TYPE_EL3``, ``INTR_TYPE_S_EL1``, or
 ``INTR_TYPE_NS``.
 
 In case of Arm standard platforms using GICv3, the implementation of the API
-returns ``1`` for all interrupt types.
+returns *true* for all interrupt types.
 
-In case of Arm standard platforms using GICv2, the API always return ``1`` for
+In case of Arm standard platforms using GICv2, the API always return *true* for
 ``INTR_TYPE_NS``. Return value for other types depends on the value of build
 option ``GICV2_G0_FOR_EL3``:
 
 - For interrupt type ``INTR_TYPE_EL3``:
 
-  - When ``GICV2_G0_FOR_EL3`` is ``0``, it returns ``0``, indicating no support
+  - When ``GICV2_G0_FOR_EL3`` is ``0``, it returns *false*, indicating no support
     for EL3 interrupts.
 
-  - When ``GICV2_G0_FOR_EL3`` is ``1``, it returns ``1``, indicating support for
+  - When ``GICV2_G0_FOR_EL3`` is ``1``, it returns *true*, indicating support for
     EL3 interrupts.
 
 - For interrupt type ``INTR_TYPE_S_EL1``:
 
-  - When ``GICV2_G0_FOR_EL3`` is ``0``, it returns ``1``, indicating support for
+  - When ``GICV2_G0_FOR_EL3`` is ``0``, it returns *true*, indicating support for
     Secure EL1 interrupts.
 
-  - When ``GICV2_G0_FOR_EL3`` is ``1``, it returns ``0``, indicating no support
+  - When ``GICV2_G0_FOR_EL3`` is ``1``, it returns *false*, indicating no support
     for Secure EL1 interrupts.
 
 Function: void plat_ic_set_interrupt_type(unsigned int id, unsigned int type); [optional]
@@ -306,4 +306,4 @@
 
 --------------
 
-*Copyright (c) 2017-2019, Arm Limited and Contributors. All rights reserved.*
+*Copyright (c) 2017-2023, Arm Limited and Contributors. All rights reserved.*
diff --git a/docs/getting_started/build-options.rst b/docs/getting_started/build-options.rst
index 1da2738..3b38480 100644
--- a/docs/getting_started/build-options.rst
+++ b/docs/getting_started/build-options.rst
@@ -945,6 +945,11 @@
    hardware will limit the effective VL to the maximum physically supported
    VL.
 
+-  ``TRANSFER_LIST``: Setting this to ``1`` enables support for Firmware
+   Handoff using Transfer List defined in `Firmware Handoff specification`_.
+   This defaults to ``0``. Please note that this is an experimental feature
+   based on Firmware Handoff specification v0.9.
+
 -  ``TRNG_SUPPORT``: Setting this to ``1`` enables support for True
    Random Number Generator Interface to BL31 image. This defaults to ``0``.
 
@@ -1300,3 +1305,4 @@
 .. _PSA DRTM specification: https://developer.arm.com/documentation/den0113/a
 .. _GCC: https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html
 .. _Clang: https://clang.llvm.org/docs/DiagnosticsReference.html
+.. _Firmware Handoff specification: https://github.com/FirmwareHandoff/firmware_handoff/releases/tag/v0.9
diff --git a/docs/plat/ast2700.rst b/docs/plat/ast2700.rst
index 0352aea..6deade3 100644
--- a/docs/plat/ast2700.rst
+++ b/docs/plat/ast2700.rst
@@ -7,11 +7,11 @@
 Boot Flow
 ---------
 
-    BootRom --> BL1/BL2 --> TF-A BL31 --> BL32 (optional) --> BL33 --> Linux Kernel
+    BootRom --> TF-A BL31 --> BL32 --> BL33 --> Linux Kernel
 
 How to build
 ------------
 
 .. code:: shell
 
-    make CROSS_COMPILE=aarch64-linux-gnu- PLAT=ast2700
+    make CROSS_COMPILE=aarch64-linux-gnu- PLAT=ast2700 SPD=opteed
diff --git a/docs/plat/index.rst b/docs/plat/index.rst
index 7a05fb6..fc3effd 100644
--- a/docs/plat/index.rst
+++ b/docs/plat/index.rst
@@ -73,13 +73,13 @@
 +----------------+----------------+--------------------+--------------------+
 |    mt6795      |      MTK       |        2.5         |       2.7          |
 +----------------+----------------+--------------------+--------------------+
-|    sgi575      |      Arm       |        2.8         |       3.0          |
+|    sgi575      |      Arm       |        2.8         |       2.10         |
 +----------------+----------------+--------------------+--------------------+
-|    rdn1edge    |      Arm       |        2.8         |       3.0          |
+|    rdn1edge    |      Arm       |        2.8         |       2.10         |
 +----------------+----------------+--------------------+--------------------+
-|    tc0         |      Arm       |        2.8         |       3.0          |
+|    tc0         |      Arm       |        2.8         |       2.10         |
 +----------------+----------------+--------------------+--------------------+
-|    rde1edge    |      Arm       |        2.9         |       3.1          |
+|    rde1edge    |      Arm       |        2.9         |       3.0          |
 +----------------+----------------+--------------------+--------------------+
 
 --------------
diff --git a/docs/plat/st/stm32mp1.rst b/docs/plat/st/stm32mp1.rst
index 35e8f8c..b6e4b0d 100644
--- a/docs/plat/st/stm32mp1.rst
+++ b/docs/plat/st/stm32mp1.rst
@@ -205,6 +205,7 @@
         --nt-fw <u-boot_directory>/u-boot-nodtb.bin \
         --hw-config <u-boot_directory>/u-boot.dtb \
         --fw-config build/stm32mp1/release/fdts/fw-config.dtb \
+        --trusted-key-cert build/stm32mp1/release/trusted_key.crt \
         --tos-fw-cert build/stm32mp1/release/tos_fw_content.crt \
         --tos-fw-key-cert build/stm32mp1/release/tos_fw_key.crt \
         --nt-fw-cert build/stm32mp1/release/nt_fw_content.crt \
diff --git a/drivers/arm/dcc/dcc_console.c b/drivers/arm/dcc/dcc_console.c
index 0b7e541..8d9f793 100644
--- a/drivers/arm/dcc/dcc_console.c
+++ b/drivers/arm/dcc/dcc_console.c
@@ -114,12 +114,6 @@
 	return __dcc_getchar();
 }
 
-int32_t dcc_console_init(unsigned long base_addr, uint32_t uart_clk,
-		      uint32_t baud_rate)
-{
-	return 0; /* No init needed */
-}
-
 /**
  * dcc_console_flush() - Function to force a write of all buffered data
  *		          that hasn't been output.
@@ -150,3 +144,9 @@
 {
 	return console_register(&dcc_console.console);
 }
+
+void console_dcc_unregister(void)
+{
+	dcc_console_flush(&dcc_console.console);
+	(void)console_unregister(&dcc_console.console);
+}
diff --git a/drivers/arm/gic/v2/gicv2_main.c b/drivers/arm/gic/v2/gicv2_main.c
index ca2a038..696bede 100644
--- a/drivers/arm/gic/v2/gicv2_main.c
+++ b/drivers/arm/gic/v2/gicv2_main.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015-2020, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2015-2023, Arm Limited and Contributors. All rights reserved.
  * Portions copyright (c) 2021-2022, ProvenRun S.A.S. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
@@ -390,7 +390,7 @@
  * This function assigns group for the interrupt identified by id. The group can
  * be any of GICV2_INTR_GROUP*
  ******************************************************************************/
-void gicv2_set_interrupt_type(unsigned int id, unsigned int type)
+void gicv2_set_interrupt_group(unsigned int id, unsigned int group)
 {
 	assert(driver_data != NULL);
 	assert(driver_data->gicd_base != 0U);
@@ -398,7 +398,7 @@
 
 	/* Serialize read-modify-write to Distributor registers */
 	spin_lock(&gic_lock);
-	switch (type) {
+	switch (group) {
 	case GICV2_INTR_GROUP1:
 		gicd_set_igroupr(driver_data->gicd_base, id);
 		break;
diff --git a/drivers/arm/gic/v3/gicv3_main.c b/drivers/arm/gic/v3/gicv3_main.c
index 2c74800..3c99517 100644
--- a/drivers/arm/gic/v3/gicv3_main.c
+++ b/drivers/arm/gic/v3/gicv3_main.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.
  * Copyright (c) 2023, NVIDIA Corporation. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
@@ -421,16 +421,15 @@
 }
 
 /*******************************************************************************
- * This function returns the type of the interrupt id depending upon the group
- * this interrupt has been configured under by the interrupt controller i.e.
- * group0 or group1 Secure / Non Secure. The return value can be one of the
- * following :
+ * This function returns the group that has been configured under by the
+ * interrupt controller for the given interrupt id i.e. either group0 or group1
+ * Secure / Non Secure. The return value can be one of the following :
  *    INTR_GROUP0  : The interrupt type is a Secure Group 0 interrupt
  *    INTR_GROUP1S : The interrupt type is a Secure Group 1 secure interrupt
  *    INTR_GROUP1NS: The interrupt type is a Secure Group 1 non secure
  *                   interrupt.
  ******************************************************************************/
-unsigned int gicv3_get_interrupt_type(unsigned int id, unsigned int proc_num)
+unsigned int gicv3_get_interrupt_group(unsigned int id, unsigned int proc_num)
 {
 	unsigned int igroup, grpmodr;
 	uintptr_t gicr_base;
@@ -1059,8 +1058,8 @@
  * is used if the interrupt is SGI or (E)PPI, and programs the corresponding
  * Redistributor interface. The group can be any of GICV3_INTR_GROUP*
  ******************************************************************************/
-void gicv3_set_interrupt_type(unsigned int id, unsigned int proc_num,
-		unsigned int type)
+void gicv3_set_interrupt_group(unsigned int id, unsigned int proc_num,
+		unsigned int group)
 {
 	bool igroup = false, grpmod = false;
 	uintptr_t gicr_base;
@@ -1071,7 +1070,7 @@
 	assert(proc_num < gicv3_driver_data->rdistif_num);
 	assert(gicv3_driver_data->rdistif_base_addrs != NULL);
 
-	switch (type) {
+	switch (group) {
 	case INTR_GROUP1S:
 		igroup = false;
 		grpmod = true;
diff --git a/include/drivers/arm/dcc.h b/include/drivers/arm/dcc.h
index 1f1fd03..072bed5 100644
--- a/include/drivers/arm/dcc.h
+++ b/include/drivers/arm/dcc.h
@@ -15,5 +15,6 @@
  * framework.
  */
 int console_dcc_register(void);
+void console_dcc_unregister(void);
 
 #endif /* DCC */
diff --git a/include/drivers/arm/gicv2.h b/include/drivers/arm/gicv2.h
index cfc168d..bebd9ce 100644
--- a/include/drivers/arm/gicv2.h
+++ b/include/drivers/arm/gicv2.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015-2018, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2015-2023, Arm Limited and Contributors. All rights reserved.
  * Portions copyright (c) 2021-2022, ProvenRun S.A.S. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
@@ -188,7 +188,7 @@
 void gicv2_enable_interrupt(unsigned int id);
 void gicv2_disable_interrupt(unsigned int id);
 void gicv2_set_interrupt_priority(unsigned int id, unsigned int priority);
-void gicv2_set_interrupt_type(unsigned int id, unsigned int type);
+void gicv2_set_interrupt_group(unsigned int id, unsigned int group);
 void gicv2_raise_sgi(int sgi_num, bool ns, int proc_num);
 void gicv2_set_spi_routing(unsigned int id, int proc_num);
 void gicv2_set_interrupt_pending(unsigned int id);
diff --git a/include/drivers/arm/gicv3.h b/include/drivers/arm/gicv3.h
index 5bb22fd..cf6a746 100644
--- a/include/drivers/arm/gicv3.h
+++ b/include/drivers/arm/gicv3.h
@@ -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
  */
@@ -556,7 +556,7 @@
 void gicv3_cpuif_disable(unsigned int proc_num);
 unsigned int gicv3_get_pending_interrupt_type(void);
 unsigned int gicv3_get_pending_interrupt_id(void);
-unsigned int gicv3_get_interrupt_type(unsigned int id,
+unsigned int gicv3_get_interrupt_group(unsigned int id,
 					  unsigned int proc_num);
 void gicv3_distif_init_restore(const gicv3_dist_ctx_t * const dist_ctx);
 void gicv3_distif_save(gicv3_dist_ctx_t * const dist_ctx);
@@ -579,8 +579,8 @@
 void gicv3_disable_interrupt(unsigned int id, unsigned int proc_num);
 void gicv3_set_interrupt_priority(unsigned int id, unsigned int proc_num,
 		unsigned int priority);
-void gicv3_set_interrupt_type(unsigned int id, unsigned int proc_num,
-		unsigned int type);
+void gicv3_set_interrupt_group(unsigned int id, unsigned int proc_num,
+		unsigned int group);
 void gicv3_raise_sgi(unsigned int sgi_num, gicv3_irq_group_t group,
 					 u_register_t target);
 void gicv3_set_spi_routing(unsigned int id, unsigned int irm,
diff --git a/include/lib/transfer_list.h b/include/lib/transfer_list.h
new file mode 100644
index 0000000..54c8643
--- /dev/null
+++ b/include/lib/transfer_list.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 2023, Linaro Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef __TRANSFER_LIST_H
+#define __TRANSFER_LIST_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <lib/utils_def.h>
+
+#define	TRANSFER_LIST_SIGNATURE		U(0x006ed0ff)
+#define TRANSFER_LIST_VERSION		U(0x0001)
+
+// Init value of maximum alignment required by any TE data in the TL
+// specified as a power of two
+#define TRANSFER_LIST_INIT_MAX_ALIGN	U(3)
+
+// alignment required by TE header start address, in bytes
+#define TRANSFER_LIST_GRANULE		U(8)
+
+// version of the register convention used.
+// Set to 1 for both AArch64 and AArch32 according to fw handoff spec v0.9
+#define REGISTER_CONVENTION_VERSION_MASK (1 << 24)
+
+#ifndef __ASSEMBLER__
+
+enum transfer_list_tag_id {
+	TL_TAG_EMPTY = 0,
+	TL_TAG_FDT = 1,
+	TL_TAG_HOB_BLOCK = 2,
+	TL_TAG_HOB_LIST = 3,
+	TL_TAG_ACPI_TABLE_AGGREGATE = 4,
+};
+
+enum transfer_list_ops {
+	TL_OPS_NON,	// invalid for any operation
+	TL_OPS_ALL,	// valid for all operations
+	TL_OPS_RO,	// valid for read only
+	TL_OPS_CUS,	// either abort or switch to special code to interpret
+};
+
+struct transfer_list_header {
+	uint32_t	signature;
+	uint8_t		checksum;
+	uint8_t		version;
+	uint8_t		hdr_size;
+	uint8_t		alignment;	// max alignment of TE data
+	uint32_t	size;		// TL header + all TEs
+	uint32_t	max_size;
+	/*
+	 * Commented out element used to visualize dynamic part of the
+	 * data structure.
+	 *
+	 * Note that struct transfer_list_entry also is dynamic in size
+	 * so the elements can't be indexed directly but instead must be
+	 * traversed in order
+	 *
+	 * struct transfer_list_entry entries[];
+	 */
+};
+
+struct transfer_list_entry {
+	uint16_t	tag_id;
+	uint8_t		reserved0;	// place holder
+	uint8_t		hdr_size;
+	uint32_t	data_size;
+	/*
+	 * Commented out element used to visualize dynamic part of the
+	 * data structure.
+	 *
+	 * Note that padding is added at the end of @data to make to reach
+	 * a 8-byte boundary.
+	 *
+	 * uint8_t	data[ROUNDUP(data_size, 8)];
+	 */
+};
+
+void transfer_list_dump(struct transfer_list_header *tl);
+struct transfer_list_header *transfer_list_init(void *addr, size_t max_size);
+
+struct transfer_list_header *transfer_list_relocate(struct transfer_list_header *tl,
+						    void *addr, size_t max_size);
+enum transfer_list_ops transfer_list_check_header(const struct transfer_list_header *tl);
+
+void transfer_list_update_checksum(struct transfer_list_header *tl);
+bool transfer_list_verify_checksum(const struct transfer_list_header *tl);
+
+bool transfer_list_set_data_size(struct transfer_list_header *tl,
+				 struct transfer_list_entry *entry,
+				 uint32_t new_data_size);
+
+void *transfer_list_entry_data(struct transfer_list_entry *entry);
+bool transfer_list_rem(struct transfer_list_header *tl, struct transfer_list_entry *entry);
+
+struct transfer_list_entry *transfer_list_add(struct transfer_list_header *tl,
+					      uint16_t tag_id, uint32_t data_size,
+					      const void *data);
+
+struct transfer_list_entry *transfer_list_add_with_align(struct transfer_list_header *tl,
+							 uint16_t tag_id, uint32_t data_size,
+							 const void *data, uint8_t alignment);
+
+struct transfer_list_entry *transfer_list_next(struct transfer_list_header *tl,
+					       struct transfer_list_entry *last);
+
+struct transfer_list_entry *transfer_list_find(struct transfer_list_header *tl,
+					       uint16_t tag_id);
+
+#endif /*__ASSEMBLER__*/
+#endif /*__TRANSFER_LIST_H*/
diff --git a/include/lib/utils_def.h b/include/lib/utils_def.h
index ba52bc6..a170a09 100644
--- a/include/lib/utils_def.h
+++ b/include/lib/utils_def.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2022, Arm Limited and Contributors. All rights reserved.
+ * Copyright (c) 2016-2023, Arm Limited and Contributors. All rights reserved.
  * Copyright (c) 2020, NVIDIA Corporation. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
@@ -104,6 +104,41 @@
 #define round_down(value, boundary)		\
 	((value) & ~round_boundary(value, boundary))
 
+/* add operation together with checking whether the operation overflowed
+ * The result is '*res',
+ * return 0 on success and 1 on overflow
+ */
+#define add_overflow(a, b, res) __builtin_add_overflow((a), (b), (res))
+
+/*
+ * Round up a value to align with a given size and
+ * check whether overflow happens.
+ * The rounduped value is '*res',
+ * return 0 on success and 1 on overflow
+ */
+#define round_up_overflow(v, size, res) (__extension__({ \
+	typeof(res) __res = res; \
+	typeof(*(__res)) __roundup_tmp = 0; \
+	typeof(v) __roundup_mask = (typeof(v))(size) - 1; \
+	\
+	add_overflow((v), __roundup_mask, &__roundup_tmp) ? 1 : \
+		(void)(*(__res) = __roundup_tmp & ~__roundup_mask), 0; \
+}))
+
+/*
+ * Add a with b, then round up the result to align with a given size and
+ * check whether overflow happens.
+ * The rounduped value is '*res',
+ * return 0 on success and 1 on overflow
+ */
+#define add_with_round_up_overflow(a, b, size, res) (__extension__({ \
+	typeof(a) __a = (a); \
+	typeof(__a) __add_res = 0; \
+	\
+	add_overflow((__a), (b), &__add_res) ? 1 : \
+		round_up_overflow(__add_res, (size), (res)) ? 1 : 0; \
+}))
+
 /**
  * Helper macro to ensure a value lies on a given boundary.
  */
diff --git a/include/plat/common/platform.h b/include/plat/common/platform.h
index e024d91..c92121f 100644
--- a/include/plat/common/platform.h
+++ b/include/plat/common/platform.h
@@ -111,7 +111,7 @@
 unsigned int plat_ic_get_interrupt_active(unsigned int id);
 void plat_ic_disable_interrupt(unsigned int id);
 void plat_ic_enable_interrupt(unsigned int id);
-int plat_ic_has_interrupt_type(unsigned int type);
+bool plat_ic_has_interrupt_type(unsigned int type);
 void plat_ic_set_interrupt_type(unsigned int id, unsigned int type);
 void plat_ic_set_interrupt_priority(unsigned int id, unsigned int priority);
 void plat_ic_raise_el3_sgi(int sgi_num, u_register_t target);
diff --git a/include/services/el3_spmd_logical_sp.h b/include/services/el3_spmd_logical_sp.h
index 1f9ef0d..15bea9f 100644
--- a/include/services/el3_spmd_logical_sp.h
+++ b/include/services/el3_spmd_logical_sp.h
@@ -105,33 +105,33 @@
 }
 
 static inline uint16_t ffa_partition_info_regs_get_last_idx(
-	struct ffa_value args)
+	struct ffa_value *args)
 {
-	return (uint16_t)(args.arg2 & 0xFFFFU);
+	return (uint16_t)(args->arg2 & 0xFFFFU);
 }
 
 static inline uint16_t ffa_partition_info_regs_get_curr_idx(
-	struct ffa_value args)
+	struct ffa_value *args)
 {
-	return (uint16_t)((args.arg2 >> 16) & 0xFFFFU);
+	return (uint16_t)((args->arg2 >> 16) & 0xFFFFU);
 }
 
-static inline uint16_t ffa_partition_info_regs_get_tag(struct ffa_value args)
+static inline uint16_t ffa_partition_info_regs_get_tag(struct ffa_value *args)
 {
-	return (uint16_t)((args.arg2 >> 32) & 0xFFFFU);
+	return (uint16_t)((args->arg2 >> 32) & 0xFFFFU);
 }
 
 static inline uint16_t ffa_partition_info_regs_get_desc_size(
-	struct ffa_value args)
+	struct ffa_value *args)
 {
-	return (uint16_t)(args.arg2 >> 48);
+	return (uint16_t)(args->arg2 >> 48);
 }
 
 uint64_t spmd_el3_populate_logical_partition_info(void *handle, uint64_t x1,
 						  uint64_t x2, uint64_t x3);
 
 bool ffa_partition_info_regs_get_part_info(
-	struct ffa_value args, uint8_t idx,
+	struct ffa_value *args, uint8_t idx,
 	struct ffa_partition_info_v1_1 *partition_info);
 
 bool spmd_el3_invoke_partition_info_get(
diff --git a/lib/transfer_list/transfer_list.c b/lib/transfer_list/transfer_list.c
new file mode 100644
index 0000000..e38bf74
--- /dev/null
+++ b/lib/transfer_list/transfer_list.c
@@ -0,0 +1,474 @@
+/*
+ * Copyright (c) 2023, Linaro Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <inttypes.h>
+#include <string.h>
+
+#include <common/debug.h>
+#include <lib/transfer_list.h>
+#include <lib/utils_def.h>
+
+void transfer_list_dump(struct transfer_list_header *tl)
+{
+	struct transfer_list_entry *te = NULL;
+	int i = 0;
+
+	if (!tl) {
+		return;
+	}
+	NOTICE("Dump transfer list:\n");
+	NOTICE("signature  0x%x\n", tl->signature);
+	NOTICE("checksum   0x%x\n", tl->checksum);
+	NOTICE("version    0x%x\n", tl->version);
+	NOTICE("hdr_size   0x%x\n", tl->hdr_size);
+	NOTICE("alignment  0x%x\n", tl->alignment);
+	NOTICE("size       0x%x\n", tl->size);
+	NOTICE("max_size   0x%x\n", tl->max_size);
+	while (true) {
+		te = transfer_list_next(tl, te);
+		if (!te) {
+			break;
+		}
+		NOTICE("Entry %d:\n", i++);
+		NOTICE("tag_id     0x%x\n", te->tag_id);
+		NOTICE("hdr_size   0x%x\n", te->hdr_size);
+		NOTICE("data_size  0x%x\n", te->data_size);
+		NOTICE("data_addr  0x%lx\n",
+		(unsigned long)transfer_list_entry_data(te));
+	}
+}
+
+/*******************************************************************************
+ * Creating a transfer list in a reserved memory region specified
+ * Compliant to 2.4.5 of Firmware handoff specification (v0.9)
+ * Return pointer to the created transfer list or NULL on error
+ ******************************************************************************/
+struct transfer_list_header *transfer_list_init(void *addr, size_t max_size)
+{
+	struct transfer_list_header *tl = addr;
+
+	if (!addr || max_size == 0) {
+		return NULL;
+	}
+
+	if (!is_aligned((uintptr_t)addr, 1 << TRANSFER_LIST_INIT_MAX_ALIGN) ||
+	    !is_aligned(max_size, 1 << TRANSFER_LIST_INIT_MAX_ALIGN) ||
+	    max_size < sizeof(*tl)) {
+		return NULL;
+	}
+
+	memset(tl, 0, max_size);
+	tl->signature = TRANSFER_LIST_SIGNATURE;
+	tl->version = TRANSFER_LIST_VERSION;
+	tl->hdr_size = sizeof(*tl);
+	tl->alignment = TRANSFER_LIST_INIT_MAX_ALIGN; // initial max align
+	tl->size = sizeof(*tl); // initial size is the size of header
+	tl->max_size = max_size;
+
+	transfer_list_update_checksum(tl);
+
+	return tl;
+}
+
+/*******************************************************************************
+ * Relocating a transfer list to a reserved memory region specified
+ * Compliant to 2.4.6 of Firmware handoff specification (v0.9)
+ * Return true on success or false on error
+ ******************************************************************************/
+struct transfer_list_header *transfer_list_relocate(
+						struct transfer_list_header *tl,
+						void *addr, size_t max_size)
+{
+	uintptr_t new_addr, align_mask, align_off;
+	struct transfer_list_header *new_tl;
+	uint32_t new_max_size;
+
+	if (!tl || !addr || max_size == 0) {
+		return NULL;
+	}
+
+	align_mask = (1 << tl->alignment) - 1;
+	align_off = (uintptr_t)tl & align_mask;
+	new_addr = ((uintptr_t)addr & ~align_mask) + align_off;
+
+	if (new_addr < (uintptr_t)addr) {
+		new_addr += (1 << tl->alignment);
+	}
+
+	new_max_size = max_size - (new_addr - (uintptr_t)addr);
+
+	// the new space is not sufficient for the tl
+	if (tl->size > new_max_size) {
+		return NULL;
+	}
+
+	new_tl = (struct transfer_list_header *)new_addr;
+	memmove(new_tl, tl, tl->size);
+	new_tl->max_size = new_max_size;
+
+	transfer_list_update_checksum(new_tl);
+
+	return new_tl;
+}
+
+/*******************************************************************************
+ * Verifying the header of a transfer list
+ * Compliant to 2.4.1 of Firmware handoff specification (v0.9)
+ * Return transfer list operation status code
+ ******************************************************************************/
+enum transfer_list_ops transfer_list_check_header(
+					const struct transfer_list_header *tl)
+{
+	if (!tl) {
+		return TL_OPS_NON;
+	}
+
+	if (tl->signature != TRANSFER_LIST_SIGNATURE) {
+		ERROR("Bad transfer list signature %#"PRIx32"\n",
+		      tl->signature);
+		return TL_OPS_NON;
+	}
+
+	if (!tl->max_size) {
+		ERROR("Bad transfer list max size %#"PRIx32"\n",
+		      tl->max_size);
+		return TL_OPS_NON;
+	}
+
+	if (tl->size > tl->max_size) {
+		ERROR("Bad transfer list size %#"PRIx32"\n", tl->size);
+		return TL_OPS_NON;
+	}
+
+	if (tl->hdr_size != sizeof(struct transfer_list_header)) {
+		ERROR("Bad transfer list header size %#"PRIx32"\n", tl->hdr_size);
+		return TL_OPS_NON;
+	}
+
+	if (!transfer_list_verify_checksum(tl)) {
+		ERROR("Bad transfer list checksum %#"PRIx32"\n", tl->checksum);
+		return TL_OPS_NON;
+	}
+
+	if (tl->version == 0) {
+		ERROR("Transfer list version is invalid\n");
+		return TL_OPS_NON;
+	} else if (tl->version == TRANSFER_LIST_VERSION) {
+		INFO("Transfer list version is valid for all operations\n");
+		return TL_OPS_ALL;
+	} else if (tl->version > TRANSFER_LIST_VERSION) {
+		INFO("Transfer list version is valid for read-only\n");
+		return TL_OPS_RO;
+	}
+
+	INFO("Old transfer list version is detected\n");
+	return TL_OPS_CUS;
+}
+
+/*******************************************************************************
+ * Enumerate the next transfer entry
+ * Return pointer to the next transfer entry or NULL on error
+ ******************************************************************************/
+struct transfer_list_entry *transfer_list_next(struct transfer_list_header *tl,
+					       struct transfer_list_entry *last)
+{
+	struct transfer_list_entry *te = NULL;
+	uintptr_t tl_ev = 0;
+	uintptr_t va = 0;
+	uintptr_t ev = 0;
+	size_t sz = 0;
+
+	if (!tl) {
+		return NULL;
+	}
+
+	tl_ev = (uintptr_t)tl + tl->size;
+
+	if (last) {
+		va = (uintptr_t)last;
+		// check if the total size overflow
+		if (add_overflow(last->hdr_size,
+			last->data_size, &sz)) {
+			return NULL;
+		}
+		// roundup to the next entry
+		if (add_with_round_up_overflow(va, sz,
+			TRANSFER_LIST_GRANULE, &va)) {
+			return NULL;
+		}
+	} else {
+		va = (uintptr_t)tl + tl->hdr_size;
+	}
+
+	te = (struct transfer_list_entry *)va;
+
+	if (va + sizeof(*te) > tl_ev || te->hdr_size < sizeof(*te) ||
+		add_overflow(te->hdr_size, te->data_size, &sz) ||
+		add_overflow(va, sz, &ev) ||
+		ev > tl_ev) {
+		return NULL;
+	}
+
+	return te;
+}
+
+/*******************************************************************************
+ * Calculate the byte sum of a transfer list
+ * Return byte sum of the transfer list
+ ******************************************************************************/
+static uint8_t calc_byte_sum(const struct transfer_list_header *tl)
+{
+	uint8_t *b = (uint8_t *)tl;
+	uint8_t cs = 0;
+	size_t n = 0;
+
+	if (!tl) {
+		return 0;
+	}
+
+	for (n = 0; n < tl->size; n++) {
+		cs += b[n];
+	}
+
+	return cs;
+}
+
+/*******************************************************************************
+ * Update the checksum of a transfer list
+ * Return updated checksum of the transfer list
+ ******************************************************************************/
+void transfer_list_update_checksum(struct transfer_list_header *tl)
+{
+	uint8_t cs;
+
+	if (!tl) {
+		return;
+	}
+
+	cs = calc_byte_sum(tl);
+	cs -= tl->checksum;
+	cs = 256 - cs;
+	tl->checksum = cs;
+	assert(transfer_list_verify_checksum(tl));
+}
+
+/*******************************************************************************
+ * Verify the checksum of a transfer list
+ * Return true if verified or false if not
+ ******************************************************************************/
+bool transfer_list_verify_checksum(const struct transfer_list_header *tl)
+{
+	return !calc_byte_sum(tl);
+}
+
+/*******************************************************************************
+ * Update the data size of a transfer entry
+ * Return true on success or false on error
+ ******************************************************************************/
+bool transfer_list_set_data_size(struct transfer_list_header *tl,
+				 struct transfer_list_entry *te,
+				 uint32_t new_data_size)
+{
+	uintptr_t tl_old_ev, new_ev = 0, old_ev = 0, ru_new_ev;
+	struct transfer_list_entry *dummy_te = NULL;
+	size_t gap = 0;
+	size_t mov_dis = 0;
+	size_t sz = 0;
+
+	if (!tl || !te) {
+		return false;
+	}
+	tl_old_ev = (uintptr_t)tl + tl->size;
+
+	// calculate the old and new end of TE
+	// both must be roundup to align with TRANSFER_LIST_GRANULE
+	if (add_overflow(te->hdr_size, te->data_size, &sz) ||
+		add_with_round_up_overflow((uintptr_t)te, sz,
+		TRANSFER_LIST_GRANULE, &old_ev)) {
+		return false;
+	}
+	if (add_overflow(te->hdr_size, new_data_size, &sz) ||
+		add_with_round_up_overflow((uintptr_t)te, sz,
+		TRANSFER_LIST_GRANULE, &new_ev)) {
+		return false;
+	}
+
+	if (new_ev > old_ev) {
+		// move distance should be roundup
+		// to meet the requirement of TE data max alignment
+		// ensure that the increased size doesn't exceed
+		// the max size of TL
+		mov_dis = new_ev - old_ev;
+		if (round_up_overflow(mov_dis, 1 << tl->alignment,
+			&mov_dis) || tl->size + mov_dis > tl->max_size) {
+			return false;
+		}
+		ru_new_ev = old_ev + mov_dis;
+		memmove((void *)ru_new_ev, (void *)old_ev, tl_old_ev - old_ev);
+		tl->size += mov_dis;
+		gap = ru_new_ev - new_ev;
+	} else {
+		gap = old_ev - new_ev;
+	}
+
+	if (gap >= sizeof(*dummy_te)) {
+		// create a dummy TE to fill up the gap
+		dummy_te = (struct transfer_list_entry *)new_ev;
+		dummy_te->tag_id = TL_TAG_EMPTY;
+		dummy_te->reserved0 = 0;
+		dummy_te->hdr_size = sizeof(*dummy_te);
+		dummy_te->data_size = gap - sizeof(*dummy_te);
+	}
+
+	te->data_size = new_data_size;
+
+	transfer_list_update_checksum(tl);
+	return true;
+}
+
+/*******************************************************************************
+ * Remove a specified transfer entry from a transfer list
+ * Return true on success or false on error
+ ******************************************************************************/
+bool transfer_list_rem(struct transfer_list_header *tl,
+			struct transfer_list_entry *te)
+{
+	if (!tl || !te || (uintptr_t)te > (uintptr_t)tl + tl->size) {
+		return false;
+	}
+	te->tag_id = TL_TAG_EMPTY;
+	te->reserved0 = 0;
+	transfer_list_update_checksum(tl);
+	return true;
+}
+
+/*******************************************************************************
+ * Add a new transfer entry into a transfer list
+ * Compliant to 2.4.3 of Firmware handoff specification (v0.9)
+ * Return pointer to the added transfer entry or NULL on error
+ ******************************************************************************/
+struct transfer_list_entry *transfer_list_add(struct transfer_list_header *tl,
+					      uint16_t tag_id,
+					      uint32_t data_size,
+					      const void *data)
+{
+	uintptr_t max_tl_ev, tl_ev, ev;
+	struct transfer_list_entry *te = NULL;
+	uint8_t *te_data = NULL;
+	size_t sz = 0;
+
+	if (!tl) {
+		return NULL;
+	}
+
+	max_tl_ev = (uintptr_t)tl + tl->max_size;
+	tl_ev = (uintptr_t)tl + tl->size;
+	ev = tl_ev;
+
+	// skip the step 1 (optional step)
+	// new TE will be added into the tail
+	if (add_overflow(sizeof(*te), data_size, &sz) ||
+		add_with_round_up_overflow(ev, sz,
+		TRANSFER_LIST_GRANULE, &ev) || ev > max_tl_ev) {
+		return NULL;
+	}
+
+	te = (struct transfer_list_entry *)tl_ev;
+	te->tag_id = tag_id;
+	te->reserved0 = 0;
+	te->hdr_size = sizeof(*te);
+	te->data_size = data_size;
+	tl->size += ev - tl_ev;
+
+	if (data) {
+		// get TE data pointer
+		te_data = transfer_list_entry_data(te);
+		if (!te_data) {
+			return NULL;
+		}
+		memmove(te_data, data, data_size);
+	}
+
+	transfer_list_update_checksum(tl);
+
+	return te;
+}
+
+/*******************************************************************************
+ * Add a new transfer entry into a transfer list with specified new data
+ * alignment requirement
+ * Compliant to 2.4.4 of Firmware handoff specification (v0.9)
+ * Return pointer to the added transfer entry or NULL on error
+ ******************************************************************************/
+struct transfer_list_entry *transfer_list_add_with_align(
+					struct transfer_list_header *tl,
+					uint16_t tag_id, uint32_t data_size,
+					const void *data, uint8_t alignment)
+{
+	struct transfer_list_entry *te = NULL;
+	uintptr_t tl_ev, ev, new_tl_ev;
+	size_t dummy_te_data_sz = 0;
+
+	if (!tl) {
+		return NULL;
+	}
+
+	tl_ev = (uintptr_t)tl + tl->size;
+	ev = tl_ev + sizeof(struct transfer_list_entry);
+
+	if (!is_aligned(ev, 1 << alignment)) {
+		// TE data address is not aligned to the new alignment
+		// fill the gap with an empty TE as a placeholder before
+		// adding the desire TE
+		new_tl_ev = round_up(ev, 1 << alignment) -
+				sizeof(struct transfer_list_entry);
+		dummy_te_data_sz = new_tl_ev - tl_ev -
+					sizeof(struct transfer_list_entry);
+		if (!transfer_list_add(tl, TL_TAG_EMPTY, dummy_te_data_sz,
+					NULL)) {
+			return NULL;
+		}
+	}
+
+	te = transfer_list_add(tl, tag_id, data_size, data);
+
+	if (alignment > tl->alignment) {
+		tl->alignment = alignment;
+		transfer_list_update_checksum(tl);
+	}
+
+	return te;
+}
+
+/*******************************************************************************
+ * Search for an existing transfer entry with the specified tag id from a
+ * transfer list
+ * Return pointer to the found transfer entry or NULL on error
+ ******************************************************************************/
+struct transfer_list_entry *transfer_list_find(struct transfer_list_header *tl,
+					       uint16_t tag_id)
+{
+	struct transfer_list_entry *te = NULL;
+
+	do {
+		te = transfer_list_next(tl, te);
+	} while (te && (te->tag_id != tag_id || te->reserved0 != 0));
+
+	return te;
+}
+
+/*******************************************************************************
+ * Retrieve the data pointer of a specified transfer entry
+ * Return pointer to the transfer entry data or NULL on error
+ ******************************************************************************/
+void *transfer_list_entry_data(struct transfer_list_entry *entry)
+{
+	if (!entry) {
+		return NULL;
+	}
+	return (uint8_t *)entry + entry->hdr_size;
+}
diff --git a/lib/transfer_list/transfer_list.mk b/lib/transfer_list/transfer_list.mk
new file mode 100644
index 0000000..42574e8
--- /dev/null
+++ b/lib/transfer_list/transfer_list.mk
@@ -0,0 +1,20 @@
+#
+# Copyright (c) 2023, Arm Limited and Contributors. All rights reserved.
+#
+# SPDX-License-Identifier: BSD-3-Clause
+#
+
+ifeq (${TRANSFER_LIST},1)
+
+ifeq (${ARCH},aarch32)
+$(eval $(call add_define,TRANSFER_LIST_AARCH32))
+endif
+
+TRANSFER_LIST_SOURCES	+=	$(addprefix lib/transfer_list/,	\
+				transfer_list.c)
+
+BL31_SOURCES	+=	$(TRANSFER_LIST_SOURCES)
+BL2_SOURCES	+=	$(TRANSFER_LIST_SOURCES)
+
+endif	# TRANSFER_LIST
+
diff --git a/make_helpers/defaults.mk b/make_helpers/defaults.mk
index aaabb27..b7e6f99 100644
--- a/make_helpers/defaults.mk
+++ b/make_helpers/defaults.mk
@@ -147,6 +147,9 @@
 # by lower ELs.
 HANDLE_EA_EL3_FIRST_NS		:= 0
 
+# Enable Handoff protocol using transfer lists
+TRANSFER_LIST			:= 0
+
 # Secure hash algorithm flag, accepts 3 values: sha256, sha384 and sha512.
 # The default value is sha256.
 HASH_ALG			:= sha256
diff --git a/plat/arm/board/fvp/fvp_spmd_logical_sp.c b/plat/arm/board/fvp/fvp_spmd_logical_sp.c
index 37b4466..8841fc1 100644
--- a/plat/arm/board/fvp/fvp_spmd_logical_sp.c
+++ b/plat/arm/board/fvp/fvp_spmd_logical_sp.c
@@ -32,7 +32,7 @@
 		panic();
 	}
 
-	num_partitions = ffa_partition_info_regs_get_last_idx(ret) + 1;
+	num_partitions = ffa_partition_info_regs_get_last_idx(&ret) + 1;
 	if (num_partitions > SPMD_LP_MAX_SUPPORTED_SP) {
 		panic();
 	}
@@ -41,7 +41,7 @@
 
 	for (uint16_t i = 0; i < num_partitions; i++) {
 		INFO("***Start Partition***\n");
-		if (!ffa_partition_info_regs_get_part_info(ret, i, &part_info[i]))
+		if (!ffa_partition_info_regs_get_part_info(&ret, i, &part_info[i]))
 			panic();
 		INFO("\tPartition ID: 0x%x\n", part_info[i].ep_id);
 		INFO("\tvCPU count:0x%x\n", part_info[i].execution_ctx_count);
diff --git a/plat/aspeed/ast2700/include/platform_reg.h b/plat/aspeed/ast2700/include/platform_reg.h
index 20ae32a..7f26865 100644
--- a/plat/aspeed/ast2700/include/platform_reg.h
+++ b/plat/aspeed/ast2700/include/platform_reg.h
@@ -18,11 +18,10 @@
 #define UART12_BASE	(UART_BASE + 0xb00)
 
 /* CPU-die SCU */
-#define SCU_CPU_BASE		U(0x12c02000)
-#define SCU_CPU_SMP_READY	(SCU_CPU_BASE + 0x780)
-#define SCU_CPU_SMP_EP1		(SCU_CPU_BASE + 0x788)
-#define SCU_CPU_SMP_EP2		(SCU_CPU_BASE + 0x790)
-#define SCU_CPU_SMP_EP3		(SCU_CPU_BASE + 0x798)
-#define SCU_CPU_SMP_POLLINSN	(SCU_CPU_BASE + 0x7a0)
+#define SCU_CPU_BASE	U(0x12c02000)
+#define SCU_CPU_SMP_EP0	(SCU_CPU_BASE + 0x780)
+#define SCU_CPU_SMP_EP1	(SCU_CPU_BASE + 0x788)
+#define SCU_CPU_SMP_EP2	(SCU_CPU_BASE + 0x790)
+#define SCU_CPU_SMP_EP3	(SCU_CPU_BASE + 0x798)
 
 #endif /* PLATFORM_REG_H */
diff --git a/plat/aspeed/ast2700/plat_bl31_setup.c b/plat/aspeed/ast2700/plat_bl31_setup.c
index 36e7338..fde5dbb 100644
--- a/plat/aspeed/ast2700/plat_bl31_setup.c
+++ b/plat/aspeed/ast2700/plat_bl31_setup.c
@@ -10,6 +10,7 @@
 #include <drivers/arm/gicv3.h>
 #include <drivers/console.h>
 #include <drivers/ti/uart/uart_16550.h>
+#include <lib/mmio.h>
 #include <lib/xlat_tables/xlat_tables_v2.h>
 #include <plat/common/platform.h>
 #include <platform_def.h>
@@ -55,7 +56,14 @@
 
 	console_set_scope(&console, CONSOLE_FLAG_BOOT | CONSOLE_FLAG_RUNTIME | CONSOLE_FLAG_CRASH);
 
-	bl31_params_parse_helper(arg0, &bl32_ep_info, &bl33_ep_info);
+	SET_PARAM_HEAD(&bl32_ep_info, PARAM_EP, VERSION_2, 0);
+	bl32_ep_info.pc = BL32_BASE;
+	SET_SECURITY_STATE(bl32_ep_info.h.attr, SECURE);
+
+	SET_PARAM_HEAD(&bl33_ep_info, PARAM_EP, VERSION_2, 0);
+	bl33_ep_info.pc = mmio_read_64(SCU_CPU_SMP_EP0);
+	bl33_ep_info.spsr = SPSR_64(MODE_EL2, MODE_SP_ELX, DISABLE_ALL_EXCEPTIONS);
+	SET_SECURITY_STATE(bl33_ep_info.h.attr, NON_SECURE);
 }
 
 void bl31_plat_arch_setup(void)
diff --git a/plat/aspeed/ast2700/plat_helpers.S b/plat/aspeed/ast2700/plat_helpers.S
index 1457692..c6d987e 100644
--- a/plat/aspeed/ast2700/plat_helpers.S
+++ b/plat/aspeed/ast2700/plat_helpers.S
@@ -10,6 +10,7 @@
 #include <cortex_a35.h>
 #include <platform_def.h>
 
+	.globl	platform_mem_init
 	.globl	plat_is_my_cpu_primary
 	.globl	plat_my_core_pos
 	.globl	plat_secondary_cold_boot_setup
@@ -18,6 +19,12 @@
 	.globl	plat_crash_console_putc
 	.globl	plat_crash_console_flush
 
+/* void platform_mem_init(void); */
+func platform_mem_init
+	/* DRAM init. is done by preceding MCU */
+	ret
+endfunc platform_mem_init
+
 /* unsigned int plat_is_my_cpu_primary(void); */
 func plat_is_my_cpu_primary
 	mrs	x0, mpidr_el1
@@ -37,6 +44,21 @@
 	ret
 endfunc plat_my_core_pos
 
+/* void plat_secondary_cold_boot_setup (void); */
+func plat_secondary_cold_boot_setup
+	mov	x0, xzr
+	bl	plat_my_core_pos
+	mov_imm	x1, SCU_CPU_SMP_EP0
+	add	x1, x1, x0, lsl #3
+
+poll_smp_mbox_go:
+	wfe
+	ldr	x0, [x1]
+	cmp	x0, xzr
+	beq	poll_smp_mbox_go
+	br	x0
+endfunc plat_secondary_cold_boot_setup
+
 /* unsigned int plat_get_syscnt_freq2(void); */
 func plat_get_syscnt_freq2
 	mov_imm	w0, PLAT_SYSCNT_CLKIN_HZ
diff --git a/plat/aspeed/ast2700/platform.mk b/plat/aspeed/ast2700/platform.mk
index 16ecf0a..873c60e 100644
--- a/plat/aspeed/ast2700/platform.mk
+++ b/plat/aspeed/ast2700/platform.mk
@@ -25,8 +25,10 @@
 	${GICV3_SOURCES}			\
 	${XLAT_TABLES_LIB_SRCS}
 
+RESET_TO_BL31 := 1
+
 PROGRAMMABLE_RESET_ADDRESS := 1
 
-COLD_BOOT_SINGLE_CPU := 1
+COLD_BOOT_SINGLE_CPU := 0
 
 ENABLE_SVE_FOR_NS := 0
diff --git a/plat/common/plat_gicv2.c b/plat/common/plat_gicv2.c
index 0f988dc..f78d2df 100644
--- a/plat/common/plat_gicv2.c
+++ b/plat/common/plat_gicv2.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015-2018, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2015-2023, Arm Limited and Contributors. All rights reserved.
  * Portions copyright (c) 2021-2022, ProvenRun S.A.S. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
@@ -193,9 +193,9 @@
 	gicv2_set_interrupt_priority(id, priority);
 }
 
-int plat_ic_has_interrupt_type(unsigned int type)
+bool plat_ic_has_interrupt_type(unsigned int type)
 {
-	int has_interrupt_type = 0;
+	bool has_interrupt_type = false;
 
 	switch (type) {
 #if GICV2_G0_FOR_EL3
@@ -204,7 +204,7 @@
 	case INTR_TYPE_S_EL1:
 #endif
 	case INTR_TYPE_NS:
-		has_interrupt_type = 1;
+		has_interrupt_type = true;
 		break;
 	default:
 		/* Do nothing in default case */
@@ -216,7 +216,7 @@
 
 void plat_ic_set_interrupt_type(unsigned int id, unsigned int type)
 {
-	unsigned int gicv2_type = 0U;
+	unsigned int gicv2_group = 0U;
 
 	/* Map canonical interrupt type to GICv2 type */
 	switch (type) {
@@ -225,17 +225,17 @@
 #else
 	case INTR_TYPE_S_EL1:
 #endif
-		gicv2_type = GICV2_INTR_GROUP0;
+		gicv2_group = GICV2_INTR_GROUP0;
 		break;
 	case INTR_TYPE_NS:
-		gicv2_type = GICV2_INTR_GROUP1;
+		gicv2_group = GICV2_INTR_GROUP1;
 		break;
 	default:
-		assert(0); /* Unreachable */
+		assert(false); /* Unreachable */
 		break;
 	}
 
-	gicv2_set_interrupt_type(id, gicv2_type);
+	gicv2_set_interrupt_group(id, gicv2_group);
 }
 
 void plat_ic_raise_el3_sgi(int sgi_num, u_register_t target)
diff --git a/plat/common/plat_gicv3.c b/plat/common/plat_gicv3.c
index e1420bb..baa70e0 100644
--- a/plat/common/plat_gicv3.c
+++ b/plat/common/plat_gicv3.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015-2018, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2015-2023, Arm Limited and Contributors. All rights reserved.
  * Portions copyright (c) 2021-2022, ProvenRun S.A.S. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
@@ -47,10 +47,6 @@
 #pragma weak plat_ic_set_interrupt_pending
 #pragma weak plat_ic_clear_interrupt_pending
 
-CASSERT((INTR_TYPE_S_EL1 == INTR_GROUP1S) &&
-	(INTR_TYPE_NS == INTR_GROUP1NS) &&
-	(INTR_TYPE_EL3 == INTR_GROUP0), assert_interrupt_type_mismatch);
-
 /*
  * This function returns the highest priority pending interrupt at
  * the Interrupt controller
@@ -116,12 +112,26 @@
 
 /*
  * This function returns the type of the interrupt `id`, depending on how
- * the interrupt has been configured in the interrupt controller
+ * the interrupt has been configured in the interrupt controller.
  */
 uint32_t plat_ic_get_interrupt_type(uint32_t id)
 {
+	unsigned int group;
+
 	assert(IS_IN_EL3());
-	return gicv3_get_interrupt_type(id, plat_my_core_pos());
+	group = gicv3_get_interrupt_group(id, plat_my_core_pos());
+
+	switch (group) {
+	case INTR_GROUP0:
+		return INTR_TYPE_EL3;
+	case INTR_GROUP1S:
+		return INTR_TYPE_S_EL1;
+	case INTR_GROUP1NS:
+		return INTR_TYPE_NS;
+	default:
+		assert(false); /* Unreachable */
+		return INTR_TYPE_EL3;
+	}
 }
 
 /*
@@ -225,16 +235,37 @@
 	gicv3_set_interrupt_priority(id, plat_my_core_pos(), priority);
 }
 
-int plat_ic_has_interrupt_type(unsigned int type)
+bool plat_ic_has_interrupt_type(unsigned int type)
 {
-	assert((type == INTR_TYPE_EL3) || (type == INTR_TYPE_S_EL1) ||
-			(type == INTR_TYPE_NS));
-	return 1;
+	if ((type == INTR_TYPE_EL3) || (type == INTR_TYPE_S_EL1) ||
+			(type == INTR_TYPE_NS)) {
+		return true;
+	}
+
+	return false;
 }
 
 void plat_ic_set_interrupt_type(unsigned int id, unsigned int type)
 {
-	gicv3_set_interrupt_type(id, plat_my_core_pos(), type);
+	unsigned int group;
+
+	switch (type) {
+	case INTR_TYPE_EL3:
+		group = INTR_GROUP0;
+		break;
+	case INTR_TYPE_S_EL1:
+		group = INTR_GROUP1S;
+		break;
+	case INTR_TYPE_NS:
+		group = INTR_GROUP1NS;
+		break;
+	default:
+		assert(false); /* Unreachable */
+		group = INTR_GROUP0;
+		break;
+	}
+
+	gicv3_set_interrupt_group(id, plat_my_core_pos(), group);
 }
 
 void plat_ic_raise_el3_sgi(int sgi_num, u_register_t target)
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
diff --git a/plat/qemu/common/qemu_common.c b/plat/qemu/common/qemu_common.c
index 98be491..d4488a4 100644
--- a/plat/qemu/common/qemu_common.c
+++ b/plat/qemu/common/qemu_common.c
@@ -47,6 +47,14 @@
 #define MAP_FLASH1	MAP_REGION_FLAT(QEMU_FLASH1_BASE, QEMU_FLASH1_SIZE, \
 					MT_MEMORY | MT_RO | MT_SECURE)
 
+#ifdef FW_HANDOFF_BASE
+#define MAP_FW_HANDOFF MAP_REGION_FLAT(FW_HANDOFF_BASE, FW_HANDOFF_SIZE, \
+				       MT_MEMORY | MT_RW | MT_SECURE)
+#endif
+#ifdef FW_NS_HANDOFF_BASE
+#define MAP_FW_NS_HANDOFF MAP_REGION_FLAT(FW_NS_HANDOFF_BASE, FW_HANDOFF_SIZE, \
+					  MT_MEMORY | MT_RW | MT_NS)
+#endif
 /*
  * Table of regions for various BL stages to map using the MMU.
  * This doesn't include TZRAM as the 'mem_layout' argument passed to
@@ -85,6 +93,9 @@
 #else
 	MAP_BL32_MEM,
 #endif
+#ifdef MAP_FW_HANDOFF
+	MAP_FW_HANDOFF,
+#endif
 	{0}
 };
 #endif
@@ -98,6 +109,12 @@
 #ifdef MAP_DEVICE2
 	MAP_DEVICE2,
 #endif
+#ifdef MAP_FW_HANDOFF
+	MAP_FW_HANDOFF,
+#endif
+#ifdef MAP_FW_NS_HANDOFF
+	MAP_FW_NS_HANDOFF,
+#endif
 #if SPM_MM
 	MAP_NS_DRAM0,
 	QEMU_SPM_BUF_EL3_MMAP,
diff --git a/plat/qemu/common/qemu_image_load.c b/plat/qemu/common/qemu_image_load.c
index 9970d1d..2b02a67 100644
--- a/plat/qemu/common/qemu_image_load.c
+++ b/plat/qemu/common/qemu_image_load.c
@@ -1,11 +1,13 @@
 /*
- * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2017-2023, Arm Limited and Contributors. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
 
 #include <common/desc_image_load.h>
 
+#include "qemu_private.h"
+
 /*******************************************************************************
  * This function is a wrapper of a common function which flushes the data
  * structures so that they are visible in memory for the next BL image.
@@ -13,6 +15,7 @@
 void plat_flush_next_bl_params(void)
 {
 	flush_bl_params_desc();
+	qemu_bl2_sync_transfer_list();
 }
 
 /*******************************************************************************
diff --git a/plat/qemu/common/qemu_private.h b/plat/qemu/common/qemu_private.h
index e80a88d..c8912b2 100644
--- a/plat/qemu/common/qemu_private.h
+++ b/plat/qemu/common/qemu_private.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015-2023, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2015-2023, Arm Limited and Contributors. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -40,4 +40,6 @@
 			size_t log_size,
 			uintptr_t *ns_log_addr);
 
+void qemu_bl2_sync_transfer_list(void);
+
 #endif /* QEMU_PRIVATE_H */
diff --git a/plat/qemu/qemu/include/platform_def.h b/plat/qemu/qemu/include/platform_def.h
index 93a3ce8..903c809 100644
--- a/plat/qemu/qemu/include/platform_def.h
+++ b/plat/qemu/qemu/include/platform_def.h
@@ -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
  */
@@ -151,9 +151,17 @@
  * current BL3-1 debug size plus a little space for growth.
  */
 #define BL31_BASE			(BL31_LIMIT - 0x60000)
-#define BL31_LIMIT			(BL_RAM_BASE + BL_RAM_SIZE)
+#define BL31_LIMIT			(BL_RAM_BASE + BL_RAM_SIZE - FW_HANDOFF_SIZE)
 #define BL31_PROGBITS_LIMIT		BL1_RW_BASE
 
+#if TRANSFER_LIST
+#define FW_HANDOFF_BASE			BL31_LIMIT
+#define FW_HANDOFF_LIMIT		(FW_HANDOFF_BASE + FW_HANDOFF_SIZE)
+#define FW_HANDOFF_SIZE			0x4000
+#else
+#define FW_HANDOFF_SIZE			0
+#endif
+
 
 /*
  * BL3-2 specific defines.
@@ -172,16 +180,20 @@
 # define BL32_MEM_BASE			BL_RAM_BASE
 # define BL32_MEM_SIZE			BL_RAM_SIZE
 # define BL32_BASE			BL32_SRAM_BASE
-# define BL32_LIMIT			BL32_SRAM_LIMIT
+# define BL32_LIMIT			(BL32_SRAM_LIMIT - FW_HANDOFF_SIZE)
 #elif BL32_RAM_LOCATION_ID == SEC_DRAM_ID
 # define BL32_MEM_BASE			SEC_DRAM_BASE
 # define BL32_MEM_SIZE			SEC_DRAM_SIZE
 # define BL32_BASE			BL32_DRAM_BASE
-# define BL32_LIMIT			BL32_DRAM_LIMIT
+# define BL32_LIMIT			(BL32_DRAM_LIMIT - FW_HANDOFF_SIZE)
 #else
 # error "Unsupported BL32_RAM_LOCATION_ID value"
 #endif
 
+#if TRANSFER_LIST
+#define FW_NS_HANDOFF_BASE		(NS_IMAGE_OFFSET - FW_HANDOFF_SIZE)
+#endif
+
 #define NS_IMAGE_OFFSET			(NS_DRAM0_BASE + 0x20000000)
 #define NS_IMAGE_MAX_SIZE		(NS_DRAM0_SIZE - 0x20000000)
 
diff --git a/plat/qemu/qemu/platform.mk b/plat/qemu/qemu/platform.mk
index 16e89c1..e902c12 100644
--- a/plat/qemu/qemu/platform.mk
+++ b/plat/qemu/qemu/platform.mk
@@ -39,6 +39,10 @@
 add-lib-optee 		:= 	yes
 endif
 
+ifeq (${TRANSFER_LIST},1)
+include lib/transfer_list/transfer_list.mk
+endif
+
 ifeq ($(NEED_BL32),yes)
 $(eval $(call add_define,QEMU_LOAD_BL32))
 endif
diff --git a/plat/rpi/rpi3/platform.mk b/plat/rpi/rpi3/platform.mk
index 53c97e2..06393e4 100644
--- a/plat/rpi/rpi3/platform.mk
+++ b/plat/rpi/rpi3/platform.mk
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2013-2020, ARM Limited and Contributors. All rights reserved.
+# Copyright (c) 2013-2023, Arm Limited and Contributors. All rights reserved.
 #
 # SPDX-License-Identifier: BSD-3-Clause
 #
@@ -44,6 +44,7 @@
 				plat/rpi/common/rpi3_io_storage.c
 
 BL31_SOURCES		+=	lib/cpus/aarch64/cortex_a53.S		\
+				plat/common/plat_gicv2.c		\
 				plat/common/plat_psci_common.c		\
 				plat/rpi/rpi3/rpi3_bl31_setup.c		\
 				plat/rpi/common/rpi3_pm.c		\
diff --git a/services/std_svc/spmd/spmd_logical_sp.c b/services/std_svc/spmd/spmd_logical_sp.c
index 964b36b..d992187 100644
--- a/services/std_svc/spmd/spmd_logical_sp.c
+++ b/services/std_svc/spmd/spmd_logical_sp.c
@@ -356,7 +356,7 @@
  * other code to consume.
  */
 bool ffa_partition_info_regs_get_part_info(
-	struct ffa_value args, uint8_t idx,
+	struct ffa_value *args, uint8_t idx,
 	struct ffa_partition_info_v1_1 *partition_info)
 {
 	uint64_t *arg_ptrs;
@@ -375,7 +375,7 @@
 	 * function, arg1 is reserved, arg2 encodes indices. arg3 and greater
 	 * values reflect partition properties.
 	 */
-	arg_ptrs = (uint64_t *)&args + ((idx * 3) + 3);
+	arg_ptrs = (uint64_t *)args + ((idx * 3) + 3);
 	info = *arg_ptrs;
 
 	arg_ptrs++;