feat(stm32mp1): retry 3 times FWU trial boot

If we reboot 3 times in trial mode, BL2 will select previous boot image.

Signed-off-by: Nicolas Toromanoff <nicolas.toromanoff@foss.st.com>
Change-Id: I82b423cc84f0471fdb6fa7c393fc5fe411d25c06
diff --git a/plat/st/common/bl2_io_storage.c b/plat/st/common/bl2_io_storage.c
index e129dfd..5cc3390 100644
--- a/plat/st/common/bl2_io_storage.c
+++ b/plat/st/common/bl2_io_storage.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015-2021, ARM Limited and Contributors. All rights reserved.
+ * Copyright (c) 2015-2022, ARM Limited and Contributors. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -485,22 +485,46 @@
 
 #if (STM32MP_SDMMC || STM32MP_EMMC) && PSA_FWU_SUPPORT
 /*
- * Eventually, this function will return the
- * boot index to be passed on to the Update
- * Agent after performing certain checks like
- * a watchdog timeout, or Auth failure while
- * trying to load from a certain bank.
- * For now, since we do not have that logic
- * implemented, just pass the active_index
- * read from the metadata.
+ * In each boot in non-trial mode, we set the BKP register to
+ * FWU_MAX_TRIAL_REBOOT, and return the active_index from metadata.
+ *
+ * As long as the update agent didn't update the "accepted" field in metadata
+ * (i.e. we are in trial mode), we select the new active_index.
+ * To avoid infinite boot loop at trial boot we decrement a BKP register.
+ * If this counter is 0:
+ *     - an unexpected TAMPER event raised (that resets the BKP registers to 0)
+ *     - a power-off occurs before the update agent was able to update the
+ *       "accepted' field
+ *     - we already boot FWU_MAX_TRIAL_REBOOT times in trial mode.
+ * we select the previous_active_index.
  */
+#define INVALID_BOOT_IDX		0xFFFFFFFF
+
 uint32_t plat_fwu_get_boot_idx(void)
 {
-	const struct fwu_metadata *metadata;
+	/*
+	 * Select boot index and update boot counter only once per boot
+	 * even if this function is called several times.
+	 */
+	static uint32_t boot_idx = INVALID_BOOT_IDX;
+	const struct fwu_metadata *data;
 
-	metadata = fwu_get_metadata();
+	data = fwu_get_metadata();
+
+	if (boot_idx == INVALID_BOOT_IDX) {
+		boot_idx = data->active_index;
+		if (fwu_is_trial_run_state()) {
+			if (stm32_get_and_dec_fwu_trial_boot_cnt() == 0U) {
+				WARN("Trial FWU fails %u times\n",
+				     FWU_MAX_TRIAL_REBOOT);
+				boot_idx = data->previous_active_index;
+			}
+		} else {
+			stm32_set_max_fwu_trial_boot_cnt();
+		}
+	}
 
-	return metadata->active_index;
+	return boot_idx;
 }
 
 static void *stm32_get_image_spec(const uuid_t *img_type_uuid)
diff --git a/plat/st/common/include/stm32mp_common.h b/plat/st/common/include/stm32mp_common.h
index d8d1c13..0010cd8 100644
--- a/plat/st/common/include/stm32mp_common.h
+++ b/plat/st/common/include/stm32mp_common.h
@@ -129,6 +129,8 @@
 
 #if !STM32MP_USE_STM32IMAGE && PSA_FWU_SUPPORT
 void stm32mp1_fwu_set_boot_idx(void);
+uint32_t stm32_get_and_dec_fwu_trial_boot_cnt(void);
+void stm32_set_max_fwu_trial_boot_cnt(void);
 #endif /* !STM32MP_USE_STM32IMAGE && PSA_FWU_SUPPORT */
 
 #endif /* STM32MP_COMMON_H */
diff --git a/plat/st/stm32mp1/stm32mp1_def.h b/plat/st/stm32mp1/stm32mp1_def.h
index d869978..3b711a3 100644
--- a/plat/st/stm32mp1/stm32mp1_def.h
+++ b/plat/st/stm32mp1/stm32mp1_def.h
@@ -511,6 +511,9 @@
 /* UID OTP */
 #define UID_WORD_NB			U(3)
 
+/* FWU configuration (max supported value is 15) */
+#define FWU_MAX_TRIAL_REBOOT		U(3)
+
 /*******************************************************************************
  * STM32MP1 TAMP
  ******************************************************************************/
diff --git a/plat/st/stm32mp1/stm32mp1_private.c b/plat/st/stm32mp1/stm32mp1_private.c
index 0624d46..1617afd 100644
--- a/plat/st/stm32mp1/stm32mp1_private.c
+++ b/plat/st/stm32mp1/stm32mp1_private.c
@@ -748,4 +748,35 @@
 			   TAMP_BOOT_FWU_INFO_IDX_MSK);
 	clk_disable(RTCAPB);
 }
+
+uint32_t stm32_get_and_dec_fwu_trial_boot_cnt(void)
+{
+	uintptr_t bkpr_fwu_cnt = tamp_bkpr(TAMP_BOOT_FWU_INFO_REG_ID);
+	uint32_t try_cnt;
+
+	clk_enable(RTCAPB);
+	try_cnt = (mmio_read_32(bkpr_fwu_cnt) & TAMP_BOOT_FWU_INFO_CNT_MSK) >>
+		TAMP_BOOT_FWU_INFO_CNT_OFF;
+
+	assert(try_cnt <= FWU_MAX_TRIAL_REBOOT);
+
+	if (try_cnt != 0U) {
+		mmio_clrsetbits_32(bkpr_fwu_cnt, TAMP_BOOT_FWU_INFO_CNT_MSK,
+				   (try_cnt - 1U) << TAMP_BOOT_FWU_INFO_CNT_OFF);
+	}
+	clk_disable(RTCAPB);
+
+	return try_cnt;
+}
+
+void stm32_set_max_fwu_trial_boot_cnt(void)
+{
+	uintptr_t bkpr_fwu_cnt = tamp_bkpr(TAMP_BOOT_FWU_INFO_REG_ID);
+
+	clk_enable(RTCAPB);
+	mmio_clrsetbits_32(bkpr_fwu_cnt, TAMP_BOOT_FWU_INFO_CNT_MSK,
+			   (FWU_MAX_TRIAL_REBOOT << TAMP_BOOT_FWU_INFO_CNT_OFF) &
+			   TAMP_BOOT_FWU_INFO_CNT_MSK);
+	clk_disable(RTCAPB);
+}
 #endif /* !STM32MP_USE_STM32IMAGE && PSA_FWU_SUPPORT */