162be2a1aSMahesh Rao /*
262be2a1aSMahesh Rao * Copyright (c) 2024, Intel Corporation. All rights reserved.
3*bf2c2136SMahesh Rao * Copyright (c) 2025, Altera Corporation. All rights reserved.
462be2a1aSMahesh Rao *
562be2a1aSMahesh Rao * SPDX-License-Identifier: BSD-3-Clause
662be2a1aSMahesh Rao */
762be2a1aSMahesh Rao
862be2a1aSMahesh Rao /* system header files*/
962be2a1aSMahesh Rao #include <assert.h>
1062be2a1aSMahesh Rao #include <endian.h>
1162be2a1aSMahesh Rao #include <string.h>
1262be2a1aSMahesh Rao
1362be2a1aSMahesh Rao /* CRC function header */
1462be2a1aSMahesh Rao #include <common/tf_crc32.h>
1562be2a1aSMahesh Rao
1662be2a1aSMahesh Rao /* Cadense qspi driver*/
1762be2a1aSMahesh Rao #include <qspi/cadence_qspi.h>
1862be2a1aSMahesh Rao
1962be2a1aSMahesh Rao /* Mailbox driver*/
2062be2a1aSMahesh Rao #include <socfpga_mailbox.h>
2162be2a1aSMahesh Rao
2262be2a1aSMahesh Rao #include <socfpga_ros.h>
2362be2a1aSMahesh Rao
24*bf2c2136SMahesh Rao #define WORD_SIZE (sizeof(uint32_t))
25*bf2c2136SMahesh Rao
swap_bits(char * const data,uint32_t len)2662be2a1aSMahesh Rao static void swap_bits(char *const data, uint32_t len)
2762be2a1aSMahesh Rao {
2862be2a1aSMahesh Rao uint32_t x, y;
2962be2a1aSMahesh Rao char tmp;
3062be2a1aSMahesh Rao
3162be2a1aSMahesh Rao for (x = 0U; x < len; x++) {
3262be2a1aSMahesh Rao tmp = 0U;
3362be2a1aSMahesh Rao for (y = 0U; y < 8; y++) {
3462be2a1aSMahesh Rao tmp <<= 1;
3562be2a1aSMahesh Rao if (data[x] & 1) {
3662be2a1aSMahesh Rao tmp |= 1;
3762be2a1aSMahesh Rao }
3862be2a1aSMahesh Rao data[x] >>= 1;
3962be2a1aSMahesh Rao }
4062be2a1aSMahesh Rao data[x] = tmp;
4162be2a1aSMahesh Rao }
4262be2a1aSMahesh Rao }
4362be2a1aSMahesh Rao
get_current_image_index(spt_table_t * spt_buf,uint32_t * const img_index)4462be2a1aSMahesh Rao static uint32_t get_current_image_index(spt_table_t *spt_buf, uint32_t *const img_index)
4562be2a1aSMahesh Rao {
4662be2a1aSMahesh Rao if (spt_buf == NULL || img_index == NULL) {
4762be2a1aSMahesh Rao return ROS_RET_INVALID;
4862be2a1aSMahesh Rao }
4962be2a1aSMahesh Rao
5062be2a1aSMahesh Rao uint32_t ret;
5162be2a1aSMahesh Rao unsigned long current_image;
5262be2a1aSMahesh Rao uint32_t rsu_status[RSU_STATUS_RES_SIZE];
5362be2a1aSMahesh Rao
5462be2a1aSMahesh Rao if (spt_buf->partitions < SPT_MIN_PARTITIONS || spt_buf->partitions > SPT_MAX_PARTITIONS) {
5562be2a1aSMahesh Rao return ROS_IMAGE_PARTNUM_OVFL;
5662be2a1aSMahesh Rao }
5762be2a1aSMahesh Rao
5862be2a1aSMahesh Rao ret = mailbox_rsu_status(rsu_status, RSU_STATUS_RES_SIZE);
5962be2a1aSMahesh Rao if (ret != MBOX_RET_OK) {
6062be2a1aSMahesh Rao return ROS_RET_NOT_RSU_MODE;
6162be2a1aSMahesh Rao }
6262be2a1aSMahesh Rao
6362be2a1aSMahesh Rao current_image = ADDR_64(rsu_status[1], rsu_status[0]);
6462be2a1aSMahesh Rao NOTICE("ROS: Current image is at 0x%08lx\n", current_image);
6562be2a1aSMahesh Rao
6662be2a1aSMahesh Rao *img_index = 0U;
6762be2a1aSMahesh Rao for (uint32_t index = 0U ; index < spt_buf->partitions; index++) {
6862be2a1aSMahesh Rao if (spt_buf->partition[index].offset == current_image) {
6962be2a1aSMahesh Rao *img_index = index;
7062be2a1aSMahesh Rao break;
7162be2a1aSMahesh Rao }
7262be2a1aSMahesh Rao }
7362be2a1aSMahesh Rao
7462be2a1aSMahesh Rao if (*img_index == 0U) {
7562be2a1aSMahesh Rao return ROS_IMAGE_INDEX_ERR;
7662be2a1aSMahesh Rao }
7762be2a1aSMahesh Rao
7862be2a1aSMahesh Rao return ROS_RET_OK;
7962be2a1aSMahesh Rao }
8062be2a1aSMahesh Rao
load_and_check_spt(spt_table_t * spt_ptr,size_t offset)8162be2a1aSMahesh Rao static uint32_t load_and_check_spt(spt_table_t *spt_ptr, size_t offset)
8262be2a1aSMahesh Rao {
8362be2a1aSMahesh Rao
8462be2a1aSMahesh Rao if (spt_ptr == NULL || offset == 0U) {
8562be2a1aSMahesh Rao return ROS_RET_INVALID;
8662be2a1aSMahesh Rao }
8762be2a1aSMahesh Rao
8862be2a1aSMahesh Rao int ret;
8962be2a1aSMahesh Rao uint32_t calc_crc;
9062be2a1aSMahesh Rao static spt_table_t spt_data;
9162be2a1aSMahesh Rao
9262be2a1aSMahesh Rao ret = cad_qspi_read(spt_ptr, offset, SPT_SIZE);
9362be2a1aSMahesh Rao if (ret != 0U) {
9462be2a1aSMahesh Rao return ROS_QSPI_READ_ERROR;
9562be2a1aSMahesh Rao }
9662be2a1aSMahesh Rao
9762be2a1aSMahesh Rao if (spt_ptr->magic_number != SPT_MAGIC_NUMBER) {
9862be2a1aSMahesh Rao return ROS_SPT_BAD_MAGIC_NUM;
9962be2a1aSMahesh Rao }
10062be2a1aSMahesh Rao
10162be2a1aSMahesh Rao if (spt_ptr->partitions < SPT_MIN_PARTITIONS || spt_ptr->partitions > SPT_MAX_PARTITIONS) {
10262be2a1aSMahesh Rao return ROS_IMAGE_PARTNUM_OVFL;
10362be2a1aSMahesh Rao }
10462be2a1aSMahesh Rao
105*bf2c2136SMahesh Rao memcpy_s(&spt_data, (sizeof(spt_table_t) / WORD_SIZE),
106*bf2c2136SMahesh Rao spt_ptr, (SPT_SIZE / WORD_SIZE));
10762be2a1aSMahesh Rao spt_data.checksum = 0U;
10862be2a1aSMahesh Rao swap_bits((char *)&spt_data, SPT_SIZE);
10962be2a1aSMahesh Rao
11062be2a1aSMahesh Rao calc_crc = tf_crc32(0, (uint8_t *)&spt_data, SPT_SIZE);
11162be2a1aSMahesh Rao if (bswap32(spt_ptr->checksum) != calc_crc) {
11262be2a1aSMahesh Rao return ROS_SPT_CRC_ERROR;
11362be2a1aSMahesh Rao }
11462be2a1aSMahesh Rao
11562be2a1aSMahesh Rao NOTICE("ROS: SPT table at 0x%08lx is verified\n", offset);
11662be2a1aSMahesh Rao return ROS_RET_OK;
11762be2a1aSMahesh Rao }
11862be2a1aSMahesh Rao
get_spt(spt_table_t * spt_buf)11962be2a1aSMahesh Rao static uint32_t get_spt(spt_table_t *spt_buf)
12062be2a1aSMahesh Rao {
12162be2a1aSMahesh Rao if (spt_buf == NULL) {
12262be2a1aSMahesh Rao return ROS_RET_INVALID;
12362be2a1aSMahesh Rao }
12462be2a1aSMahesh Rao
12562be2a1aSMahesh Rao uint32_t ret;
12662be2a1aSMahesh Rao uint32_t spt_offset[RSU_GET_SPT_RESP_SIZE];
12762be2a1aSMahesh Rao
12862be2a1aSMahesh Rao /* Get SPT offset from SDM via mailbox commands */
12962be2a1aSMahesh Rao ret = mailbox_rsu_get_spt_offset(spt_offset, RSU_GET_SPT_RESP_SIZE);
13062be2a1aSMahesh Rao if (ret != MBOX_RET_OK) {
13162be2a1aSMahesh Rao WARN("ROS: Not booted in RSU mode\n");
13262be2a1aSMahesh Rao return ROS_RET_NOT_RSU_MODE;
13362be2a1aSMahesh Rao }
13462be2a1aSMahesh Rao
13562be2a1aSMahesh Rao /* Print the SPT table addresses */
13662be2a1aSMahesh Rao VERBOSE("ROS: SPT0 0x%08lx\n", ADDR_64(spt_offset[0], spt_offset[1]));
13762be2a1aSMahesh Rao VERBOSE("ROS: SPT1 0x%08lx\n", ADDR_64(spt_offset[2], spt_offset[3]));
13862be2a1aSMahesh Rao
13962be2a1aSMahesh Rao /* Load and validate SPT1*/
14062be2a1aSMahesh Rao ret = load_and_check_spt(spt_buf, ADDR_64(spt_offset[2], spt_offset[3]));
14162be2a1aSMahesh Rao if (ret != ROS_RET_OK) {
14262be2a1aSMahesh Rao /* Load and validate SPT0*/
14362be2a1aSMahesh Rao ret = load_and_check_spt(spt_buf, ADDR_64(spt_offset[0], spt_offset[1]));
14462be2a1aSMahesh Rao if (ret != ROS_RET_OK) {
14562be2a1aSMahesh Rao WARN("Both SPT tables are unusable\n");
14662be2a1aSMahesh Rao return ret;
14762be2a1aSMahesh Rao }
14862be2a1aSMahesh Rao }
14962be2a1aSMahesh Rao
15062be2a1aSMahesh Rao return ROS_RET_OK;
15162be2a1aSMahesh Rao }
15262be2a1aSMahesh Rao
ros_qspi_get_ssbl_offset(unsigned long * offset)15362be2a1aSMahesh Rao uint32_t ros_qspi_get_ssbl_offset(unsigned long *offset)
15462be2a1aSMahesh Rao {
15562be2a1aSMahesh Rao if (offset == NULL) {
15662be2a1aSMahesh Rao return ROS_RET_INVALID;
15762be2a1aSMahesh Rao }
15862be2a1aSMahesh Rao
15962be2a1aSMahesh Rao uint32_t ret, img_index;
160*bf2c2136SMahesh Rao size_t len;
16162be2a1aSMahesh Rao char ssbl_name[SPT_PARTITION_NAME_LENGTH];
16262be2a1aSMahesh Rao static spt_table_t spt;
16362be2a1aSMahesh Rao
16462be2a1aSMahesh Rao ret = get_spt(&spt);
16562be2a1aSMahesh Rao if (ret != ROS_RET_OK) {
16662be2a1aSMahesh Rao return ret;
16762be2a1aSMahesh Rao }
16862be2a1aSMahesh Rao
16962be2a1aSMahesh Rao ret = get_current_image_index(&spt, &img_index);
17062be2a1aSMahesh Rao if (ret != ROS_RET_OK) {
17162be2a1aSMahesh Rao return ret;
17262be2a1aSMahesh Rao }
17362be2a1aSMahesh Rao
17462be2a1aSMahesh Rao if (strncmp(spt.partition[img_index].name, FACTORY_IMAGE,
17562be2a1aSMahesh Rao SPT_PARTITION_NAME_LENGTH) == 0U) {
176*bf2c2136SMahesh Rao strcpy_secure(ssbl_name, SPT_PARTITION_NAME_LENGTH, FACTORY_SSBL);
17762be2a1aSMahesh Rao } else {
178*bf2c2136SMahesh Rao strcpy_secure(ssbl_name, SPT_PARTITION_NAME_LENGTH, SSBL_PREFIX);
179*bf2c2136SMahesh Rao len = strnlen_secure(ssbl_name, SPT_PARTITION_NAME_LENGTH);
180*bf2c2136SMahesh Rao strcpy_secure(ssbl_name + len, SPT_PARTITION_NAME_LENGTH - len,
181*bf2c2136SMahesh Rao spt.partition[img_index].name);
18262be2a1aSMahesh Rao }
18362be2a1aSMahesh Rao
18462be2a1aSMahesh Rao for (uint32_t index = 0U; index < spt.partitions; index++) {
18562be2a1aSMahesh Rao if (strncmp(spt.partition[index].name, ssbl_name,
18662be2a1aSMahesh Rao SPT_PARTITION_NAME_LENGTH) == 0U) {
18762be2a1aSMahesh Rao *offset = spt.partition[index].offset;
18862be2a1aSMahesh Rao NOTICE("ROS: Corresponding SSBL is at 0x%08lx\n", *offset);
18962be2a1aSMahesh Rao return ROS_RET_OK;
19062be2a1aSMahesh Rao }
19162be2a1aSMahesh Rao }
19262be2a1aSMahesh Rao
19362be2a1aSMahesh Rao return ROS_IMAGE_INDEX_ERR;
19462be2a1aSMahesh Rao }
195