/*******************************************************************************
 * Copyright (c) 2018-2021 Renesas Electronics Corporation. All rights reserved.
 *
 *     RENESAS ELECTRONICS CONFIDENTIAL AND PROPRIETARY
 *
 * This software is provided as reference/sample code under the license
 * agreement between Renesas Electronics Corporation and licensee (the
 * "License Agreement") and shall be treated as specified in the License
 * Agreement.
 * These instructions, statements, and software are the confidential
 * information of Renesas Electronics Corporation. They must be used and
 * modified solely for the purpose for which it was furnished by Renesas
 * Electronics Corporation. All or part of these instructions, statements and
 * software must not be reproduced nor disclosed to any third party in any
 * form, unless permitted by the License Agreement.
 *
 * THIS SOFTWARE IS PROVIDED BY RENESAS ELEOCTRONICS CORPORATION "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
 * SATISFACTORY QUALITY, ACCURACY, TITLE AND NON-INFRINGEMENT ARE DISCLAIMED.
 * IN NO EVENT SHALL RENESAS ELECTRONICS CORPORATION BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, PUNITIVE, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 ******************************************************************************/

/*******************************************************************************
 * DESCRIPTION   : Image load function
 ******************************************************************************/
/******************************************************************************
 * @file          image_load.c
 * - Version      : 0.01
 * @brief         Access protection setting driver.
 * .
 *****************************************************************************/
/******************************************************************************
 * History : DD.MM.YYYY Version  Description
 *         : 03.07.2020 0.01     First Release
 *****************************************************************************/
 
/* indelude */
#include <stdint.h>
#include <image_load.h>
#include <dma.h>
#include <remap.h>
#include <scif.h>
#include <mem_io.h>
#include <log.h>
#include <rom_api.h>


#define KEY_SIZE_FLG_MSK            (0x00000003U)
#define KEY_SIZE_BIT_SHIFT          (21U)
#define CERT_INFO_FLG_OFFSET        (0x0000000CU)
#define KEY_SIZE_3072               (0x00000001U)
#define KEY_SIZE_2048               (0x00000000U)
#define NUM_OF_ALWAYS_LOAD_IMAGE    (2U)
#define WORD_TO_BYTE                (4U)
#define ERROR_PARAM                 (0U)
#define NOT_OVERLAP_FLAG            (0U)
#define OVERLAP_FLAG                (1U)


static void 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,
    uintptr_t *dest_addr);

void load_key_cert(void)
{
    uint32_t phys_dst;

    phys_dst = remap_get_phys_addr(KEY_CERT_DEST_ADDR);
    /* Load key cert */
    load_start(phys_dst, RTSRAM_BASE + KEY_CERT_SRC_OFFSET, KEY_CERT_SIZE);

    NOTICE(
        "======== key cert info ========\n"
        "destination address:0x%08x\n"
        "physical destination address:0x%08x\n"
        "source address:0x%08x\n"
        "size:0x%08x\n", KEY_CERT_DEST_ADDR, phys_dst,
        RTSRAM_BASE + KEY_CERT_SRC_OFFSET, KEY_CERT_SIZE);


    /* End loading key cert */
    load_end();
}
/* End of function load_key_cert(void) */

uint32_t load_content_cert(void)
{
    uint32_t load_num;
    uint32_t phys_dst;
    uint32_t loop;

    /* Get physical address of transfer destination. */
    phys_dst = remap_get_phys_addr(CONTENT_CERT_DEST_ADDR);
    /* Load content cert header */
    load_start(phys_dst, FLASH_CONTENT_CERT_ADDR, CONTENT_CERT_INFO_SIZE);

    NOTICE(
        "======== content cert info ========\n"
        "destination address:0x%08x\n"
        "physical destination address:0x%08x\n"
        "source address:0x%08x\n"
        "size:0x%08x\n", CONTENT_CERT_DEST_ADDR, phys_dst,
        FLASH_CONTENT_CERT_ADDR, CONTENT_CERT_INFO_SIZE);


    /* End loading cert header */
    load_end();

    load_num = mem_read32(CONTENT_CERT_DEST_ADDR);


    /* Check number of image load.
       In case of number of image load is 0, error of transfer parameter.
       In case of number of image loads is higher than 8,
       the transfer parameter error.                */
    if ((load_num == 0U) || (load_num > CA53_MAX_IMAGE)) {
        ERROR("Content cert info 'load image num' fault.\n");
        ERROR("load image num = %d\n",load_num);
        panic;
    }

    /* Load content cert of all images. */
    for(loop = 0; loop < (NUM_OF_ALWAYS_LOAD_IMAGE + load_num); loop++)
    {
        load_start((phys_dst + CONTENT_CERT_DST_OFFSET(loop)),
            (FLASH_CONTENT_CERT_ADDR + CONTENT_CERT_SRC_OFFSET(loop)),
            CONTENT_CERT_DST_SIZE);

        /* End loading content cert */
        load_end();
    }

    NOTICE(
        "======== content cert ========\n"
        "address:0x%08x size:0x%08x\n",
        (CONTENT_CERT_DEST_ADDR + CONTENT_CERT_INFO_SIZE),
        (CONTENT_CERT_DST_SIZE * (NUM_OF_ALWAYS_LOAD_IMAGE + load_num)));

    return load_num;
}
/* End of function load_content_cert(void) */

uint32_t load_image(uint32_t cert_addr, uint32_t flash_src_offset,
    const char *name)
{
    uint32_t load_addr;
    uint32_t flash_image_addr;
    uint32_t size;

    /* Get transfer parameter of image from a content certificate. */
    flash_image_addr = (FLASH_BASE + mem_read32(flash_src_offset));
    get_info_from_cert(cert_addr, &size, &load_addr);

    NOTICE("======== %s image load info ========\n"
           "load address \t= 0x%08x\n" "image size \t= 0x%08x\n"
           "source address \t= 0x%08x\n",
        name, load_addr, size, flash_image_addr);

    /* Check transfer range of image. */
    check_load_area(load_addr, flash_image_addr, size);

    /* Image load start. */
    load_start(load_addr, flash_image_addr, size);

    return load_addr;
}
/* End of function load_image(uint32_t cert_addr, uint32_t flash_src_offset, const char *name) */

static void check_load_area(uint32_t dst, uint32_t src, uint32_t len)
{
    uint32_t src_end;
    uint32_t dst_end;
    uint32_t overlap;
    uint32_t loop;
    static uint32_t s_num = 0U;

    /* The memory range of destination. */
    const ADDRESS_RANGE add_list[] = {
        [TARGET_MEM_DRAM] = {DRAM_BASE, DRAM_END},
        [TARGET_MEM_RTSRAM] = {RTSRAM_BASE, RTSRAM_END}
    };

    static ADDRESS_RANGE s_placed_image[MAX_PLACED] = {
        [0] = {IPL_TOP, IPL_END}, /* Overwrite after RTOS range check. */
        [1] = {0U,0U},
        [2] = {0U,0U},
        [3] = {0U,0U},
        [4] = {0U,0U},
        [5] = {0U,0U},
        [6] = {0U,0U},
        [7] = {0U,0U},
        [8] = {0U,0U},
        [9] = {0U,0U}
    };

    /* Check image size */
    if (len == 0U)
    {
        ERROR("image size error\n");
        panic;
    }

    /* Check whether source is overflow */
    if (src > (UINT32_MAX - len))
    {
        ERROR("1:overflow is occurred at source\n");
        ERROR("1:source address = 0x%x image size = 0x%x\n", src, len);
        panic;
    } else {
        src_end = src + len - 1U;
    }
    if (src_end < src)
    {
        ERROR("2:overflow is occurred at source\n");
        ERROR("2:source address = 0x%x image size = 0x%x\n", src, len);
        panic;
    }

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

    /* Check source address range. */
    if ((src < FLASH_BASE) || (FLASH_END < src_end))
    {
        ERROR("check load area (source address)\n");
        ERROR("source address = 0x%x image size = 0x%x\n", src, len);
        panic;
    }

    /* Check destination address range. */
    if ((add_list[1].topadd <= dst)
      && (dst_end <= add_list[1].endadd))
    { 
        /* check RT-SRAM */
    }
    else if ((add_list[0].topadd <= dst)
      && (dst_end <= add_list[0].endadd))
    { 
        /* check SDRAM */
    }
    else
    {
        ERROR("check_load_area (destination address)\n");
        ERROR("destination address = 0x%x image size = 0x%x\n", dst,
                len);
        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].topadd)
            && (dst <= s_placed_image[loop].endadd))
        {
            overlap = OVERLAP_FLAG;
        }
        else if ((dst_end >= s_placed_image[loop].topadd)
            && (dst_end <= s_placed_image[loop].endadd))
        {
            overlap = OVERLAP_FLAG;
        }
        else if ((dst < s_placed_image[loop].topadd)
            && (s_placed_image[loop].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].topadd = dst;
        s_placed_image[s_num].endadd = dst_end;
        INFO("[0x%x] topadd = 0x%x endadd = 0x%x\n", s_num,
            placed_image[s_num].topadd, placed_image[s_num].endadd);
        s_num++;
    } else {
        ERROR("check_load_area (overlap)\n");
        ERROR("destination address = 0x%x image size = 0x%x\n", dst,
                len);
        ERROR("overlapped image is [%x]\n", loop);
        ERROR("top address = 0x%x end address = 0x%x\n",
            s_placed_image[loop].topadd, s_placed_image[loop].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,
    uintptr_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_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 = (uintptr_t)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 = (uintptr_t)mem_read32(pDestL);
    }
    else
    {
        *size = ERROR_PARAM;
        *dest_addr = ERROR_PARAM;
    }
}
/* End of function get_info_from_cert(uint32_t cert_addr, uint32_t *size, uintptr_t *dest_addr) */
