feat(imx8m): obtain boot image set for imx8mn/mp

In i.MX8MM/MQ it is possible to have two copies of bootloader in
SD/eMMC and switch between them. The switch is triggered either
by the BootROM in case the bootloader image is faulty OR can be
enforced by the user, and there is API introduced in
9ce232fe ("feat(plat/imx8m): add SiP call for secondary boot"),
which leverages this SoC feature.

However neither i.MX8MP nor i.MX8MN have a dedicated bit
which indicates what boot image set is currently booted.
According to AN12853 [1] "i.MX ROMs Log Events", it is
possible to determine whether fallback event occurred
by parsing the BootROM event log. In case ROM event ID 0x51 is
present,fallback event did occur and secondary boot image was booted.

Knowing which boot image was booted might be useful for reliable
bootloader A/B updates, detecting fallback event might be used for
making decision if boot firmware rollback is required.

This patche introduces implementation, that replicates the same
imx_src_handler() behaviour as on i.MX8MM/MQ SoCs.

The code is based on original U-Boot implementation [2].

[1]: https://www.nxp.com/webapp/Download?colCode=AN12853
[2]: https://github.com/u-boot/u-boot/commit/a5ee05cf7180b411ffdf148ca8cb220c029f2e19

Change-Id: I9a4c5229aa0e53fa23b5261459da99cb3ce6bdbe
Signed-off-by: Igor Opaniuk <igor.opaniuk@foundries.io>
diff --git a/plat/imx/common/imx_sip_handler.c b/plat/imx/common/imx_sip_handler.c
index ec8631a..f830b64 100644
--- a/plat/imx/common/imx_sip_handler.c
+++ b/plat/imx/common/imx_sip_handler.c
@@ -17,6 +17,15 @@
 #include <lib/mmio.h>
 #include <sci/sci.h>
 
+#if defined(PLAT_imx8mn) || defined(PLAT_imx8mp)
+/*
+ * Defined in
+ * table 11. ROM event log buffer address location
+ * AN12853 "i.MX ROMs Log Events"
+ */
+#define ROM_LOG_BUFFER_ADDR	0x9E0
+#endif
+
 #if defined(PLAT_imx8qm) || defined(PLAT_imx8qx)
 
 #ifdef PLAT_imx8qm
@@ -177,6 +186,76 @@
 }
 #endif /* defined(PLAT_imx8mm) || defined(PLAT_imx8mq) */
 
+#if defined(PLAT_imx8mn) || defined(PLAT_imx8mp)
+static bool is_secondary_boot(void)
+{
+	uint32_t *rom_log_addr = (uint32_t *)ROM_LOG_BUFFER_ADDR;
+	bool is_secondary = false;
+	uint32_t *rom_log;
+	uint8_t event_id;
+
+	/* If the ROM event log pointer is not valid. */
+	if (*rom_log_addr < 0x900000 || *rom_log_addr >= 0xB00000 ||
+	    *rom_log_addr & 0x3) {
+		return false;
+	}
+
+	/* Parse the ROM event ID version 2 log */
+	rom_log = (uint32_t *)(uintptr_t)(*rom_log_addr);
+	for (size_t i = 0; i < 128; i++) {
+		event_id = rom_log[i] >> 24;
+		switch (event_id) {
+		case 0x00: /* End of list */
+			return is_secondary;
+		/* Log entries with 1 parameter, skip 1 */
+		case 0x80: /* Perform the device initialization */
+		case 0x81: /* The boot device initialization completes */
+		case 0x82: /* Execute boot device driver pre-config */
+		case 0x8F: /* The boot device initialization fails */
+		case 0x90: /* Start to read data from boot device */
+		case 0x91: /* Reading data from boot device completes */
+		case 0x9F: /* Reading data from boot device fails */
+			i += 1;
+			continue;
+		/* Log entries with 2 parameters, skip 2 */
+		case 0xA0: /* Image authentication result */
+		case 0xC0: /* Jump to the boot image soon */
+			i += 2;
+			continue;
+		/* Booted the primary boot image */
+		case 0x50:
+			is_secondary = false;
+			continue;
+		/* Booted the secondary boot image */
+		case 0x51:
+			is_secondary = true;
+			continue;
+		}
+	}
+
+	return is_secondary;
+}
+
+int imx_src_handler(uint32_t smc_fid,
+		    u_register_t x1,
+		    u_register_t x2,
+		    u_register_t x3,
+		    void *handle)
+{
+	switch (x1) {
+	case IMX_SIP_SRC_SET_SECONDARY_BOOT:
+		/* we do support that on these SoCs */
+		break;
+	case IMX_SIP_SRC_IS_SECONDARY_BOOT:
+		return is_secondary_boot();
+	default:
+		return SMC_UNK;
+	};
+
+	return 0;
+}
+#endif /* defined(PLAT_imx8mn) || defined(PLAT_imx8mp) */
+
 static uint64_t imx_get_commit_hash(u_register_t x2,
 		    u_register_t x3,
 		    u_register_t x4)
diff --git a/plat/imx/common/imx_sip_svc.c b/plat/imx/common/imx_sip_svc.c
index 6d6633c..69d4f05 100644
--- a/plat/imx/common/imx_sip_svc.c
+++ b/plat/imx/common/imx_sip_svc.c
@@ -60,7 +60,8 @@
 	case IMX_SIP_MISC_SET_TEMP:
 		SMC_RET1(handle, imx_misc_set_temp_handler(smc_fid, x1, x2, x3, x4));
 #endif
-#if defined(PLAT_imx8mm) || defined(PLAT_imx8mq)
+#if defined(PLAT_imx8mm) || defined(PLAT_imx8mq) || defined(PLAT_imx8mn) || \
+	defined(PLAT_imx8mp)
 	case IMX_SIP_SRC:
 		SMC_RET1(handle, imx_src_handler(smc_fid, x1, x2, x3, handle));
 		break;
diff --git a/plat/imx/common/include/imx_sip_svc.h b/plat/imx/common/include/imx_sip_svc.h
index 0e91c71..35a9f47 100644
--- a/plat/imx/common/include/imx_sip_svc.h
+++ b/plat/imx/common/include/imx_sip_svc.h
@@ -68,7 +68,9 @@
 		    u_register_t x2, u_register_t x3);
 #endif
 
-#if defined(PLAT_imx8mm) || defined(PLAT_imx8mq)
+#if defined(PLAT_imx8mm) || defined(PLAT_imx8mq) || defined(PLAT_imx8mn) || \
+	defined(PLAT_imx8mp)
+
 int imx_src_handler(uint32_t smc_fid, u_register_t x1,
 		    u_register_t x2, u_register_t x3, void *handle);
 #endif