/*
 * Copyright (c) 2015-2021, Renesas Electronics Corporation
 * All rights reserved.
 */

/******************************************************************************
 * @file          rcar_addr.c
 * - Version      : 0.01
 * @brief         1. IP initialization.
 *                2. DMA transfer the binary image from Flash to RAM.
 *                3. Authentication of the transferred image.
 * .
 *****************************************************************************/
/******************************************************************************
 * History : DD.MM.YYYY Version  Description
 *         : 29.05.2020 0.01     First Release
 *****************************************************************************/

#include <stdint.h>
#include <string.h>
#include "bit.h"
#include "debug.h"
#include "mmio.h"
#include "rpc_driver.h"
#include "dma_driver.h"
#include "rom_api.h"
#include "auth.h"
#include "rcar_def.h"


/*****************************************************************************
 *  Definitions
 *****************************************************************************/
#define RTOS_MAX_SIZE           (1024U * 1024U)
#define MAIN_LOADER_MAX_SIZE        ( 170U * 1024U)
#define UBOOT_MAX_SIZE          (1024U * 1024U)

#define FLASH_BASE          (0x08000000U)
#define FLASH_SIZE          (0x04000000U)
#define FLASH_RTOS_CERT_ADDR        (FLASH_BASE + 0x000C0000U)
#define FLASH_CA5X_CERT_ADDR        (FLASH_BASE + 0x000C0800U)
#define FLASH_UBOOT_CERT_ADDR       (FLASH_BASE + 0x000C1000U)

#define FLASH_RTOS_IMAGE_ADDR       (FLASH_BASE + 0x001C0000U)
#define FLASH_CA5X_IMAGE_ADDR       (FLASH_BASE + 0x002C0000U)
#define FLASH_UBOOT_IMAGE_ADDR      (FLASH_BASE + 0x00840000U)
#define FLASH_CERT_SIZE         (0x400U)

#define SYSRAM_BASE         (0xE6300000U)
#define SYSRAM_END          (0xE636FFFFU)
#define DRAM_BASE           (0x40000000U)
#define DRAM_END            (0xBFFFFFFFU)
/* Calculate address of certificate */
#define SEC_BOOT_CERT_ADDR(a)       (SYSRAM_BASE + (a) + 0x400U)
#define RTOS_CERT_ADDR          (DRAM_BASE + 0x08220000U)
#define CA5X_CERT_ADDR          (DRAM_BASE + 0x0800U)
#define UBOOT_CERT_ADDR         (DRAM_BASE + 0x1000U)

#define MAIN_CPU_NONE           (0U)

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

/*********************** RCarGen3_MFI *************************/
#define MFISSOFTMDR     (0xE6260600U)       /* SOFTMD register */

/*********************** RCarGen3_RST *************************/
#define RST_MODEMR      (0xE6160060U)       /* Mode Monitor Register */

typedef uint32_t (* SECURE_BOOT_API)(uint32_t a, uint32_t b, ROM_FuncReadFlash c);


/*****************************************************************************
 *  Unions
 *****************************************************************************/


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

/*****************************************************************************
 *  Globals
 *****************************************************************************/


/*****************************************************************************
 *  Macros
 *****************************************************************************/


/*****************************************************************************
 *  Prototypes
 *****************************************************************************/
static void check_load_area(uint32_t dst, uint32_t src, uint32_t len);
static void smoni_set_param(uint32_t smoni_entry_point);

uint32_t rcar_addr(uint32_t *main_cpu_addr)
{
#if (RCAR_KICK_MAIN_CPU != MAIN_CPU_NONE)
    uint32_t ca5x_load_addr;
    uint32_t uboot_load_addr;
#endif /* RCAR_KICK_MAIN_CPU */
    uint32_t rtos_load_addr;
    uint32_t boot_cert_addr;
    uint32_t ipl_cert_addr;
    uint32_t size;
    uint32_t ret;
    SECURE_BOOT_API sbrom_SecureBootAPI;

/*****************************************************************************
 *  Initialize Hardware
 *****************************************************************************/
    init_rpc();         /* Initialize RPC */
    init_dma();         /* Initialize DMA */

/*****************************************************************************
 *  LCS judgement for secure boot
 *****************************************************************************/
#if RCAR_SECURE_BOOT == 1
{
    uint32_t lcs;
    uint32_t md     = (mmio_read_32(RST_MODEMR)   & 0x00000020U) >> 5U;
    uint32_t softmd = (mmio_read_32(MFISSOFTMDR)  & 0x00000001U);

    /* Get LCS */
    ret = ROM_GetLcs(&lcs);
    if(ret != 0U)
    {   /* error check */
        ERROR("CR7: Failed to get the LCS. (%d)\n", ret);
        panic();
    }

    if (lcs == LCS_SE)
    { /* LCS=Secure */
        if (softmd == 0x1U)
        {   /* LCS=Secure + Normal boot (temp setting) */
            sbrom_SecureBootAPI = NULL;
        }
        else
        {   /* else is LCS=Secure + Secure boot */
            sbrom_SecureBootAPI = (SECURE_BOOT_API)ROM_SecureBootAPI;
        }
    }
    else if (lcs == LCS_SD)
    {
        /* LCS=SD */
        sbrom_SecureBootAPI = NULL;
    }
    else if (md == 0U)
    {   /* MD5=0 => LCS=CM/DM/RMA + Secure boot */
        sbrom_SecureBootAPI = (SECURE_BOOT_API)ROM_SecureBootAPI;
    }
    else
    {
        sbrom_SecureBootAPI = NULL; /* MD5=1 => LCS=CM/DM/SD/RMA + Normal boot */
    }
}
#else /* RCAR_SECURE_BOOT == 1 */
    sbrom_SecureBootAPI = NULL;
#endif /* RCAR_SECURE_BOOT == 1 */

    if (sbrom_SecureBootAPI != NULL)
    {
        NOTICE("Secure boot(CR7)\n");
    }
    else
    {
        NOTICE("Normal boot(CR7)\n");
    }

/*****************************************************************************
 *  Reload certificate from HyperFlash
 *****************************************************************************/
    get_cert_info(&boot_cert_addr, &size, &ipl_cert_addr);

/*****************************************************************************
 *  Load CR7 RTOS certificate from HyperFlash
 *****************************************************************************/
    exec_dma(RTOS_CERT_ADDR, FLASH_RTOS_CERT_ADDR, FLASH_CERT_SIZE);

/*****************************************************************************
 *  Load RTOS from HyperFlash
 *****************************************************************************/
    get_info_from_cert(RTOS_CERT_ADDR, &size, &rtos_load_addr);

    if (size == 0U)
    {
        ERROR("RTOS image size error\n");
        panic();
    }
    else if (size > RTOS_MAX_SIZE)
    {
        ERROR("RTOS image size error\n");
        panic();
    }
    else 
    {
        /* No else processing QAC compliant */
    }

    check_load_area(rtos_load_addr, FLASH_RTOS_IMAGE_ADDR, size);

    NOTICE("RTOS load address=0x%x RTOS image size=0x%x\n", rtos_load_addr, size);
    exec_dma(rtos_load_addr, FLASH_RTOS_IMAGE_ADDR, size);

/*****************************************************************************
 *  CR7 RTOS authentication
 *****************************************************************************/
    if (sbrom_SecureBootAPI != NULL)
    {
        ret = sbrom_SecureBootAPI(SEC_BOOT_CERT_ADDR(boot_cert_addr),
            RTOS_CERT_ADDR, 0U);
        if (ret != 0U)
        {
            ERROR("RTOS image Verification Failed!!!(0x%x)\n", ret);
            panic();
        }
    }

#if (RCAR_KICK_MAIN_CPU != MAIN_CPU_NONE)
/*****************************************************************************
 *  Load BL31 certificate from HyperFlash
 *****************************************************************************/
    exec_dma(CA5X_CERT_ADDR, FLASH_CA5X_CERT_ADDR, FLASH_CERT_SIZE);

/*****************************************************************************
 *  Load BL31 from HyperFlash
 *****************************************************************************/
    get_info_from_cert(CA5X_CERT_ADDR, &size, &ca5x_load_addr);

    if (size == 0U)
    {
        ERROR("BL31 image size error\n");
        panic();
    }
    else if (size > MAIN_LOADER_MAX_SIZE)
    {
        ERROR("BL31 image size error\n");
        panic();
    }
    else
    {
        /* No else processing QAC compliant */
    }

    check_load_area(ca5x_load_addr, FLASH_CA5X_IMAGE_ADDR, size);

    NOTICE("BL31 load address=0x%x BL31 image size=0x%x\n", ca5x_load_addr, size);
    exec_dma(ca5x_load_addr, FLASH_CA5X_IMAGE_ADDR, size);
    *main_cpu_addr = ca5x_load_addr;

/*****************************************************************************
 *  BL31 authentication
 *****************************************************************************/
    if (sbrom_SecureBootAPI != NULL)
    {
        ret = sbrom_SecureBootAPI(SEC_BOOT_CERT_ADDR(boot_cert_addr)
            ,CA5X_CERT_ADDR, 0);
        if (ret != 0U)
        {
            ERROR("BL31 image Verification Failed!!!(0x%x)\n", ret);
            panic();
        }
    }

/*****************************************************************************
 *  Load U-Boot Loader certificate from HyperFlash
 *****************************************************************************/
    exec_dma(UBOOT_CERT_ADDR, FLASH_UBOOT_CERT_ADDR, FLASH_CERT_SIZE);

/*****************************************************************************
 *  Load U-Boot from HyperFlash
 *****************************************************************************/
    get_info_from_cert(UBOOT_CERT_ADDR, &size, &uboot_load_addr);

    if (size == 0U)
    {
        ERROR("U-Boot image size error\n");
        panic();
    }
    else if (size > UBOOT_MAX_SIZE)
    {
        ERROR("U-Boot image size error\n");
        panic();
    }
    else
    {
        /* No else processing QAC compliant */
    }

    check_load_area(uboot_load_addr, FLASH_UBOOT_IMAGE_ADDR, size);

    NOTICE("U-Boot load address=0x%x U-Boot image size=0x%x\n", uboot_load_addr, size);
    exec_dma(uboot_load_addr, FLASH_UBOOT_IMAGE_ADDR, size);

/*****************************************************************************
 *  U-Boot Loader authentication
 *****************************************************************************/
    if (sbrom_SecureBootAPI != NULL)
    {
        ret = sbrom_SecureBootAPI(SEC_BOOT_CERT_ADDR(boot_cert_addr)
            ,UBOOT_CERT_ADDR, 0);
        if (ret != 0U)
        {
            ERROR("U-Boot image Verification Failed!!!(0x%x)\n", ret);
            panic();
        }
    }

/*****************************************************************************
 *  store the  Secure monitor parameter to SDRAM.
 *****************************************************************************/
    smoni_set_param(ca5x_load_addr);
#endif /* (RCAR_KICK_MAIN_CPU != MAIN_CPU_NONE) */
    return rtos_load_addr;
}
/* End of function rcar_addr */

static void check_load_area(uint32_t dst, uint32_t src, uint32_t len)
{
    /* check source range */
    if((src < FLASH_BASE) || ((FLASH_BASE + FLASH_SIZE -1) < (src + len)))
    {
        ERROR("CR7: check load area (source address)\n");
        panic();
    }

    /* check destination range */
    if((dst < DRAM_BASE) || (DRAM_END < (dst + len)))
    {
        ERROR("CR7: check_load_area (destination address)\n");
        panic();
    }
}
/* End of function check_load_area */

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

    /* set struct address */
    smoni_ipl_param = (st_smoni_iplparams_t *)
                (smoni_entry_point + SMONI_IPL_PARAM_OFFSET);
    smoni_ep_info = (st_smoni_entrypointinfo_t *)
                (smoni_entry_point + 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  = (uintptr_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);
#endif /* CA53_PROG1_IS_SMONI */
}
/* End of function smoni_set_param */

