/*******************************************************************************
 * 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   : Loader main function
 ******************************************************************************/
/******************************************************************************
 * @file          loader_main.c
 * - Version      : 0.01
 * @brief         1. IP initialization.
 *                2. DMA transfer the binary image from Flash to RAM.
 *                3. Authentication of the transferred image.
 *                4. Boot CR52 and CA76 core.
 *                5. Release of used resources.
 * .
 *****************************************************************************/
/******************************************************************************
 * History : DD.MM.YYYY Version  Description
 *         : 03.07.2020 0.01     First Release
 *****************************************************************************/

#include <stdint.h>
#include <stdio.h>
#include <loader_main.h>
#include <cpu_on.h>
#include <rst_register.h>
#include <image_load.h>
#include <ip_control.h>
#include <qos.h>
#include <dma.h>
#include <acc_prot.h>
#include <remap.h>
#include <remap_register.h>
#include <rom_api.h>
#include <mem_io.h>
#include <log.h>
#include <rcar_def.h>
#include <wdt.h>
#include <rst_register.h>

/* Provided code */
#include "../ip/ddr/boot_init_dram.h"


#define SECURE_BOOT             (1U)
#define NORMALE_BOOT            (0U)
#define RST_MODEMR_MD5          (0x00000020U)
#define PRR_DUMMY_REMAP_SIZE    (0x00000100U)
#define SMONI_REMAP_SIZE        (0x00022300U)

#define SMONI_IPL_PARAM_OFFSET  (0x00022200U)
#define SMONI_EP_INFO_OFFSET    (SMONI_IPL_PARAM_OFFSET + 0x0030U)

#define WDTRSTCR_RWDT_RSTMSK    ((uint32_t)1U << 0U)
#define WDTRSTCR_PASSWORD       (0xA55A0000U)

/* struct */
typedef struct{
    uint8_t uctype;
    uint8_t ucversion;
    uint16_t ussize;
    uint32_t uiattr;
    uint32_t psecmonimageinfo_low;
    uint32_t psecmonimageinfo_high;
    uint32_t psecoptepinfo_low;
    uint32_t psecoptepinfo_high;
    uint32_t psecoptimageinfo_low;
    uint32_t psecoptimageinfo_high;
    uint32_t pnonsecepinfo_low;
    uint32_t pnonsecepinfo_high;
    uint32_t pnonsecimageinfo_low;
    uint32_t pnonsecimageinfo_high;
}st_smoni_iplparams_t;

typedef struct{
    uint8_t uctype;
    uint8_t ucversion;
    uint16_t ussize;
    uint32_t uiattr;
    uint32_t ulpc_low;
    uint32_t ulpc_high;
    uint32_t ulspsr_low;
    uint32_t ulspsr_high;
    uint32_t ularg0_low;
    uint32_t ularg0_high;
    uint32_t ularg1_low;
    uint32_t ularg1_high;
    uint32_t ularg2_low;
    uint32_t ularg2_high;
    uint32_t ularg3_low;
    uint32_t ularg3_high;
    uint32_t ularg4_low;
    uint32_t ularg4_high;
    uint32_t ularg5_low;
    uint32_t ularg5_high;
    uint32_t ularg6_low;
    uint32_t ularg6_high;
    uint32_t ularg7_low;
    uint32_t ularg7_high;
}st_smoni_entrypointinfo_t;

/* prototype */
static uint32_t judge_bootmode(uint32_t lcs);
static void smoni_set_param(uint32_t smoni_entry_point);

uint32_t loader_main(void)
{
    uint32_t reg;           /* store register value */
    uint32_t ret;           /* store return value */
    int32_t result;         /* store result of ddr_init() */
    uint32_t ca53_load_num; /* number of load for CA53 program */
    uint32_t lcs;           /* store LCS state */
    uint32_t remap_addr;    /* store PRR logical address */
    uint32_t is_verify;
    uint32_t loop;
    __attribute__((__unused__)) const char *str;
    __attribute__((__unused__)) const char *product_v3h     = "V3H";
    __attribute__((__unused__)) const char *unknown         = "unknown";
    const char *lcs_name[] = {
        [LCS_CM] = "CM",
        [LCS_DM] = "DM",
        [LCS_SD] = "SD",
        [LCS_SE] = "SE",
        [LCS_FA] = "FA",
    };

    LOAD_INFO li[MAX_PLACED];


/*****************************************************************************
 *  Initialize Hardware
 *****************************************************************************/
    /* IP initialize */
    ip_init();

    /* Set the PRR register to the remap spece */
    remap_register(ICU_REMAP_PRR, PRR_DUMMY_REMAP_SIZE, &remap_addr);

    /* Unmask the detection of RWDT overflow */
    reg = mem_read32(RST_WDTRSTCR);
    reg &= ~WDTRSTCR_RWDT_RSTMSK;
    reg |= WDTRSTCR_PASSWORD;
    mem_write32(RST_WDTRSTCR, reg);

/*****************************************************************************
 *  Output boot message
 *****************************************************************************/
    NOTICE("ICUMXA Loader Rev.%s\n", IPL_VERSION);

    NOTICE("%s\n", build_message);

    /* Get PRR */
    reg = mem_read32(PRR);
    switch (reg & PRR_PRODUCT_MASK)
    {
        case PRR_PRODUCT_V3H:
        {
            str = product_v3h;
            break;
        }
        default:
        {
            str = unknown;
            break;
        }
    }
    NOTICE("PRR is R-Car %s Ver%d.%d\n", str,
        ((reg & PRR_MAJOR_MASK) >> PRR_MAJOR_SHIFT) + PRR_MAJOR_OFFSET,
         (reg & PRR_MINOR_MASK));

    /* Get LCS state */
    reg = ROM_GetLcs(&lcs);
    str = unknown;
    if (reg == GETLCS_OK)
    {
        if (NULL != lcs_name[lcs])
        {
            str = lcs_name[lcs];
        }
    }
    NOTICE("LCM state is %s\n", str);

#if ACC_PROT_ENABLE
    NOTICE("Access Protection Enable.\n");
#endif

/*****************************************************************************
 *  Load Certficate from QSPI
 *****************************************************************************/
    /* LCS judgement for secure boot */
    is_verify = judge_bootmode(lcs);

    /* Load key certificate */
    load_key_cert();

    /* Load content certificate */
    ca53_load_num = load_content_cert();

/*****************************************************************************
 *  Load RTOS from QSPI
 *****************************************************************************/
    /* Load RTOS from Flash */
    li[RTOS_ID].name = "RTOS";
    li[RTOS_ID].cert_addr = LOGICAL_CONTENT_CERT_ADDR(RTOS_ID);
    li[RTOS_ID].src_addr = CONTENT_CERT_DEST_ADDR +
        SRC_ADDR_OFFSET(RTOS_ID);

    /* Loading start */
    li[RTOS_ID].boot_addr = load_image(li[RTOS_ID].cert_addr,
        li[RTOS_ID].src_addr, li[RTOS_ID].name);

    /* DDR initialize */
    result = InitDram();
    if (INITDRAM_OK != result)
    {
        ERROR("Failed to DRAM initialize (%d).\n", result);
        panic;
    }

#if RCAR_DDR_REG_CHECK
    /* initialized reg check */
    result = InitDram_regcheck();

    if (result != 0)
    {
        ERROR("Failed to DDRPHY REG check (%d).\n", result);
        /*
         * User need to implement what IPL should do
         * if InitDram_regcheck() is failed.
         */
    }
    else
    {
        NOTICE("DDRPHY REG check is OK (%d).\n", result);
    }
#endif

    /* QoS configuration */
    qos_init();

    /* finish loading RTOS */
    load_end();


/*****************************************************************************
 *  Load CA53 Program#1 from QSPI
 *****************************************************************************/
    /* Load CA53 Program#1 from Flash */
    li[CA53_PROGRAM_ID].name = "CA53 Program";
    li[CA53_PROGRAM_ID].cert_addr = LOGICAL_CONTENT_CERT_ADDR(CA53_PROGRAM_ID);
    li[CA53_PROGRAM_ID].src_addr = CONTENT_CERT_DEST_ADDR
        + SRC_ADDR_OFFSET(CA53_PROGRAM_ID);

    /* Loading start */
    li[CA53_PROGRAM_ID].boot_addr =  load_image(
        li[CA53_PROGRAM_ID].cert_addr,
        li[CA53_PROGRAM_ID].src_addr, li[CA53_PROGRAM_ID].name);

    /* Authenticate of RTOS */
    if (NORMALE_BOOT != is_verify)
    {
        ret = ROM_SecureBootAPI((uint32_t *)li[RTOS_ID].cert_addr);
        if (SBROM_OK != ret)
        {
            ERROR("%s image Verification Failed!!!(0x%x)\n",
            li[RTOS_ID].name, ret);
            panic;
        }
    }

    /* CR7 Boot address set */
    arm_cpu_set_address(RCAR_PWR_TARGET_CR7, li[RTOS_ID].boot_addr);

    /* Access Protection Setting */
    acc_prot_init();

    /* boot CR7 */
    arm_cpu_on(RCAR_PWR_TARGET_CR7, 0U/* not use */);

    /* finish loading CA53 Program#1 */
    load_end();

/*****************************************************************************
 *  Load CA53 Program#2--#8 from QSPI
 *****************************************************************************/
    /* Load CA53 Program#n from Flash */
    for (loop = 1; loop < ca53_load_num; loop++)
    {
        li[CA53_PROGRAM_ID + loop].name = "CA53 Program";
        li[CA53_PROGRAM_ID + loop].cert_addr =
            LOGICAL_CONTENT_CERT_ADDR(CA53_PROGRAM_ID + loop);
        li[CA53_PROGRAM_ID + loop].src_addr = CONTENT_CERT_DEST_ADDR
            + SRC_ADDR_OFFSET(CA53_PROGRAM_ID + loop);

        /* Loading start */
        li[CA53_PROGRAM_ID + loop].boot_addr = load_image(
            li[CA53_PROGRAM_ID + loop].cert_addr,
            li[CA53_PROGRAM_ID + loop].src_addr,
            li[CA53_PROGRAM_ID + loop].name);

        /* Authenticate of CA53 Program#n-1 */
        if (NORMALE_BOOT != is_verify)
        {
            ret = ROM_SecureBootAPI(
                (uint32_t *)li[CA53_PROGRAM_ID + (loop - 1U)].cert_addr);
            if (SBROM_OK != ret)
            {
                ERROR("%s image Verification Failed!!!(0x%x)\n",
                     li[CA53_PROGRAM_ID + (loop - 1U)].name,
                     ret);
                panic;
            }
        }

        /* finish loading CA53 Program#n */
        load_end();
    }

/*****************************************************************************
 *  Load Secure Firmware from QSPI
 *****************************************************************************/
    /* Load Secure Firmware from Flash */
    li[SECURE_FW_ID].name = "SecureFW";
    li[SECURE_FW_ID].cert_addr = LOGICAL_CONTENT_CERT_ADDR(SECURE_FW_ID);
    li[SECURE_FW_ID].src_addr = CONTENT_CERT_DEST_ADDR
        + SRC_ADDR_OFFSET(SECURE_FW_ID);

    /* Loading start */
    li[SECURE_FW_ID].boot_addr =  load_image(li[SECURE_FW_ID].cert_addr,
        li[SECURE_FW_ID].src_addr, li[SECURE_FW_ID].name);

    /* Authenticate of CA53 Program#n */
    if (NORMALE_BOOT != is_verify)
    {
        ret = ROM_SecureBootAPI(
            (uint32_t *)li[CA53_PROGRAM_ID + (ca53_load_num - 1U)].cert_addr);
        if (SBROM_OK != ret)
        {
            ERROR("%s image Verification Failed!!!(0x%x)\n",
                 li[CA53_PROGRAM_ID + (ca53_load_num - 1U)].name,
                 ret);
            panic;
        }
    }

    /* Set Secure Monitor parameter */
    smoni_set_param(li[CA53_PROGRAM_ID].boot_addr);

    /* boot CA53 */
    arm_cpu_on(RCAR_PWR_TARGET_CA53, li[CA53_PROGRAM_ID].boot_addr);

    /* finish loading Secure Firmware */
    load_end();

    /* Authenticate of Secure Firmware */
    if (NORMALE_BOOT != is_verify)
    {
        ret = ROM_SecureBootAPI((uint32_t *)li[SECURE_FW_ID].cert_addr);
        if (SBROM_OK != ret)
        {
            ERROR("%s image Verification Failed!!!(0x%x)\n",
                 li[SECURE_FW_ID].name, ret);
            panic;
        }
    }

    /* Release the PRR register on the remap space. */
    remap_unregister(remap_addr);
    /* Set Hardware Initial value to IP. */
    ip_release();

    wdt_restart();

    return remap_get_remap_addr(li[SECURE_FW_ID].boot_addr);
}
/* End of function loader_main(void) */

static uint32_t judge_bootmode(uint32_t lcs)
{
    uint32_t md;
    uint32_t is_verify;

    /* LCS Status check */
    if((lcs != LCS_CM)
    && (lcs != LCS_DM)
    && (lcs != LCS_SD)
    && (lcs != LCS_SE)
    && (lcs != LCS_FA))
    {
        ERROR("LCS state error."
            "LCS = 0x%x", lcs);
        panic;
    }

    md = (mem_read32(RST_MODEMR) & RST_MODEMR_MD5) >> 5U;
    if (lcs == LCS_SE)
    {
        /* LCS=SE => Secure boot */
        is_verify = SECURE_BOOT;
    }
    else if ((lcs != LCS_SD) && (md == 0U))
    {
        /* LCS=CM/DM/FA and MD5=0 => Secure boot */
        is_verify = SECURE_BOOT;
    }
    else
    {
        /* LCS=SD or MD5=1 => Normal boot */
        is_verify = NORMALE_BOOT;
    }
    if (NORMALE_BOOT != is_verify)
    {
        NOTICE("Secure boot(ICUMX)\n");
    }
    else
    {
        NOTICE("Normal boot(ICUMX)\n");
    }

    return is_verify;
}
/* End of function judge_bootmode(uint32_t lcs) */

static void smoni_set_param(uint32_t smoni_entry_point)
{
#if CA53_PROG1_IS_SMONI
    uint32_t mapped_addr;
    st_smoni_iplparams_t *smoni_ipl_param;
    st_smoni_entrypointinfo_t *smoni_ep_info;

    remap_register(smoni_entry_point, SMONI_REMAP_SIZE, &mapped_addr);

    /* set struct address */
    smoni_ipl_param = (st_smoni_iplparams_t *)
                (mapped_addr + SMONI_IPL_PARAM_OFFSET);
    smoni_ep_info = (st_smoni_entrypointinfo_t *)
                (mapped_addr + SMONI_EP_INFO_OFFSET);

    /* set parameter */
    smoni_ipl_param->uctype         = 0x03U;
    smoni_ipl_param->ucversion      = 0x01U;
    smoni_ipl_param->ussize         = 0x0030U;
    smoni_ipl_param->uiattr         = 0x00000000U;
    smoni_ipl_param->psecmonimageinfo_low   = 0x00000000U;
    smoni_ipl_param->psecmonimageinfo_high  = 0x00000000U;
    smoni_ipl_param->psecoptepinfo_low  = 0x00000000U;
    smoni_ipl_param->psecoptepinfo_high     = 0x00000000U;
    smoni_ipl_param->psecoptimageinfo_low   = 0x00000000U;
    smoni_ipl_param->psecoptimageinfo_high  = 0x00000000U;
    smoni_ipl_param->pnonsecepinfo_low  =
        remap_get_phys_addr((uint32_t)smoni_ep_info);
    smoni_ipl_param->pnonsecepinfo_high     = 0x00000000U;
    smoni_ipl_param->pnonsecimageinfo_low   = 0x00000000U;
    smoni_ipl_param->pnonsecimageinfo_high  = 0x00000000U;

    smoni_ep_info->uctype           = 0x01U;
    smoni_ep_info->ucversion        = 0x01U;
    smoni_ep_info->ussize           = 0x0058U;
    smoni_ep_info->uiattr           = (uint32_t)CA53_PROG2_ATTR;
    smoni_ep_info->ulpc_low         =
        (uint32_t)((uint64_t)CA53_PROG2_PC & 0xFFFFFFFFU);
    smoni_ep_info->ulpc_high        =
        (uint32_t)(((uint64_t)CA53_PROG2_PC >> 32U) & 0xFFFFFFFFU);
    smoni_ep_info->ulspsr_low       =
        (uint32_t)((uint64_t)CA53_PROG2_SPSR & 0xFFFFFFFFU);
    smoni_ep_info->ulspsr_high      =
        (uint32_t)(((uint64_t)CA53_PROG2_SPSR >> 32U) & 0xFFFFFFFFU);
    smoni_ep_info->ularg0_low       =
        (uint32_t)((uint64_t)CA53_PROG2_ARG0 & 0xFFFFFFFFU);
    smoni_ep_info->ularg0_high      =
        (uint32_t)(((uint64_t)CA53_PROG2_ARG0 >> 32U) & 0xFFFFFFFFU);
    smoni_ep_info->ularg1_low       =
        (uint32_t)((uint64_t)CA53_PROG2_ARG1 & 0xFFFFFFFFU);
    smoni_ep_info->ularg1_high      =
        (uint32_t)(((uint64_t)CA53_PROG2_ARG1 >> 32U) & 0xFFFFFFFFU);
    smoni_ep_info->ularg2_low       =
        (uint32_t)((uint64_t)CA53_PROG2_ARG2 & 0xFFFFFFFFU);
    smoni_ep_info->ularg2_high      =
        (uint32_t)(((uint64_t)CA53_PROG2_ARG2 >> 32U) & 0xFFFFFFFFU);
    smoni_ep_info->ularg3_low       =
        (uint32_t)((uint64_t)CA53_PROG2_ARG3 & 0xFFFFFFFFU);
    smoni_ep_info->ularg3_high      =
        (uint32_t)(((uint64_t)CA53_PROG2_ARG3 >> 32U) & 0xFFFFFFFFU);
    smoni_ep_info->ularg4_low       =
        (uint32_t)((uint64_t)CA53_PROG2_ARG4 & 0xFFFFFFFFU);
    smoni_ep_info->ularg4_high      =
        (uint32_t)(((uint64_t)CA53_PROG2_ARG4 >> 32U) & 0xFFFFFFFFU);
    smoni_ep_info->ularg5_low       =
        (uint32_t)((uint64_t)CA53_PROG2_ARG5 & 0xFFFFFFFFU);
    smoni_ep_info->ularg5_high      =
        (uint32_t)(((uint64_t)CA53_PROG2_ARG5 >> 32U) & 0xFFFFFFFFU);
    smoni_ep_info->ularg6_low       =
        (uint32_t)((uint64_t)CA53_PROG2_ARG6 & 0xFFFFFFFFU);
    smoni_ep_info->ularg6_high      =
        (uint32_t)(((uint64_t)CA53_PROG2_ARG6 >> 32U) & 0xFFFFFFFFU);
    smoni_ep_info->ularg7_low       =
        (uint32_t)((uint64_t)CA53_PROG2_ARG7 & 0xFFFFFFFFU);
    smoni_ep_info->ularg7_high      =
        (uint32_t)(((uint64_t)CA53_PROG2_ARG7 >> 32U) & 0xFFFFFFFFU);

    remap_unregister(mapped_addr);
#endif  /* CA53_PROG1_IS_SMONI */
}
/* End of function smoni_set_param(uint32_t smoni_entry_point) */
