/*******************************************************************************
 * 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 2021-2023 Renesas Electronics Corporation All rights reserved.
 *******************************************************************************/

/*******************************************************************************
 * DESCRIPTION   : Loader main function
 ******************************************************************************/
/******************************************************************************
 * @file          loader_main.c
 * - Version      : 0.15
 * @brief         1. IP initialization.
 *                2. DMA transfer the binary image from Flash to RAM.
 *                3. Authentication of the transferred image.
 *                4. Boot CR and CA core.
 *                5. Release of used resources.
 * .
 *****************************************************************************/
/******************************************************************************
 * History : DD.MM.YYYY Version  Description
 *         : 28.07.2021 0.01     First Release
 *         : 03.09.2021 0.02     Modify macro definition name.
 *                               Add process to call rpc_release function.
 *         : 30.09.2021 0.03     Support of eMMC boot.
 *         : 15.10.2021 0.04     Modified the MCU boot sequence.
 *                               remove include of Flash/eMMC.
 *         : 03.12.2021 0.05     Add RT-VRAM extend mode.
 *         :                     CA IPL boot support
 *         : 06.01.2022 0.06     Support for two-stage boot of G4MH
 *         :                     Add the Reset mask release process for RWDT.
 *         :                     Add exception handling for ICUMX_WDTA.
 *         : 20.01.2022 0.07     Capture DDR rev.0.02rc11
 *                               Add ICUMX name unification.
 *         : 28.02.2022 0.08     Modify the process of setting the parameters
 *                               used by BL31 in IPL.
 *         : 14.04.2022 0.09     Fixed Set SDCLK to 200MHz.
 *         : 23.05.2022 0.10     Integration of S4 and V4H
 *                               Renamed from loader_main.c to loader_main_s4.c.
 *         : 04.07.2022 0.11     Change loading Control Domain process to
 *                               calling load_main_for_mcu() function.
 *         : 05.08.2022 0.12     Add authentication of software minimum
 *                               version table.
 *         : 30.09.2022 0.13     Modify pre-process pranch in load_main_for_mcu
 *                               function.
 *                               Modify authentication process of software
 *                               minimum version.
 *         : 12.01.2023 0.14     Modified argument to fixed
 *                               when calls smoni_set_param() function.
 *                               Modified processing sequence for Access
 *                               protection.
 *         : 04.04.2023 0.15     Removed stdio.h.
 *****************************************************************************/

#include <stdint.h>
#include <loader_main.h>
#include <loader_main_common.h>
#include <cpu_on.h>
#include <rst_register.h>
#include <image_load.h>
#include <ip_control.h>
#include <qos.h>
#include <remap.h>
#include <remap_register.h>
#include <rom_api.h>
#include <mem_io.h>
#include <log.h>
#include <wdt.h>
#include <micro_wait.h>
#include <mcu_register.h>
#include <rtvram.h>
#include <ram_def.h>
#include <access_protection.h>
#include <cpu_on_for_mcu.h>
#include <loader_main_mcu.h>

/* Provided code */
#include "../ip/ddr/boot_init_dram.h"
# if (ECC_ENABLE == 1)
    #include "../ip/ddr/s4/lpddr4x/ecc_enable_s4.h"
# endif

#define CA_IPL  (0U)
#define BL31    (1U)

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

/* flag of set BL31 parameter*/
#define CA_APP_SET_PARAM_ENABLE    (1U)
/* Calculation set address for BL31 parameter */
#define SMONI_IPL_PARAM_OFFSET  (0x00022200U)
#define SMONI_EP_INFO_OFFSET    (SMONI_IPL_PARAM_OFFSET + 0x0030U)
#define OPTEE_EP_INFO_OFFSET    (SMONI_IPL_PARAM_OFFSET + 0x0088U)
#define BL31_KIND_BOOT_ADDR     (SMONI_IPL_PARAM_OFFSET + 0x0D00U)
/* KIND BOOT flag*/
#define BL31_COLD_BOOT          (0x0000000000000000U)
#define BL31_WARM_BOOT          (0x0000000000000001U)
#define SMONI_KIND_BOOT_PARAM   (0xFFFFFFFFFFFFFFFFU)
/* Program top address of Secure Monitor(BL31), OP-TEE and u-boot. */
#define SMONI_PHYS_TOP_ADDR     (0x46400000U)
#define OPTEE_PHYS_TOP_ADDR     (0x44100000U)
#define UBOOT_PHYS_TOP_ADDR     (0x50000000U)

/* 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;

static void load_main_for_mcu(const LOAD_INFO *li, uint32_t is_verify);
static void smoni_set_param(uint32_t smoni_entry_point,
                            uint32_t tee_entry_point,
                            uint32_t uboot_entry_point);

static void load_main_for_mcu(const LOAD_INFO *li);

#if ((CR_SECURE_DEBUG == SECURE_DEBUG_ENABLE) || (ADD_HOTPLUG_MAGIC == ADD_MAGIC_NUMBER))
#define HOTPLUG_MAGIC_NUM       (0x853F912EU)
#else
#define HOTPLUG_MAGIC_NUM       (0x00000000U)
#endif
__attribute__ ((section (".cr_hot_plug_magic"))) const uint32_t magic_num[4] = {0x00000000U, 0x00000000U,
                                                                                0x00000000U, HOTPLUG_MAGIC_NUM};

uint32_t loader_main(void)
{
    uint32_t reg;       /* store register value */
    int32_t result;     /* store result of ddr_init() */
    uint32_t is_verify = SECURE_BOOT;
    uint32_t boot_ca_id;
    uint32_t auth_count = 0U;
    __attribute__((__unused__)) uint32_t loop = 0U;
    __attribute__((__unused__)) uint32_t ca_load_num; /* number of load for Optional CA program */
    LOAD_INFO li[MAX_PLACED] = {0U};


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

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

# if (ECC_ENABLE == 1)
/*****************************************************************************
 *  ECC and EDC Initialize
 *****************************************************************************/
    ecc_rtsram_enable();
    edc_axi_enable();
    edc_vram_enable();
#endif

/*****************************************************************************
 *  Work Around for APMU
 *****************************************************************************/
    wa_setting_apmu();

/*****************************************************************************
 *  Output boot message
 *****************************************************************************/
    is_verify =print_boot_msg();

/*****************************************************************************
 *  Setting Access protection
 *****************************************************************************/
    /* Region ID access protection */
    rgid_protection();
    ram_protection();

    /* Change the Region ID (Master) of the Module used for data transfer to a temporary setting.  */
    set_master_rgid_4_tfr_mod();

/*****************************************************************************
 *  Load Certficate from QSPI
 *****************************************************************************/
    /* Load content certificate */
    ca_load_num = load_content_cert();

    /* Get load information */
    load_init(li);

/*****************************************************************************
 *  Authenticate Software minimum version table
 *****************************************************************************/
    auth_min_ver_tbl(is_verify, li);

/*****************************************************************************
 *  Load Control Domain
 *****************************************************************************/
    load_main_for_mcu(li, is_verify);

/*****************************************************************************
 *  DDR Initialization
 *****************************************************************************/

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

#if (ECC_ENABLE == 1)
    /* ECC Protection */
	enable_ecc();
#endif /* ECC_ENABLE */

    /* QoS configuration */
    qos_init();

    /* RT-VRAM Extend mode */
    rtvram_extendmode();

/*****************************************************************************
 *  Load RTOS from Flash
 *****************************************************************************/
    /* Start loading RTOS image from Flash into SDRAM */
    load_image(&li[RTOS_ID]);

    /* finish loading RTOS */
    load_end();


/*****************************************************************************
 *  Load Cx IPL from Flash
 *****************************************************************************/
#if (CA_LOAD_TYPE == CA_IPL)
    /* Start loading Cx IPL image from Flash into SDRAM */
    load_image(&li[CA_PROGRAM_ID]);

    /* Authenticate of RTOS */
    rom_secureboot(is_verify, &li[RTOS_ID]);

    /* boot CR */
    arm_cpu_on(RCAR_PWR_TARGET_CR, li[RTOS_ID].boot_addr);

    /* The CA image to boot is CA IPL. */
    boot_ca_id = CA_PROGRAM_ID;

    /* finish loading Cx IPL */
    load_end();
#elif  (CA_LOAD_TYPE == BL31)
    /* Authenticate of RTOS */
    rom_secureboot(is_verify, &li[RTOS_ID]);

    /* boot CR */
    arm_cpu_on(RCAR_PWR_TARGET_CR, li[RTOS_ID].boot_addr);

    /* The CA image to boot is CA prgram#1. */
    boot_ca_id = CA_OPTIONAL_ID;

/*****************************************************************************
 *  Load CA Program#1--#8 from Flash
 *****************************************************************************/
    /* Start loading CA Program#n image from Flash into SDRAM */
    for (loop = 0U; loop < ca_load_num; loop++)
    {
        /* Loading start */
        load_image(&li[boot_ca_id + loop]);

        /* Authenticate of CA Program#n-1 */
        if (loop != 0U)
        {
            rom_secureboot(is_verify, &li[boot_ca_id + loop - 1U]);
            auth_count++;
        }

        /* finish loading CA Program#n */
        load_end();
    }
#endif  /* (CA_LOAD_TYPE == CA_IPL) */
/*****************************************************************************
 *  Load Secure Firmware from Flash
 *****************************************************************************/
    /* Start loading Secure FW image from Flash into SDRAM */
    load_image(&li[SECURE_FW_ID]);

    /* Authenticate of Cx IPL or CA Program#n */
    rom_secureboot(is_verify, &li[boot_ca_id + auth_count]);

    /* Set Secure Monitor parameter */
    smoni_set_param(SMONI_PHYS_TOP_ADDR,    /* BL31 */
                    OPTEE_PHYS_TOP_ADDR,    /* OP-TEE */
                    UBOOT_PHYS_TOP_ADDR);   /* U-Boot */

    /* boot CA */
    arm_cpu_on(RCAR_PWR_TARGET_CA, li[boot_ca_id].boot_addr);

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

    /* load_secure data(for ICUMXB) */
    load_securedata(SECURE_FW_ID);

    /* Authenticate of Secure Firmware */
    rom_secureboot(is_verify, &li[SECURE_FW_ID]);

    /* finish loading secure data */
    load_end();

    /* Finally Protection setting */
    ram_protection_final();
    rgid_protection_final();

#if (ECC_ENABLE == 1)
    /* Notice the ecc enable */
    NOTICE("Enabled ECC and EDC for RT-SRAM, AXI, VRAM, SDRAM. \n");
# endif
    NOTICE("Load finish.\n");
    ip_release();

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

static void load_main_for_mcu(const LOAD_INFO *li, uint32_t is_verify)
{
#if (BOOT_MCU != 0U)
    mcu_load_main(li, is_verify);
#endif /* (BOOT_MCU != 0U) */
}
/* End of function load_main_for_mcu(const LOAD_INFO *li, uint32_t is_verify) */

static void smoni_set_param(uint32_t smoni_entry_point,
                            uint32_t tee_entry_point,
                            uint32_t uboot_entry_point)
{
#if (SET_CA_PARAM == CA_APP_SET_PARAM_ENABLE)
    uint32_t mapped_addr;
    st_smoni_iplparams_t *smoni_ipl_param;
    st_smoni_entrypointinfo_t *smoni_ep_info;
    st_smoni_entrypointinfo_t *optee_ep_info;

    remap_register(smoni_entry_point, &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);
    optee_ep_info = (st_smoni_entrypointinfo_t *)
                (mapped_addr + OPTEE_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;      /* structure size */
    smoni_ep_info->uiattr                   = 0x00000001U;  /* SECURE */
    smoni_ep_info->ulpc_low                 = uboot_entry_point;
    smoni_ep_info->ulpc_high                = 0x00000000U;
    /* SPSR_EL3.E[9]   = b1    (BigEndian)      *
     *         .A[8]   = b1    (DataAbort MASK) *
     *         .I[7]   = b1    (IRQ MASK)       *
     *         .F[6]   = b1    (FIQ MASK)       *
     *         .M[4]   = b0    (AArcch64)       *
     *         .M[3:1] = b0101 (EL1h)           */
    smoni_ep_info->ulspsr_low               = 0x000003C5U;
    smoni_ep_info->ulspsr_high              = 0x00000000U;
    smoni_ep_info->ularg0_low               = 0x00000000U;
    smoni_ep_info->ularg0_high              = 0x00000000U;
    smoni_ep_info->ularg1_low               = 0x00000000U;
    smoni_ep_info->ularg1_high              = 0x00000000U;
    smoni_ep_info->ularg2_low               = 0x00000000U;
    smoni_ep_info->ularg2_high              = 0x00000000U;
    smoni_ep_info->ularg3_low               = 0x00000000U;
    smoni_ep_info->ularg3_high              = 0x00000000U;
    smoni_ep_info->ularg4_low               = 0x00000000U;
    smoni_ep_info->ularg4_high              = 0x00000000U;
    smoni_ep_info->ularg5_low               = 0x00000000U;
    smoni_ep_info->ularg5_high              = 0x00000000U;
    smoni_ep_info->ularg6_low               = 0x00000000U;
    smoni_ep_info->ularg6_high              = 0x00000000U;
    smoni_ep_info->ularg7_low               = 0x00000000U;
    smoni_ep_info->ularg7_high              = 0x00000000U;

    optee_ep_info->uctype                   = 0x01U;
    optee_ep_info->ucversion                = 0x02U;
    optee_ep_info->ussize                   = 0x0058U;
    optee_ep_info->uiattr                   = 0x00000008U;
    optee_ep_info->ulpc_low                 = tee_entry_point;
    optee_ep_info->ulpc_high                = 0x00000000U;
    optee_ep_info->ulspsr_low               = 0x000003C5U;
    optee_ep_info->ulspsr_high              = 0x00000000U;
    optee_ep_info->ularg0_low               = 0x00000000U;
    optee_ep_info->ularg0_high              = 0x00000000U;
    optee_ep_info->ularg1_low               = 0x00000000U;
    optee_ep_info->ularg1_high              = 0x00000000U;
    optee_ep_info->ularg2_low               = 0x00000000U;
    optee_ep_info->ularg2_high              = 0x00000000U;
    optee_ep_info->ularg3_low               = 0x00000000U;
    optee_ep_info->ularg3_high              = 0x00000000U;
    optee_ep_info->ularg4_low               = 0x00000000U;
    optee_ep_info->ularg4_high              = 0x00000000U;
    optee_ep_info->ularg5_low               = 0x00000000U;
    optee_ep_info->ularg5_high              = 0x00000000U;
    optee_ep_info->ularg6_low               = 0x00000000U;
    optee_ep_info->ularg6_high              = 0x00000000U;
    optee_ep_info->ularg7_low               = 0x00000000U;
    optee_ep_info->ularg7_high              = 0x00000000U;

    mapped_addr += BL31_KIND_BOOT_ADDR;
    mem_write32((mapped_addr), (uint32_t)((uint64_t)BL31_COLD_BOOT & 0xFFFFFFFFU));
    mem_write32(((mapped_addr) + 0x4U),
        (uint32_t)(((uint64_t)BL31_COLD_BOOT >> 32U) & 0xFFFFFFFFU));

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