feat(fvp): update HW_CONFIG DT loading mechanism

Currently, HW-config is loaded into non-secure memory, which mean
a malicious NS-agent could tamper with it. Ideally, this shouldn't
be an issue since no software runs in non-secure world at this time
(non-secure world has not been started yet).

It does not provide a guarantee though since malicious external
NS-agents can take control of this memory region for update/corruption
after BL2 loads it and before BL31/BL32/SP_MIN consumes it. The threat
is mapped to Threat ID#3 (Bypass authentication scenario) in threat
model [1].

Hence modified the code as below -
1. BL2 loads the HW_CONFIG into secure memory
2. BL2 makes a copy of the HW_CONFIG in the non-secure memory at an
   address provided by the newly added property(ns-load-address) in
   the 'hw-config' node of the FW_CONFIG
3. SP_MIN receives the FW_CONFIG address from BL2 via arg1 so that
   it can retrieve details (address and size) of HW_CONFIG from
   FW_CONFIG
4. A secure and non-secure HW_CONFIG address will eventually be used
   by BL31/SP_MIN/BL32 and BL33 components respectively
5. BL31/SP_MIN dynamically maps the Secure HW_CONFIG region and reads
   information from it to local variables (structures) and then
   unmaps it
6. Reduce HW_CONFIG maximum size from 16MB to 1MB; it appears
   sufficient, and it will also create a free space for any future
   components to be added to memory

[1]: https://trustedfirmware-a.readthedocs.io/en/latest/threat_model/threat_model.html

Change-Id: I1d431f3e640ded60616604b1c33aa638b9a1e55e
Signed-off-by: Manish V Badarkhe <Manish.Badarkhe@arm.com>
diff --git a/include/plat/arm/common/arm_def.h b/include/plat/arm/common/arm_def.h
index 2af8c11..29edb4b 100644
--- a/include/plat/arm/common/arm_def.h
+++ b/include/plat/arm/common/arm_def.h
@@ -284,12 +284,10 @@
 					ARM_EL3_TZC_DRAM1_SIZE,		\
 					MT_MEMORY | MT_RW | EL3_PAS)
 
-#if defined(SPD_spmd)
 #define ARM_MAP_TRUSTED_DRAM	MAP_REGION_FLAT(			\
 					PLAT_ARM_TRUSTED_DRAM_BASE,	\
 					PLAT_ARM_TRUSTED_DRAM_SIZE,	\
 					MT_MEMORY | MT_RW | MT_SECURE)
-#endif
 
 #if ENABLE_RME
 #define ARM_MAP_RMM_DRAM	MAP_REGION_FLAT(			\
diff --git a/plat/arm/board/fvp/fdts/fvp_fw_config.dts b/plat/arm/board/fvp/fdts/fvp_fw_config.dts
index c26b519..577ac74 100644
--- a/plat/arm/board/fvp/fdts/fvp_fw_config.dts
+++ b/plat/arm/board/fvp/fdts/fvp_fw_config.dts
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2019-2021, ARM Limited. All rights reserved.
+ * Copyright (c) 2019-2022, Arm Limited. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -19,9 +19,10 @@
 		};
 
 		hw-config {
-			load-address = <0x0 0x82000000>;
-			max-size = <0x01000000>;
+			load-address = <0x0 0x07f00000>;
+			max-size = <0x00100000>;
 			id = <HW_CONFIG_ID>;
+			ns-load-address = <0x0 0x82000000>;
 		};
 
 		/*
diff --git a/plat/arm/board/fvp/fvp_bl2_setup.c b/plat/arm/board/fvp/fvp_bl2_setup.c
index 5a17a0d..4eee522 100644
--- a/plat/arm/board/fvp/fvp_bl2_setup.c
+++ b/plat/arm/board/fvp/fvp_bl2_setup.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013-2021, Arm Limited and Contributors. All rights reserved.
+ * Copyright (c) 2013-2022, Arm Limited and Contributors. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -40,17 +40,23 @@
 struct bl_params *plat_get_next_bl_params(void)
 {
 	struct bl_params *arm_bl_params;
+	const struct dyn_cfg_dtb_info_t *hw_config_info __unused;
+	bl_mem_params_node_t *param_node __unused;
 
 	arm_bl_params = arm_get_next_bl_params();
 
-#if __aarch64__ && !BL2_AT_EL3
+#if !BL2_AT_EL3 && !EL3_PAYLOAD_BASE
 	const struct dyn_cfg_dtb_info_t *fw_config_info;
-	bl_mem_params_node_t *param_node;
-	uintptr_t fw_config_base = 0U;
+	uintptr_t fw_config_base = 0UL;
 	entry_point_info_t *ep_info;
 
+#if __aarch64__
 	/* Get BL31 image node */
 	param_node = get_bl_mem_params_node(BL31_IMAGE_ID);
+#else /* aarch32 */
+	/* Get SP_MIN image node */
+	param_node = get_bl_mem_params_node(BL32_IMAGE_ID);
+#endif /* __aarch64__ */
 	assert(param_node != NULL);
 
 	/* get fw_config load address */
@@ -58,15 +64,41 @@
 	assert(fw_config_info != NULL);
 
 	fw_config_base = fw_config_info->config_addr;
-	assert(fw_config_base != 0U);
+	assert(fw_config_base != 0UL);
 
 	/*
-	 * Get the entry point info of BL31 image and override
+	 * Get the entry point info of next executable image and override
 	 * arg1 of entry point info with fw_config base address
 	 */
 	ep_info = &param_node->ep_info;
 	ep_info->args.arg1 = (uint32_t)fw_config_base;
-#endif /* __aarch64__ && !BL2_AT_EL3 */
+
+	/* grab NS HW config address */
+	hw_config_info = FCONF_GET_PROPERTY(dyn_cfg, dtb, HW_CONFIG_ID);
+
+	/* To retrieve actual size of the HW_CONFIG */
+	param_node = get_bl_mem_params_node(HW_CONFIG_ID);
+	assert(param_node != NULL);
+
+	/* Copy HW config from Secure address to NS address */
+	memcpy((void *)hw_config_info->ns_config_addr,
+	       (void *)hw_config_info->config_addr,
+	       (size_t)param_node->image_info.image_size);
+
+	/*
+	 * Ensure HW-config device tree committed to memory, as there is
+	 * a possibility to use HW-config without cache and MMU enabled
+	 * at BL33
+	 */
+	flush_dcache_range(hw_config_info->ns_config_addr,
+			   param_node->image_info.image_size);
+
+	param_node = get_bl_mem_params_node(BL33_IMAGE_ID);
+	assert(param_node != NULL);
+
+	/* Update BL33's ep info with NS HW config address  */
+	param_node->ep_info.args.arg1 = hw_config_info->ns_config_addr;
+#endif /* !BL2_AT_EL3 && !EL3_PAYLOAD_BASE */
 
 	return arm_bl_params;
 }
diff --git a/plat/arm/board/fvp/fvp_bl31_setup.c b/plat/arm/board/fvp/fvp_bl31_setup.c
index a94a4f4..dd90965 100644
--- a/plat/arm/board/fvp/fvp_bl31_setup.c
+++ b/plat/arm/board/fvp/fvp_bl31_setup.c
@@ -17,6 +17,8 @@
 
 #include "fvp_private.h"
 
+static const struct dyn_cfg_dtb_info_t *hw_config_info __unused;
+
 void __init bl31_early_platform_setup2(u_register_t arg0,
 		u_register_t arg1, u_register_t arg2, u_register_t arg3)
 {
@@ -34,6 +36,17 @@
 	if (soc_fw_config_info != NULL) {
 		arg1 = soc_fw_config_info->config_addr;
 	}
+
+	/*
+	 * arg2 is currently holding the 'secure' address of HW_CONFIG.
+	 * But arm_bl31_early_platform_setup() below expects the 'non-secure'
+	 * address of HW_CONFIG (which it will pass to BL33).
+	 * This why we need to override arg2 here.
+	 */
+	hw_config_info = FCONF_GET_PROPERTY(dyn_cfg, dtb, HW_CONFIG_ID);
+	assert(hw_config_info != NULL);
+	assert(hw_config_info->ns_config_addr != 0UL);
+	arg2 = hw_config_info->ns_config_addr;
 #endif /* !RESET_TO_BL31 && !BL2_AT_EL3 */
 
 	arm_bl31_early_platform_setup((void *)arg0, arg1, arg2, (void *)arg3);
@@ -66,22 +79,57 @@
 
 void __init bl31_plat_arch_setup(void)
 {
+	int rc __unused;
+	uintptr_t hw_config_base_align __unused;
+	size_t mapped_size_align __unused;
+
 	arm_bl31_plat_arch_setup();
 
 	/*
 	 * For RESET_TO_BL31 systems, BL31 is the first bootloader to run.
 	 * So there is no BL2 to load the HW_CONFIG dtb into memory before
-	 * control is passed to BL31.
+	 * control is passed to BL31. The code below relies on dynamic mapping
+	 * capability, which is not supported by xlat tables lib V1.
+	 * TODO: remove the ARM_XLAT_TABLES_LIB_V1 check when its support
+	 * gets deprecated.
 	 */
-#if !RESET_TO_BL31 && !BL2_AT_EL3
-	/* HW_CONFIG was also loaded by BL2 */
-	const struct dyn_cfg_dtb_info_t *hw_config_info;
-
-	hw_config_info = FCONF_GET_PROPERTY(dyn_cfg, dtb, HW_CONFIG_ID);
+#if !RESET_TO_BL31 && !BL2_AT_EL3 && !ARM_XLAT_TABLES_LIB_V1
 	assert(hw_config_info != NULL);
+	assert(hw_config_info->config_addr != 0UL);
 
+	/* Page aligned address and size if necessary */
+	hw_config_base_align = page_align(hw_config_info->config_addr, DOWN);
+	mapped_size_align = page_align(hw_config_info->config_max_size, UP);
+
+	if ((hw_config_info->config_addr != hw_config_base_align) &&
+	    (hw_config_info->config_max_size == mapped_size_align)) {
+		mapped_size_align += PAGE_SIZE;
+	}
+
+	/*
+	 * map dynamically HW config region with its aligned base address and
+	 * size
+	 */
+	rc = mmap_add_dynamic_region((unsigned long long)hw_config_base_align,
+				     hw_config_base_align,
+				     mapped_size_align,
+				     MT_RO_DATA);
+	if (rc != 0) {
+		ERROR("Error while mapping HW_CONFIG device tree (%d).\n", rc);
+		panic();
+	}
+
+	/* Populate HW_CONFIG device tree with the mapped address */
 	fconf_populate("HW_CONFIG", hw_config_info->config_addr);
-#endif
+
+	/* unmap the HW_CONFIG memory region */
+	rc = mmap_remove_dynamic_region(hw_config_base_align, mapped_size_align);
+	if (rc != 0) {
+		ERROR("Error while unmapping HW_CONFIG device tree (%d).\n",
+		      rc);
+		panic();
+	}
+#endif /* !RESET_TO_BL31 && !BL2_AT_EL3 && !ARM_XLAT_TABLES_LIB_V1 */
 }
 
 unsigned int plat_get_syscnt_freq2(void)
diff --git a/plat/arm/board/fvp/fvp_common.c b/plat/arm/board/fvp/fvp_common.c
index d8d19de..e9f725c 100644
--- a/plat/arm/board/fvp/fvp_common.c
+++ b/plat/arm/board/fvp/fvp_common.c
@@ -104,9 +104,10 @@
 #ifdef __aarch64__
 	ARM_MAP_DRAM2,
 #endif
-#if defined(SPD_spmd)
+	/*
+	 * Required to load HW_CONFIG, SPMC and SPs to trusted DRAM.
+	 */
 	ARM_MAP_TRUSTED_DRAM,
-#endif
 #if ENABLE_RME
 	ARM_MAP_RMM_DRAM,
 	ARM_MAP_GPT_L1_DRAM,
@@ -166,8 +167,6 @@
 #if SPM_MM
 	ARM_SPM_BUF_EL3_MMAP,
 #endif
-	/* Required by fconf APIs to read HW_CONFIG dtb loaded into DRAM */
-	ARM_DTB_DRAM_NS,
 #if ENABLE_RME
 	ARM_MAP_GPT_L1_DRAM,
 #endif
@@ -197,8 +196,6 @@
 	V2M_MAP_IOFPGA,
 	MAP_DEVICE0,
 	MAP_DEVICE1,
-	/* Required by fconf APIs to read HW_CONFIG dtb loaded into DRAM */
-	ARM_DTB_DRAM_NS,
 	{0}
 };
 #endif
diff --git a/plat/arm/board/fvp/include/platform_def.h b/plat/arm/board/fvp/include/platform_def.h
index 5e5ddce..bbddd4f 100644
--- a/plat/arm/board/fvp/include/platform_def.h
+++ b/plat/arm/board/fvp/include/platform_def.h
@@ -86,10 +86,6 @@
 #define FVP_DTB_DRAM_MAP_START		ULL(0x82000000)
 #define FVP_DTB_DRAM_MAP_SIZE		ULL(0x02000000)	/* 32 MB */
 
-#define ARM_DTB_DRAM_NS			MAP_REGION_FLAT(		\
-					FVP_DTB_DRAM_MAP_START,		\
-					FVP_DTB_DRAM_MAP_SIZE,		\
-					MT_MEMORY | MT_RO | MT_NS)
 /*
  * Load address of BL33 for this platform port
  */
diff --git a/plat/arm/board/fvp/platform.mk b/plat/arm/board/fvp/platform.mk
index c9f5551..19f913e 100644
--- a/plat/arm/board/fvp/platform.mk
+++ b/plat/arm/board/fvp/platform.mk
@@ -333,15 +333,10 @@
 endif
 
 # Enable the dynamic translation tables library.
-ifeq (${ARCH},aarch32)
-    ifeq (${RESET_TO_SP_MIN},1)
+ifeq ($(filter 1,${BL2_AT_EL3} ${ARM_XLAT_TABLES_LIB_V1}),)
+    ifeq (${ARCH},aarch32)
         BL32_CPPFLAGS	+=	-DPLAT_XLAT_TABLES_DYNAMIC
-    endif
-else # AArch64
-    ifeq (${RESET_TO_BL31},1)
-        BL31_CPPFLAGS	+=	-DPLAT_XLAT_TABLES_DYNAMIC
-    endif
-    ifeq (${SPD},trusty)
+    else # AArch64
         BL31_CPPFLAGS	+=	-DPLAT_XLAT_TABLES_DYNAMIC
     endif
 endif
diff --git a/plat/arm/board/fvp/sp_min/fvp_sp_min_setup.c b/plat/arm/board/fvp/sp_min/fvp_sp_min_setup.c
index 763b42a..9ab36a6 100644
--- a/plat/arm/board/fvp/sp_min/fvp_sp_min_setup.c
+++ b/plat/arm/board/fvp/sp_min/fvp_sp_min_setup.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016-2020, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2016-2022, Arm Limited and Contributors. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -9,15 +9,31 @@
 #include <bl32/sp_min/platform_sp_min.h>
 #include <common/debug.h>
 #include <lib/fconf/fconf.h>
+#include <lib/fconf/fconf_dyn_cfg_getter.h>
 #include <plat/arm/common/plat_arm.h>
 
 #include "../fvp_private.h"
 
-uintptr_t hw_config_dtb;
-
 void plat_arm_sp_min_early_platform_setup(u_register_t arg0, u_register_t arg1,
 			u_register_t arg2, u_register_t arg3)
 {
+	const struct dyn_cfg_dtb_info_t *tos_fw_config_info __unused;
+
+	/* Initialize the console to provide early debug support */
+	arm_console_boot_init();
+
+#if !RESET_TO_SP_MIN && !BL2_AT_EL3
+
+	INFO("SP_MIN FCONF: FW_CONFIG address = %lx\n", (uintptr_t)arg1);
+	/* Fill the properties struct with the info from the config dtb */
+	fconf_populate("FW_CONFIG", arg1);
+
+	tos_fw_config_info = FCONF_GET_PROPERTY(dyn_cfg, dtb, TOS_FW_CONFIG_ID);
+	if (tos_fw_config_info != NULL) {
+		arg1 = tos_fw_config_info->config_addr;
+	}
+#endif /* !RESET_TO_SP_MIN && !BL2_AT_EL3 */
+
 	arm_sp_min_early_platform_setup((void *)arg0, arg1, arg2, (void *)arg3);
 
 	/* Initialize the platform config for future decision making */
@@ -37,12 +53,15 @@
 	 * FVP PSCI code will enable coherency for other clusters.
 	 */
 	fvp_interconnect_enable();
-
-	hw_config_dtb = arg2;
 }
 
 void sp_min_plat_arch_setup(void)
 {
+	int rc __unused;
+	const struct dyn_cfg_dtb_info_t *hw_config_info __unused;
+	uintptr_t hw_config_base_align __unused;
+	size_t mapped_size_align __unused;
+
 	arm_sp_min_plat_arch_setup();
 
 	/*
@@ -50,11 +69,53 @@
 	 * to run. So there is no BL2 to load the HW_CONFIG dtb into memory
 	 * before control is passed to SP_MIN.
 	 * Also, BL2 skips loading HW_CONFIG dtb for BL2_AT_EL3 builds.
+	 * The code below relies on dynamic mapping capability, which is not
+	 * supported by xlat tables lib V1.
+	 * TODO: remove the ARM_XLAT_TABLES_LIB_V1 check when its support
+	 * gets deprecated.
 	 */
-#if !RESET_TO_SP_MIN && !BL2_AT_EL3
-	assert(hw_config_dtb != 0U);
+#if !RESET_TO_SP_MIN && !BL2_AT_EL3 && !ARM_XLAT_TABLES_LIB_V1
+	hw_config_info = FCONF_GET_PROPERTY(dyn_cfg, dtb, HW_CONFIG_ID);
+	assert(hw_config_info != NULL);
+	assert(hw_config_info->config_addr != 0UL);
+
+	INFO("SP_MIN FCONF: HW_CONFIG address = %p\n",
+	     (void *)hw_config_info->config_addr);
+
+	/*
+	 * Preferrably we expect this address and size are page aligned,
+	 * but if they are not then align it.
+	 */
+	hw_config_base_align = page_align(hw_config_info->config_addr, DOWN);
+	mapped_size_align = page_align(hw_config_info->config_max_size, UP);
+
+	if ((hw_config_info->config_addr != hw_config_base_align) &&
+	    (hw_config_info->config_max_size == mapped_size_align)) {
+		mapped_size_align += PAGE_SIZE;
+	}
+
+	/*
+	 * map dynamically HW config region with its aligned base address and
+	 * size
+	 */
+	rc = mmap_add_dynamic_region((unsigned long long)hw_config_base_align,
+				     hw_config_base_align,
+				     mapped_size_align,
+				     MT_RO_DATA);
+	if (rc != 0) {
+		ERROR("Error while mapping HW_CONFIG device tree (%d).\n", rc);
+		panic();
+	}
+
+	/* Populate HW_CONFIG device tree with the mapped address */
+	fconf_populate("HW_CONFIG", hw_config_info->config_addr);
 
-	INFO("SP_MIN FCONF: HW_CONFIG address = %p\n", (void *)hw_config_dtb);
-	fconf_populate("HW_CONFIG", hw_config_dtb);
-#endif
+	/* unmap the HW_CONFIG memory region */
+	rc = mmap_remove_dynamic_region(hw_config_base_align, mapped_size_align);
+	if (rc != 0) {
+		ERROR("Error while unmapping HW_CONFIG device tree (%d).\n",
+		      rc);
+		panic();
+	}
+#endif /* !RESET_TO_SP_MIN && !BL2_AT_EL3 && !ARM_XLAT_TABLES_LIB_V1 */
 }
diff --git a/plat/arm/board/fvp/sp_min/sp_min-fvp.mk b/plat/arm/board/fvp/sp_min/sp_min-fvp.mk
index 0d8cca5..183d802 100644
--- a/plat/arm/board/fvp/sp_min/sp_min-fvp.mk
+++ b/plat/arm/board/fvp/sp_min/sp_min-fvp.mk
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2016-2021, ARM Limited and Contributors. All rights reserved.
+# Copyright (c) 2016-2022, Arm Limited and Contributors. All rights reserved.
 #
 # SPDX-License-Identifier: BSD-3-Clause
 #
@@ -25,7 +25,8 @@
 # Added separately from the above list for better readability
 ifeq ($(filter 1,${BL2_AT_EL3} ${RESET_TO_SP_MIN}),)
 BL32_SOURCES		+=	lib/fconf/fconf.c				\
-				plat/arm/board/fvp/fconf/fconf_hw_config_getter.c
+				lib/fconf/fconf_dyn_cfg_getter.c		\
+				plat/arm/board/fvp/fconf/fconf_hw_config_getter.c \
 
 BL32_SOURCES		+=	${FDT_WRAPPERS_SOURCES}