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

/*******************************************************************************
 * DESCRIPTION   : Loader main for MCU
 ******************************************************************************/
 /******************************************************************************
 * @file          loader_main_mcu.c
 * - Version      : 0.03
 * @brief         1. Loading G4MH(1st) image including integrity check.
 *                2. Loading ICUMH image including integrity check.
 *                3. Loading G4MH(2nd) image including integrity check.
 * .
 *****************************************************************************/
/******************************************************************************
 * History : DD.MM.YYYY Version  Description
 *         : 22.06.2022 0.01     First Release
 *         : 05.08.2022 0.02     Add sw_version_check function call to
 *         :                     mcu_img_verify function.
 *         : 19.01.2023 0.03     Add ECC and Address parity check for CodeSRAM.
 *****************************************************************************/

#include <stdint.h>
#include <string.h>
#include <types.h>
#include <mem_io.h>
#include <cpu_on_for_mcu.h>
#include <ram_def.h>
#include <mcu_register.h>
#include <micro_wait.h>
#include <image_load.h>
#include <cpu_on_for_mcu.h>
#include <loader_main_mcu.h>
#include <image_load_for_mcu.h>
#if (MCU_SECURE_BOOT == MCU_SEC_BOOT_ENABLE)
#include <mcu_secureboot.h>
#endif /* (MCU_SECURE_BOOT == MCU_SEC_BOOT_ENABLE) */
#include <remap.h>
#include <rom_api.h>
#include <codesram_ecc.h>

#define ICUMH_OPBT_OFFSET       (0x200U)

#define MCU_TRANSFER_UNIT       (0x00040000U)   /* 256KiB */
#define RTVRAM_TMP_G4MH_TOP_A   (0xE2080000U)
#define RTVRAM_TMP_G4MH_TOP_B   (0xE20C0000U)

#define GREG120_CODE            (0x5AA5A55AU)
#define G4MH_LOAD_FIN_CODE      (0x0000001EU)

/* Prototype */
static void load_g4mh_1st(const LOAD_INFO *li, uint32_t is_verify);
static void load_icumh(const LOAD_INFO *li, uint32_t is_verify);
static void load_g4mh_2nd(const LOAD_INFO *li, uint32_t is_verify);
static void w_load_even(const LOAD_INFO *li, LOAD_INFO *tmp_li, uint32_t count, uint32_t is_verify);
static void w_load_odd(const LOAD_INFO *li, LOAD_INFO *tmp_li, uint32_t count, uint32_t is_verify);
static void mcu_key_cert_check(LOAD_INFO *li, uint32_t is_verify);
static void mcu_cnt_cert_check(const LOAD_INFO *li, uint32_t is_verify);
static void mcu_img_verify(const LOAD_INFO *li, uint32_t is_verify);


static void load_g4mh_1st(const LOAD_INFO *li, uint32_t is_verify)
{
    LOAD_INFO tmp_li = {0U};

    /* Copy li to tmporary structure. (Because original li members are required later.) */
    (void)memcpy((void *)&tmp_li, li, sizeof(LOAD_INFO));
    /* Change boot_addr to RTVRAM_BASE. */
    tmp_li.boot_addr = RTVRAM_BASE;

    /* Output image info. */
    load_image_info_print(li);

    /* Check load information. */
    check_load_area(li);

    /* Release write protection of SCDS0. */
    mem_write32(MCUAXI_PBG_ERRSLV_PBGKCPROT, MCU_HBG_REL_CODE);
    mem_write32(MCU_GREG120, 0x00000000U);

    /* Set G4MH Option Byte. */
    mcu_set_opbt();
    /* Set ICUMH Option Byte. */
    mcu_set_icum_opbt();

    mem_write32(MCU_GREG120, GREG120_CODE);
    micro_wait(10U);

    /* Write protection of SCDS0. */
    mem_write32(MCUAXI_PBG_ERRSLV_PBGKCPROT, MCU_HBG_PROT_CODE);

    /* Load G4MH(1st) image from external flash to RT-VRAM. */
    load_start(&tmp_li);

    /* Verify Key Certificate. */
    mcu_key_cert_check(&tmp_li, is_verify);

    /* Disable bus guard of MCU. */
    mcu_set_hbg();

    /* Finish loading G4MH(1st) image from external flash to RT-VRAM. */
    load_end();

    /* Change src_addr from external flash to RT-VRAM. */
    tmp_li.src_addr  = tmp_li.boot_addr;
    /* Change boot_addr from RT-VRAM to CodeSRAM. */
    tmp_li.boot_addr = li->boot_addr;

    /* Disable ECC error and Address parity error check for CodeSRAM. */
    disable_codesram_ecc_parity(G4MH_PRG_1_BOOT_ADDR, G4MH_PRG_1_SIZE);

    /* Load G4MH(1st) image from RT-VRAM to CodeSRAM. */
    image_load_for_mcu(&tmp_li, is_verify);

    /* Verify Content Certificate */
    mcu_cnt_cert_check(&tmp_li, is_verify);

    /* Finish loading G4MH(1st) image from RT-VRAM to CodeSRAM. */
    load_end_for_mcu(&tmp_li, is_verify);

    /* Enable ECC error and Address parity error check for CodeSRAM. */
    enable_codesram_ecc_parity(G4MH_PRG_1_BOOT_ADDR, G4MH_PRG_1_SIZE);

    /* Verify G4MH(1st) image. */
    mcu_img_verify(&tmp_li, is_verify);

    /* Check ECC error and Address parity error detection for CodeSRAM. */
    chk_codesram_ecc_parity(G4MH_PRG_1_BOOT_ADDR, G4MH_PRG_1_SIZE);

    /* Reset CSRM registers that related CodeSRAM ECC to H/W initial value. */
    initialize_codesram_ecc_parity(G4MH_PRG_1_BOOT_ADDR, G4MH_PRG_1_SIZE);


    /* Change Bus mode of CodeSRAM to the Local Flash Bus mode. */
    mcu_set_csrm(G4MH_PRG_1_BOOT_ADDR, G4MH_PRG_1_SIZE);

    /* Boot G4MH */
    mcu_cpu_on(MCU_PWR_TARGET_G4MH);
}
/* End of function load_g4mh_1st(const LOAD_INFO *li) */

static void load_icumh(const LOAD_INFO *li, uint32_t is_verify)
{
#if (BOOT_MCU == MCU_BOOT_G4MH_ICUMH)
    LOAD_INFO tmp_li = {0U};

    /* Copy li to tmporary structure. (Because original li members are required later.) */
    (void)memcpy((void *)&tmp_li, li, sizeof(LOAD_INFO));
    /* Change boot_addr to RTVRAM_BASE. */
    tmp_li.boot_addr = RTVRAM_BASE;

    /* Output image info. */
    load_image_info_print(li);

    /* Check load information. */
    check_load_area(li);

    /* Load ICUMH image from external flash to RT-VRAM. */
    load_start(&tmp_li);

    /* Verify Key Certificate. */
    mcu_key_cert_check(&tmp_li, is_verify);

    /* Finish loading ICUMH image from external flash to RT-VRAM. */
    load_end();

    /* Change src_addr from external flash to RT-VRAM. */
    tmp_li.src_addr  = tmp_li.boot_addr;
    /* Change boot_addr from RT-VRAM to CodeSRAM. */
    tmp_li.boot_addr = li->boot_addr;

    /* Disable ECC error and Address parity error check for CodeSRAM. */
    disable_codesram_ecc_parity(ICUMH_PRG_BOOT_ADDR, ICUMH_PRG_SIZE);

    /* Load ICUMH image from RT-VRAM to CodeSRAM. */
    image_load_for_mcu(&tmp_li, is_verify);

    /* Verify Content Certificate */
    mcu_cnt_cert_check(&tmp_li, is_verify);

    /* Finish loading ICUMH image from RT-VRAM to CodeSRAM. */
    load_end_for_mcu(&tmp_li, is_verify);

    /* Load Secure data from external flash to RT-VRAM */
    load_securedata(ICUMH_PROGRAM_ID);

    /* Enable ECC error and Address parity error check for CodeSRAM. */
    enable_codesram_ecc_parity(ICUMH_PRG_BOOT_ADDR, ICUMH_PRG_SIZE);

    /* Verify ICUMH image. */
    mcu_img_verify(&tmp_li, is_verify);

    /* Finish loading Secure data from external flash to RT-VRAM. */
    load_end();

    /* Check ECC error and Address parity error detection for CodeSRAM. */
    chk_codesram_ecc_parity(ICUMH_PRG_BOOT_ADDR, ICUMH_PRG_SIZE);

    /* Reset CSRM registers that related CodeSRAM ECC to H/W initial value. */
    initialize_codesram_ecc_parity(ICUMH_PRG_BOOT_ADDR, ICUMH_PRG_SIZE);

    /* Load Secure data from RT-VRAM to Data-SRAM. */
    load_securedata_for_mcu();

    /* Change Bus mode of CodeSRAM to the Local Flash Bus mode. */
    mcu_set_csrm(ICUMH_PRG_BOOT_ADDR, ICUMH_PRG_SIZE);

    /* Boot ICUMH */
    mcu_cpu_on(MCU_PWR_TARGET_ICUMH);
#endif /* (BOOT_MCU == MCU_BOOT_G4MH_ICUMH) */
}
/* End of function load_icumh(const LOAD_INFO *li) */

static void load_g4mh_2nd(const LOAD_INFO *li, uint32_t is_verify)
{
    uint32_t count       = 0U;
    uint32_t dst_size    = li->image_size;
    uint32_t even_or_odd = 0U;
    LOAD_INFO tmp_li     = {0U};
    void (*p_w_load_func[])(const LOAD_INFO *li, LOAD_INFO *tmp_li, uint32_t count, uint32_t is_verify)
                         = {w_load_even, w_load_odd};

    /* Copy li to tmporary structure. (Because original li members are required later.) */
    (void)memcpy((void *)&tmp_li, li, sizeof(LOAD_INFO));

    /* A transfer unit is 256KiB. */
    tmp_li.image_size = MCU_TRANSFER_UNIT;
    /* Change boot_addr to 0xE2080000. */
    tmp_li.boot_addr = RTVRAM_TMP_G4MH_TOP_A;

    /* Output image info. */
    load_image_info_print(li);

    /* Check load information. */
    check_load_area(li);

    /* Load image from external flash to RT-VRAM. */
    load_start(&tmp_li);

    /* Verify Key Certificate. */
    mcu_key_cert_check(&tmp_li, is_verify);

    /* Finish loading image from external flash to RT-VRAM. */
    load_end();

    /* Disable ECC error and Address parity error check for CodeSRAM. */
    disable_codesram_ecc_parity(G4MH_PRG_2_BOOT_ADDR, G4MH_PRG_2_SIZE);

    if (dst_size <= tmp_li.image_size)
    {
        /* If image size is less than 256KiB. */
        tmp_li.image_size = dst_size;
        mcu_cnt_cert_check(li, is_verify);
    }
    else /* If image size is larger than 256KiB. */
    {
        /* dst_size - 256KiB */
        dst_size -= tmp_li.image_size;

        if (dst_size < tmp_li.image_size)
        {
            /* If remaining image size is less than 256KiB. */
            count++;
            even_or_odd       = count;
            tmp_li.image_size = dst_size;
            dst_size          = 0U;
        }
        else /* If remaining image size is still larger than 256KiB. */
        {
            count++;
            even_or_odd  = count;
            dst_size    -= tmp_li.image_size;
        }
        /* Load image(#count) from external flash to RT-VRAM. *
         * Load image(#count-1) from RT-VRAM to CodeSRAM.     */
        p_w_load_func[even_or_odd](li, &tmp_li, count, is_verify);

        /* Verify Content Certificate */
        mcu_cnt_cert_check(&tmp_li, is_verify);

        /* Finish loading image(#count) from external flash to RT-VRAM. */
        load_end();
        /* Finish loading image(#count-1) from RT-VRAM to CodeSRAM. */
        load_end_for_mcu(&tmp_li, is_verify);

        /* Load remaining image. */
        while (dst_size != 0U)
        {
            if (dst_size < tmp_li.image_size)
            {
                /* If remaining image size is less than 256KiB. */
                count++;
                even_or_odd       = count % 2U;
                tmp_li.image_size = dst_size;
                dst_size          = 0U;
            }
            else /* If remaining image size is still larger than 256KiB. */
            {
                count++;
                even_or_odd  = count % 2U;
                dst_size    -= tmp_li.image_size;
            }
            /* Load image(#count) from external flash to RT-VRAM.               *
             * Load image(#count-1) from RT-VRAM to CodeSRAM. (with decryption) */
            p_w_load_func[even_or_odd](li, &tmp_li, count, is_verify);
            /* Finish loading image(#count) from external flash to RT-VRAM. */
            load_end();
            /* Finish loading image(#count-1) from RT-VRAM to CodeSRAM. */
            load_end_for_mcu(&tmp_li, is_verify);
        }
    }
    tmp_li.src_addr = (RTVRAM_TMP_G4MH_TOP_A + (even_or_odd * MCU_TRANSFER_UNIT));
    tmp_li.boot_addr = (li->boot_addr + (MCU_TRANSFER_UNIT * (count)));

    /* Load image from RT-VRAM to CodeSRAM (with decryption). */
    image_load_for_mcu(&tmp_li, is_verify);

    /* Finish loading image(bottom) from RT-VRAM to CodeSRAM. */
    load_end_for_mcu(&tmp_li);

    /* Enable ECC error and Address parity error check for CodeSRAM. */
    enable_codesram_ecc_parity(G4MH_PRG_2_BOOT_ADDR, G4MH_PRG_2_SIZE);

    /* Verify G4MH(2nd) image. */
    mcu_img_verify(li, is_verify);

    /* Check ECC error and Address parity error detection for CodeSRAM. */
    chk_codesram_ecc_parity(G4MH_PRG_2_BOOT_ADDR, G4MH_PRG_2_SIZE);

    /* Reset CSRM registers that related CodeSRAM ECC to H/W initial value. */
    initialize_codesram_ecc_parity(G4MH_PRG_2_BOOT_ADDR, G4MH_PRG_2_SIZE);

    /* Change Bus mode of CodeSRAM to the Local Flash Bus mode. */
    mcu_set_csrm(G4MH_PRG_2_BOOT_ADDR, G4MH_PRG_2_SIZE);

    /* Release write protection of SCDS0. */
    mem_write32(MCUAXI_PBG_ERRSLV_PBGKCPROT, MCU_HBG_REL_CODE);
    mem_write32(MCU_BOOT_STAT, G4MH_LOAD_FIN_CODE);
    /* Write protection of SCDS0. */
    mem_write32(MCUAXI_PBG_ERRSLV_PBGKCPROT, MCU_HBG_PROT_CODE);

    hbg_reg_write(MCU_HBGSLVER_PFS_HBGKCPROT, MCU_HBG_PFS_HBGPROT0, 0x00000000U);
}
/* End of function load_g4mh_2nd(const LOAD_INFO *li) */

static void w_load_even(const LOAD_INFO *li, LOAD_INFO *tmp_li, uint32_t count, uint32_t is_verify)
{
    tmp_li->src_addr  = (li->src_addr + (MCU_TRANSFER_UNIT * count));
    tmp_li->boot_addr = RTVRAM_TMP_G4MH_TOP_A;

    /* Load image(#count) from external flash to RT-VRAM. */
    load_start(tmp_li);

    tmp_li->src_addr  = RTVRAM_TMP_G4MH_TOP_B;
    tmp_li->boot_addr = (li->boot_addr + (MCU_TRANSFER_UNIT * (count - 1U)));

    /* Load image(#count-1) from RT-VRAM to CodeSRAM. (with decryption) */
    image_load_for_mcu(tmp_li, is_verify);
}
/* End of function w_load_even(const LOAD_INFO *li, LOAD_INFO *tmp_li, uint32_t count) */

static void w_load_odd(const LOAD_INFO *li, LOAD_INFO *tmp_li, uint32_t count, uint32_t is_verify)
{
    tmp_li->src_addr  = (li->src_addr + (MCU_TRANSFER_UNIT * count));
    tmp_li->boot_addr = RTVRAM_TMP_G4MH_TOP_B;

    /* Load image(#count) from external flash to RT-VRAM. */
    load_start(tmp_li);

    tmp_li->src_addr  = RTVRAM_TMP_G4MH_TOP_A;
    tmp_li->boot_addr = (li->boot_addr + (MCU_TRANSFER_UNIT * (count - 1U)));

    /* Load image(#count-1) from RT-VRAM to CodeSRAM. (with decryption) */
    image_load_for_mcu(tmp_li, is_verify);
}
/* End of function w_load_even(const LOAD_INFO *li, LOAD_INFO *tmp_li, uint32_t count) */

static void mcu_key_cert_check(LOAD_INFO *li, uint32_t is_verify)
{
#if (MCU_SECURE_BOOT == MCU_SEC_BOOT_ENABLE)
    if (is_verify != NORMAL_BOOT)
    {
        r_mcu_key_cert_check_api(li);
    }
#endif /* (MCU_SECURE_BOOT == MCU_SEC_BOOT_ENABLE) */
}
/* End of function mcu_key_cert_check(LOAD_INFO *li, uint32_t is_verify). */

static void mcu_cnt_cert_check(const LOAD_INFO *li, uint32_t is_verify)
{
#if (MCU_SECURE_BOOT == MCU_SEC_BOOT_ENABLE)
    if (is_verify != NORMAL_BOOT)
    {
        r_mcu_cnt_cert_check_api(li);
    }
#endif /* (MCU_SECURE_BOOT == MCU_SEC_BOOT_ENABLE) */
}
/* End of function mcu_key_cert_check(const LOAD_INFO *li, uint32_t is_verify) */

void mcu_img_decrypt(const LOAD_INFO *li)
{
#if (MCU_SECURE_BOOT == MCU_SEC_BOOT_ENABLE)
    r_mcu_img_decrypt_api(li);
#endif /* (MCU_SECURE_BOOT == MCU_SEC_BOOT_ENABLE) */
}
/* End of function mcu_img_decrypt(const LOAD_INFO *li, uint32_t is_verify) */

void mcu_img_decrypt_end(const LOAD_INFO *li)
{
    /* Argument li is not used but compliant to MISRA Rule 2.2. */
#if (MCU_SECURE_BOOT == MCU_SEC_BOOT_ENABLE)
    r_mcu_img_decrypt_end();
#endif /* (MCU_SECURE_BOOT == MCU_SEC_BOOT_ENABLE) */
}
/* End of function mcu_img_decrypt_end(const LOAD_INFO *li) */

static void mcu_img_verify(const LOAD_INFO *li, uint32_t is_verify)
{
#if (MCU_SECURE_BOOT == MCU_SEC_BOOT_ENABLE)
    if (is_verify != NORMAL_BOOT)
    {
        r_mcu_img_verify_api(li);
        sw_version_check(li);
    }
#endif /* (MCU_SECURE_BOOT == MCU_SEC_BOOT_ENABLE) */
}
/* End of function mcu_img_verify(const LOAD_INFO *li, uint32_t is_verify) */

void mcu_load_main(const LOAD_INFO *li, uint32_t is_verify)
{
    load_g4mh_1st(&li[G4MH_PROGRAM_ID], is_verify);        /* Load G4MH(1st) image. */
    load_icumh(&li[ICUMH_PROGRAM_ID], is_verify);          /* Load ICUMH image.     */
    load_g4mh_2nd(&li[G4MH_PROGRAM_ID + 1U], is_verify);   /* Load G4MH(2nd) image. */
}
/* End of function mcu_load_main(const LOAD_INFO *li, uint32_t is_verify) */

