/*******************************************************************************
* DISCLAIMER
* This software is supplied by Renesas Electronics Corporation and is only
* intended for use with Renesas products. No other uses are authorized. This
* software is owned by Renesas Electronics Corporation and is protected under
* all applicable laws, including copyright laws.
* THIS SOFTWARE IS PROVIDED "AS IS" AND RENESAS MAKES NO WARRANTIES REGARDING
* THIS SOFTWARE, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING BUT NOT
* LIMITED TO WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
* AND NON-INFRINGEMENT. ALL SUCH WARRANTIES ARE EXPRESSLY DISCLAIMED.
* TO THE MAXIMUM EXTENT PERMITTED NOT PROHIBITED BY LAW, NEITHER RENESAS
* ELECTRONICS CORPORATION NOR ANY OF ITS AFFILIATED COMPANIES SHALL BE LIABLE
* FOR ANY DIRECT, INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES FOR
* ANY REASON RELATED TO THIS SOFTWARE, EVEN IF RENESAS OR ITS AFFILIATES HAVE
* BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
* Renesas reserves the right, without notice, to make changes to this software
* and to discontinue the availability of this software. By using this software,
* you agree to the additional terms and conditions found by accessing the
* following link:
* http://www.renesas.com/disclaimer
* Copyright 2018-2025 Renesas Electronics Corporation All rights reserved.
*******************************************************************************/



/*******************************************************************************
 * DESCRIPTION   : Image load function
 ******************************************************************************/
/******************************************************************************
 * @file          image_load.c
 * - Version      : 0.11
 * @brief         Access protection setting driver.
 * .
 *****************************************************************************/
/******************************************************************************
 * History : DD.MM.YYYY Version  Description
 *         : 29.11.2021 0.01     First Release
 *         : 10.02.2022 0.02     Updated eMMC driver
 *                               Removed the comment
 *                               Change the line feed code of log output
 *                               Change the number of CA programs
 *         : 17.02.2022 0.03     Support AArch32
 *         : 22.03.2022 0.04     Support for GSCE[3.3b]
 *         : 11.05.2022 0.05     Used the standard library
 *                               Integrated LOAD_INFO
 *                               Changed image_name table
 *                               Changed check_load_area
 *                               - Change RAM check process
 *                               - Change address check process
 *                               - Add Image check process
 *                               - Processing integration
 *                               Changed to processing for each device
 *                               Change structure member name
 *                               Added function return value judgment
 *                               Changed LOGICAL_CONTENT_CERT_ADDR to
 *                                 get_logic_cont_cert_addr
 *                               Changed uint32_t to uintptr_t
 *                               Change log output
 *                               Add argument of load_init()
 *                               Change to error when key information is invalid
 *                               Remove unnecessary type conversion
 *         : 11.07.2022 0.06     Change log output
 *                               Support secure boot for S4
 *                               Change load start processing other than 512byte align
 *         : 02.09.2022 0.07     Added 512byte boundary check
 *         : 31.10.2022 0.08     License notation change.
 *         : 21.08.2023 0.09     Add support for V4M.
 *         : 19.12.2024 0.10     Add support for RTOS#1 and RTOS#2.
 *         : 26.05.2025 0.11     Change key cert address of [CA_OPTIONAL_ID+2].
*****************************************************************************/
/* indelude */
#include <stdint.h>
#include <image_load.h>
#include <mem_io.h>
#include <log.h>

#include <image_load_emmc.h>
#include <emmc_multiboot.h>

#define KEY_SIZE_FLG_MSK            (0x00000003U)
#define KEY_SIZE_BIT_SHIFT          (21U)
#define CERT_INFO_FLG_OFFSET        (0x0000000CU)
#define KEY_SIZE_4096               (0x00000002U)
#define KEY_SIZE_3072               (0x00000001U)
#define KEY_SIZE_2048               (0x00000000U)
#define WORD_TO_BYTE                (4U)
#define NOT_OVERLAP_FLAG            (0U)
#define OVERLAP_FLAG                (1U)
#define RAM_RANGE_OK                (0U)
#define RAM_RANGE_NG                (1U)
#if (RTOS_LOAD_NUM == RTOS_LOAD_NUM_1)
#define RAM_MAX                     (4U)
#elif (RTOS_LOAD_NUM == RTOS_LOAD_NUM_3)
#define RAM_MAX                     (5U)    /* ++ SRAM in RT-VRAM (0xE2000000 - 0xE200FFFF) */
#endif /* RTOS_LOAD_NUM == RTOS_LOAD_NUM_3 */
#define ADDRESS_RANGE_512           (512U)

static void check_load_area(LOAD_INFO* li);
static void get_info_from_cert(uint32_t cert_addr, uint32_t *size,
    uint32_t *dest_addr);

void load_image(LOAD_INFO* li)
{
    /* log output of load image for information */
    load_image_info_print_for_emmc(li);

	/* Check transfer range of image. */
    check_load_area(li);

    /* Image load start. */
    load_start(li);
}
/* End of function load_image(LOAD_INFO* li) */

void load_init(LOAD_INFO* li, uint32_t num)
{
    uint32_t  loop;
    uintptr_t buf;
    
    const char *image_name[MAX_PLACED]  = {
#if ((RCAR_LSI == RCAR_V4H) || (RCAR_LSI == RCAR_V4M))
        [RTOS_ID]           = "RTOS",
#endif /* RCAR_LSI == RCAR_V4H || RCAR_LSI == RCAR_V4M */
#ifdef MOBIS_PRK3
        [CA_OPTIONAL_ID]     = "BL31",
        [CA_OPTIONAL_ID + 1] = "u-boot",
        [CA_OPTIONAL_ID + 2] = "tee OS",
        [CA_OPTIONAL_ID + 3] = "BL2",
        [CA_OPTIONAL_ID + 4] = "QNX OS",
#else
        [CA_OPTIONAL_ID]     = "CA Program #1",
        [CA_OPTIONAL_ID + 1] = "CA Program #2",
        [CA_OPTIONAL_ID + 2] = "CA Program #3",
        [CA_OPTIONAL_ID + 3] = "CA Program #4",
        [CA_OPTIONAL_ID + 4] = "CA Program #5",
#endif
        [CA_OPTIONAL_ID + 5] = "CA Program #6",
        [CA_OPTIONAL_ID + 6] = "CA Program #7",
        [CA_OPTIONAL_ID + 7] = "CA Program #8",
#if (RTOS_LOAD_NUM == RTOS_LOAD_NUM_3)
        [RTOS1_ID]           = "RTOS#1",
        [RTOS2_ID]           = "RTOS#2",
#endif /* RTOS_LOAD_NUM == RTOS_LOAD_NUM_3 */
    };

    const uint32_t key_cert[MAX_PLACED]  = {
#if ((RCAR_LSI == RCAR_V4H) || (RCAR_LSI == RCAR_V4M))
        [RTOS_ID]                  = TFMV_KEY_CERT_ADDR,
#endif /* RCAR_LSI == RCAR_V4H || RCAR_LSI == RCAR_V4M */
        [CA_OPTIONAL_ID]           = TFMV_KEY_CERT_ADDR,    /* bl31 */
#if (RCAR_LSI == RCAR_S4)
        [CA_OPTIONAL_ID + 1]       = TFMV_KEY_CERT_ADDR,
#elif ((RCAR_LSI == RCAR_V4H) || (RCAR_LSI == RCAR_V4M))
        [CA_OPTIONAL_ID + 1]       = NTFMV_KEY_CERT_ADDR,   /* u-boot */
#endif
#if (OPTEE_LOAD_ENABLE == OPTEE_DISABLE)
        [CA_OPTIONAL_ID + 2]       = NTFMV_KEY_CERT_ADDR,
#else
        [CA_OPTIONAL_ID + 2]       = TFMV_KEY_CERT_ADDR,    /* tee OS */
#endif /* OPTEE_LOAD_ENABLE == OPTEE_DISABLE */
#if (BL2_LOAD_ENABLE == BL2_DISABLE)
        [CA_OPTIONAL_ID + 3]       = NTFMV_KEY_CERT_ADDR,
#else
        [CA_OPTIONAL_ID + 3]       = TFMV_KEY_CERT_ADDR,    /* ca76-loader */
#endif /* BL2_LOAD_ENABLE == BL2_DISABLE */
        [CA_OPTIONAL_ID + 4]       = NTFMV_KEY_CERT_ADDR,   /* qnx OS */
        [CA_OPTIONAL_ID + 5]       = NTFMV_KEY_CERT_ADDR,
        [CA_OPTIONAL_ID + 6]       = NTFMV_KEY_CERT_ADDR,
        [CA_OPTIONAL_ID + 7]       = NTFMV_KEY_CERT_ADDR,
#if (RTOS_LOAD_NUM == RTOS_LOAD_NUM_3)
        [RTOS1_ID]                 = TFMV_KEY_CERT_ADDR,
        [RTOS2_ID]                 = TFMV_KEY_CERT_ADDR,
#endif /* RTOS_LOAD_NUM == RTOS_LOAD_NUM_3 */
    };


    if (num >= 1U && num <= 8U)
    {
        /* Set Load info parameter */
        for (loop = CA_OPTIONAL_ID; loop < CA_OPTIONAL_ID + num; loop++)
        {
            li[loop].name          = image_name[loop];
            li[loop].key_cert_addr = key_cert[loop];
            li[loop].cnt_cert_addr = get_logic_cont_cert_addr(loop);
            get_info_from_cert(li[loop].cnt_cert_addr, &li[loop].image_size, &li[loop].boot_addr);
            buf                    = get_src_addr_offset_in_cert(loop);
            li[loop].src_addr      = (SRC_TOP + mem_read32(buf));
            buf                    = get_part_num_in_cert(loop);
            li[loop].part_num      = mem_read32(buf);
            li[loop].load_id       = loop;
        }
    }
    else
    {
        ERROR("load_init(CA program num error).\n");
        panic;
    }

#if ((RCAR_LSI == RCAR_V4H) || (RCAR_LSI == RCAR_V4M))
    li[RTOS_ID].name       = image_name[RTOS_ID];
    li[RTOS_ID].key_cert_addr  = key_cert[RTOS_ID];
    li[RTOS_ID].cnt_cert_addr  = get_logic_cont_cert_addr(RTOS_ID);
    get_info_from_cert(li[RTOS_ID].cnt_cert_addr, &li[RTOS_ID].image_size, &li[RTOS_ID].boot_addr);
    buf                    = get_src_addr_offset_in_cert(RTOS_ID);
    li[RTOS_ID].src_addr   = (SRC_TOP + mem_read32(buf));
    buf                    = get_part_num_in_cert(RTOS_ID);
    li[RTOS_ID].part_num   = mem_read32(buf);
    li[RTOS_ID].load_id       = RTOS_ID;

 #if (RTOS_LOAD_NUM == RTOS_LOAD_NUM_3)
    /* Set Load info parameter for RTOS#1 and RTOS#2 */
    for (loop = RTOS1_ID; loop <= RTOS2_ID; loop++)
    {
        li[loop].name          = image_name[loop];
        li[loop].key_cert_addr = key_cert[loop];
        li[loop].cnt_cert_addr = get_logic_cont_cert_addr(loop);
        get_info_from_cert(li[loop].cnt_cert_addr, &li[loop].image_size, &li[loop].boot_addr);
        buf                    = get_src_addr_offset_in_cert(loop);
        li[loop].src_addr      = (SRC_TOP + mem_read32(buf));
        buf                    = get_part_num_in_cert(loop);
        li[loop].part_num      = mem_read32(buf);
        li[loop].load_id       = loop;
    }
 #endif /* RTOS_LOAD_NUM == RTOS_LOAD_NUM_3 */
#endif /* RCAR_LSI == RCAR_V4H || RCAR_LSI == RCAR_V4M */

}/* End of function load_init(LOAD_INFO* li) */

void load_update_part_num(LOAD_INFO* li, uint32_t num, int slot)
{
    uint32_t  loop;

    /* Set Load info parameter */
    for (loop = CA_OPTIONAL_ID; loop < CA_OPTIONAL_ID + num; loop++)
    {
        li[loop].part_num      = (slot == 1) ? 2 : 1;
    }
    li[RTOS_ID].part_num   = (slot == 1) ? 2 : 1;
}

static void check_load_area(LOAD_INFO* li)
{
    uint32_t src;
    uint32_t dst;
    uint32_t len;
    uint32_t dst_end;
    uint32_t overlap;
    uint32_t loop;
    uint32_t rge_chk_flg;
    static uint32_t s_num = 1U;

    /* The memory range of destination. */
    const ADDRESS_RANGE add_list[RAM_MAX] = {
        [TARGET_MEM_DRAM]   = {DRAM_BASE, DRAM_END},
        [TARGET_MEM_RTSRAM] = {RTSRAM_BASE, RTSRAM_END},
        [TARGET_MEM_RTVRAM] = {RTVRAM_VBUF_TOP, RTVRAM_VBUF_END},
        [TARGET_MEM_SYSRAM] = {SYSRAM_BASE, SYSRAM_END},
#if (RTOS_LOAD_NUM == RTOS_LOAD_NUM_3)
        [TARGET_MEM_SRAM_IN_RTVRAM] = {RTVRAM_SRAM_TOP, RTVRAM_SRAM_END},
#endif /* RTOS_LOAD_NUM == RTOS_LOAD_NUM_3 */
    };

	    /* The image range check */
    const IMAGE_RANGE size_list[CA_IMAGESIZECHK_DEF] = {
        { CA_PROGRAM1_ID, CA_PROGRAM1_ADR, CA_PROGRAM1_SIZE},
        { CA_PROGRAM2_ID, CA_PROGRAM2_ADR, CA_PROGRAM2_SIZE},
#if (OPTEE_LOAD_ENABLE == OPTEE_ENABLE)
        { CA_PROGRAM3_ID, CA_PROGRAM3_ADR, CA_PROGRAM3_SIZE},
#endif /* OPTEE_LOAD_ENABLE == OPTEE_ENABLE */
#if (BL2_LOAD_ENABLE == BL2_ENABLE)
        { CA_PROGRAM4_ID, CA_PROGRAM4_ADR, CA_PROGRAM4_SIZE},
#endif  /* BL2_LOAD_ENABLE == BL2_ENABLE */
#if (QNX_OS_LOAD_ENABLE == QNX_OS_ENABLE)
        { CA_PROGRAM5_ID, CA_PROGRAM5_ADR, CA_PROGRAM5_SIZE},
#endif  /* QNX_OS_LOAD_ENABLE == QNX_OS_ENABLE */
    };

    static ADDRESS_RANGE s_placed_image[MAX_PLACED] = {
        [0] = {IPL_TOP, IPL_END}
    };

    
    src = li->src_addr;
    dst = li->boot_addr;
    len = li->image_size;
    
    /* Check image size */
    if (len == 0U)
    {
        ERROR("image size error\n");
        panic;
    }

    /* Check whether source is overflow */
    /* INT30-C Pre confirmation */
    if (src > (UINT32_MAX - len))
    {
        ERROR("1:overflow is occurred at source\n");
        ERROR("1:source address = 0x%x image size = 0x%x\n", (unsigned int)src, (unsigned int)len);
        panic;
    }

    /* Check whether destination is overflow */
    /* INT30-C Pre confirmation */
    if (dst > (UINT32_MAX - len))
    {
        ERROR("1:overflow is occurred at destination\n");
        ERROR("1:destination address = 0x%x image size = 0x%x\n", (unsigned int)dst,
            (unsigned int)len);
        panic;
    }
    else
    {
        dst_end = dst + len - 1U;
    }

    /* Check source address range. */
    /* No error check is performed because it is detected by the eMMC device */

    /* Check destination address range. */

    /* 512byte boundary check */
    if (0U != (dst % ADDRESS_RANGE_512))
    {
        ERROR("check_load_area (destination address)\n");
        ERROR("destination address = 0x%x \n", (unsigned int)dst);
        panic;
    }

    /* range check */
    rge_chk_flg = RAM_RANGE_NG;

    for(loop = 0U; loop < RAM_MAX; loop++)
    {
        if (add_list[loop].cx_topadd <= dst)
        {
            if(dst_end <= add_list[loop].cx_endadd)
            {
                rge_chk_flg = RAM_RANGE_OK;
                break;
            }
        }
    }

    if(rge_chk_flg != RAM_RANGE_OK)
    {
        ERROR("check_load_area (destination address)\n");
        ERROR("destination address = 0x%x image size = 0x%x\n", (unsigned int)dst,
        (unsigned int)len);
        panic;
    }

    /* Check whether destination is overflow */
    for (loop = 0U; loop < CA_IMAGESIZECHK_DEF; loop++)
    {
        if (li->load_id == size_list[loop].load_id)
        {
            if ((size_list[loop].image_adr != 0U) && (dst != size_list[loop].image_adr))
            {
                ERROR("check load area.(outside secure area)\n");
                ERROR("destination address = 0x%x(0x%x) image size = 0x%x\n", (unsigned int)dst,
                (unsigned int)(size_list[loop].image_adr), (unsigned int)len);
                panic;
            }
            if ((size_list[loop].image_size != 0U) && (len > size_list[loop].image_size))
            {
                ERROR("check load area.(outside secure area)\n");
                ERROR("destination address = 0x%x image size = 0x%x(0x%x)\n", (unsigned int)dst,
                (unsigned int)len, (unsigned int)(size_list[loop].image_size));
                panic;
            }
       }
    }

    /* Check there are no overlaps the image that will be loaded and
       the images that have already loaded.             */
    overlap = NOT_OVERLAP_FLAG;
    loop = 0U;
    do
    {
        /* check overlap */
        if ((dst >= s_placed_image[loop].cx_topadd)
            && (dst <= s_placed_image[loop].cx_endadd))
        {
            overlap = OVERLAP_FLAG;
        }
        else if ((dst_end >= s_placed_image[loop].cx_topadd)
            && (dst_end <= s_placed_image[loop].cx_endadd))
        {
            overlap = OVERLAP_FLAG;
        }
        else if ((dst < s_placed_image[loop].cx_topadd)
            && (s_placed_image[loop].cx_endadd < dst_end))
        {
            overlap = OVERLAP_FLAG;
        }
        else
        {
            loop++;
        }
    } while ((loop < s_num) && (overlap == NOT_OVERLAP_FLAG));

    /* Check the overlap flag.
       Parameter error if overwrite occurred.
       Otherwise, add parameters of the image to be loaded into
       Placed_image.                    */
    if (overlap == NOT_OVERLAP_FLAG)
    {
        s_placed_image[s_num].cx_topadd = dst;
        s_placed_image[s_num].cx_endadd = dst_end;
        s_num++;
    }
    else
    {
        ERROR("check load area (overlap)\n");
        ERROR("destination address = 0x%x image size = 0x%x\n", (unsigned int)dst, (unsigned int)len);
        ERROR("overlapped image is [%u]\n", (unsigned int)loop);
        ERROR("top address = 0x%x end address = 0x%x\n",
            (unsigned int)s_placed_image[loop].cx_topadd, (unsigned int)s_placed_image[loop].cx_endadd);
        panic;
    }
}
/* End of function check_load_area(uint32_t dst, uint32_t src, uint32_t len) */

static void get_info_from_cert(uint32_t cert_addr, uint32_t *size,
    uint32_t *dest_addr)
{
    uint32_t    val;
    uint32_t    certInfo1;
    uintptr_t   pSize;
    uintptr_t   pDestL;

    /* Get key length of content certificate. */
    val = mem_read32((uintptr_t)cert_addr + CERT_INFO_FLG_OFFSET);
    certInfo1 = (val >> KEY_SIZE_BIT_SHIFT) & KEY_SIZE_FLG_MSK;

    /* Get the transfer address and transfer size from
       the certificate in accordance with the key length.   */
    if (KEY_SIZE_4096 == certInfo1)  /* key size = 4096 */
    {
        pSize = cert_addr + CERT_INFO_SIZE_OFFSET2;
        *size = mem_read32(pSize) * WORD_TO_BYTE;
        pDestL = cert_addr + CERT_INFO_DST_OFFSET2;
        *dest_addr = mem_read32(pDestL);
    }
    else if (KEY_SIZE_3072 == certInfo1)    /* key size = 3072 */
    {
        pSize = cert_addr + CERT_INFO_SIZE_OFFSET1;
        *size = mem_read32(pSize) * WORD_TO_BYTE;
        pDestL = cert_addr + CERT_INFO_DST_OFFSET1;
        *dest_addr = mem_read32(pDestL);
    }
    else if (KEY_SIZE_2048 == certInfo1)    /* key size = 2048 */
    {
        pSize = cert_addr + CERT_INFO_SIZE_OFFSET;
        *size = mem_read32(pSize) * WORD_TO_BYTE;
        pDestL = cert_addr + CERT_INFO_DST_OFFSET;
        *dest_addr = mem_read32(pDestL);
    }
    else
    {
        ERROR("get_info_from_cert key size error.\n");
        panic;
    }
}
/* End of function get_info_from_cert(uint32_t cert_addr, uint32_t *size, uint32_t *dest_addr) */

void load_start(LOAD_INFO* li)
{
    uint32_t rtn_val = EMMC_DEV_ERR;
    uint32_t sector_count;
    uint32_t fraction;

    /* Converted to number of sectors transferred. */
    sector_count = li->image_size >> CX_EMMC_SECTOR_SIZE_SHIFT;

    fraction = li->image_size % CX_EMMC_SECTOR_SIZE;
    /* Add 1 if there is a fraction */
    if(0U != fraction)
    {
        sector_count += 1U;
    }

    rtn_val = emmc_trans_data(li->part_num, (uintptr_t)(li->src_addr >> CX_EMMC_SECTOR_SIZE_SHIFT), 
                    (uintptr_t)li->boot_addr, sector_count);

    if(EMMC_DEV_OK != rtn_val)
    {
        ERROR("load_start(emmc_trans_data error).\n");
        panic;
    }
}
/* End of function load_start(LOAD_INFO* li) */

