feat(intel): support query of fip offset using RSU

Query the fip binary from SPT table on RSU boot on Intel Agilex series.

Change-Id: I8856b49539f33272625d4c0a8c26b81b5864c4eb
Signed-off-by: Mahesh Rao <mahesh.rao@intel.com>
diff --git a/plat/intel/soc/common/include/socfpga_ros.h b/plat/intel/soc/common/include/socfpga_ros.h
new file mode 100644
index 0000000..10cabd3
--- /dev/null
+++ b/plat/intel/soc/common/include/socfpga_ros.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2024, Intel Corporation. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef SOCFPGA_ROS_H
+#define SOCFPGA_ROS_H
+
+#include <arch_helpers.h>
+#include <lib/utils_def.h>
+
+/** status response*/
+#define ROS_RET_OK			(0x00U)
+#define ROS_RET_INVALID			(0x01U)
+#define ROS_RET_NOT_RSU_MODE		(0x02U)
+#define ROS_QSPI_READ_ERROR		(0x03U)
+#define ROS_SPT_BAD_MAGIC_NUM		(0x04U)
+#define ROS_SPT_CRC_ERROR		(0x05U)
+#define ROS_IMAGE_INDEX_ERR		(0x06U)
+#define ROS_IMAGE_PARTNUM_OVFL		(0x07U)
+
+#define ADDR_64(h, l)			(((((unsigned long)(h)) & 0xffffffff) << 32) | \
+						(((unsigned long)(l)) & 0xffffffff))
+
+#define RSU_GET_SPT_RESP_SIZE		(4U)
+
+#define RSU_STATUS_RES_SIZE		(9U)
+
+#define SPT_MAGIC_NUMBER		(0x57713427U)
+#define SPT_VERSION			(0U)
+#define SPT_FLAG_RESERVED		(1U)
+#define SPT_FLAG_READONLY		(2U)
+
+#define SPT_MAX_PARTITIONS		(127U)
+#define SPT_PARTITION_NAME_LENGTH	(16U)
+#define SPT_RSVD_LENGTH			(4U)
+#define SPT_SIZE			(4096U)
+/*BOOT_INFO + FACTORY_IMAGE + SPT0 + SPT1 + CPB0 + CPB1 + FACTORY_IM.SSBL+ *APP* + *APP*.SSBL*/
+#define SPT_MIN_PARTITIONS		(9U)
+
+#define FACTORY_IMAGE			"FACTORY_IMAGE"
+#define FACTORY_SSBL			"FACTORY_IM.SSBL"
+#define SSBL_SUFFIX			".SSBL"
+
+typedef struct {
+	const uint32_t magic_number;
+	const uint32_t version;
+	const uint32_t partitions;
+	uint32_t checksum;
+	const uint32_t __RSVD[SPT_RSVD_LENGTH];
+	struct {
+		const char name[SPT_PARTITION_NAME_LENGTH];
+		const uint64_t offset;
+		const uint32_t length;
+		const uint32_t flags;
+	} partition[SPT_MAX_PARTITIONS];
+} __packed spt_table_t;
+
+uint32_t ros_qspi_get_ssbl_offset(unsigned long *offset);
+
+#endif /* SOCFPGA_ROS_H */
diff --git a/plat/intel/soc/common/socfpga_ros.c b/plat/intel/soc/common/socfpga_ros.c
new file mode 100644
index 0000000..ea37384
--- /dev/null
+++ b/plat/intel/soc/common/socfpga_ros.c
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 2024, Intel Corporation. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+/* system header files*/
+#include <assert.h>
+#include <endian.h>
+#include <string.h>
+
+/* CRC function header */
+#include <common/tf_crc32.h>
+
+/* Cadense qspi driver*/
+#include <qspi/cadence_qspi.h>
+
+/* Mailbox driver*/
+#include <socfpga_mailbox.h>
+
+#include <socfpga_ros.h>
+
+static void swap_bits(char *const data, uint32_t len)
+{
+	uint32_t x, y;
+	char tmp;
+
+	for (x = 0U; x < len; x++) {
+		tmp = 0U;
+		for (y = 0U; y < 8; y++) {
+			tmp <<= 1;
+			if (data[x] & 1) {
+				tmp |= 1;
+			}
+			data[x] >>= 1;
+		}
+		data[x] = tmp;
+	}
+}
+
+static uint32_t get_current_image_index(spt_table_t *spt_buf, uint32_t *const img_index)
+{
+	if (spt_buf == NULL || img_index == NULL) {
+		return ROS_RET_INVALID;
+	}
+
+	uint32_t ret;
+	unsigned long current_image;
+	uint32_t rsu_status[RSU_STATUS_RES_SIZE];
+
+	if (spt_buf->partitions < SPT_MIN_PARTITIONS || spt_buf->partitions > SPT_MAX_PARTITIONS) {
+		return ROS_IMAGE_PARTNUM_OVFL;
+	}
+
+	ret = mailbox_rsu_status(rsu_status, RSU_STATUS_RES_SIZE);
+	if (ret != MBOX_RET_OK) {
+		return ROS_RET_NOT_RSU_MODE;
+	}
+
+	current_image = ADDR_64(rsu_status[1], rsu_status[0]);
+	NOTICE("ROS: Current image is at 0x%08lx\n", current_image);
+
+	*img_index = 0U;
+	for (uint32_t index = 0U ; index < spt_buf->partitions; index++) {
+		if (spt_buf->partition[index].offset == current_image) {
+			*img_index = index;
+			break;
+		}
+	}
+
+	if (*img_index == 0U) {
+		return ROS_IMAGE_INDEX_ERR;
+	}
+
+	return ROS_RET_OK;
+}
+
+static uint32_t load_and_check_spt(spt_table_t *spt_ptr, size_t offset)
+{
+
+	if (spt_ptr == NULL || offset == 0U) {
+		return ROS_RET_INVALID;
+	}
+
+	int ret;
+	uint32_t calc_crc;
+	static spt_table_t spt_data;
+
+	ret = cad_qspi_read(spt_ptr, offset, SPT_SIZE);
+	if (ret != 0U) {
+		return ROS_QSPI_READ_ERROR;
+	}
+
+	if (spt_ptr->magic_number != SPT_MAGIC_NUMBER) {
+		return ROS_SPT_BAD_MAGIC_NUM;
+	}
+
+	if (spt_ptr->partitions < SPT_MIN_PARTITIONS || spt_ptr->partitions > SPT_MAX_PARTITIONS) {
+		return ROS_IMAGE_PARTNUM_OVFL;
+	}
+
+	memcpy_s(&spt_data, SPT_SIZE, spt_ptr, SPT_SIZE);
+	spt_data.checksum = 0U;
+	swap_bits((char *)&spt_data, SPT_SIZE);
+
+	calc_crc = tf_crc32(0, (uint8_t *)&spt_data, SPT_SIZE);
+	if (bswap32(spt_ptr->checksum) != calc_crc) {
+		return ROS_SPT_CRC_ERROR;
+	}
+
+	NOTICE("ROS: SPT table at 0x%08lx is verified\n", offset);
+	return ROS_RET_OK;
+}
+
+static uint32_t get_spt(spt_table_t *spt_buf)
+{
+	if (spt_buf == NULL) {
+		return ROS_RET_INVALID;
+	}
+
+	uint32_t ret;
+	uint32_t spt_offset[RSU_GET_SPT_RESP_SIZE];
+
+	/* Get SPT offset from SDM via mailbox commands */
+	ret = mailbox_rsu_get_spt_offset(spt_offset, RSU_GET_SPT_RESP_SIZE);
+	if (ret != MBOX_RET_OK) {
+		WARN("ROS: Not booted in RSU mode\n");
+		return ROS_RET_NOT_RSU_MODE;
+	}
+
+	/* Print the SPT table addresses */
+	VERBOSE("ROS: SPT0 0x%08lx\n", ADDR_64(spt_offset[0], spt_offset[1]));
+	VERBOSE("ROS: SPT1 0x%08lx\n", ADDR_64(spt_offset[2], spt_offset[3]));
+
+	/* Load and validate SPT1*/
+	ret = load_and_check_spt(spt_buf, ADDR_64(spt_offset[2], spt_offset[3]));
+	if (ret != ROS_RET_OK) {
+		/* Load and validate SPT0*/
+		ret = load_and_check_spt(spt_buf, ADDR_64(spt_offset[0], spt_offset[1]));
+		if (ret != ROS_RET_OK) {
+			WARN("Both SPT tables are unusable\n");
+			return ret;
+		}
+	}
+
+	return ROS_RET_OK;
+}
+
+uint32_t ros_qspi_get_ssbl_offset(unsigned long *offset)
+{
+	if (offset == NULL) {
+		return ROS_RET_INVALID;
+	}
+
+	uint32_t ret, img_index;
+	char ssbl_name[SPT_PARTITION_NAME_LENGTH];
+	static spt_table_t spt;
+
+	ret = get_spt(&spt);
+	if (ret != ROS_RET_OK) {
+		return ret;
+	}
+
+	ret = get_current_image_index(&spt, &img_index);
+	if (ret != ROS_RET_OK) {
+		return ret;
+	}
+
+	if (strncmp(spt.partition[img_index].name, FACTORY_IMAGE,
+		SPT_PARTITION_NAME_LENGTH) == 0U) {
+		strlcpy(ssbl_name, FACTORY_SSBL, SPT_PARTITION_NAME_LENGTH);
+	} else {
+		strlcpy(ssbl_name, spt.partition[img_index].name,
+			SPT_PARTITION_NAME_LENGTH);
+		strlcat(ssbl_name, SSBL_SUFFIX, SPT_PARTITION_NAME_LENGTH);
+	}
+
+	for (uint32_t index = 0U; index < spt.partitions; index++) {
+		if (strncmp(spt.partition[index].name, ssbl_name,
+			SPT_PARTITION_NAME_LENGTH) == 0U) {
+			*offset = spt.partition[index].offset;
+			NOTICE("ROS: Corresponding SSBL is at 0x%08lx\n", *offset);
+			return ROS_RET_OK;
+		}
+	}
+
+	return ROS_IMAGE_INDEX_ERR;
+}