/* SPDX-License-Identifier: LGPL-2.1-only OR BSD-3-Clause */
/******************************************************************************
 *
 * This file is provided under a dual license.  When you use or
 * distribute this software, you may choose to be licensed under
 * version 2.1 of the GNU Lesser General Public License
 * ("LGPLv2.1 License") or BSD License.
 *
 * LGPLv2.1 License
 *
 * Copyright(C) 2019 MediaTek Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of version 2.1 of the GNU Lesser General Public
 * License as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html for more
 * details.
 *
 * BSD LICENSE
 *
 * Copyright(C) 2019 MediaTek Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  * Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *  * Neither the name of the copyright holder nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 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:
 *         The main routine of boot preloader
 *
 *---------------------------------------------------------------------------*/

#include "x_typedef.h"
#include "x_bim.h"
#include "x_hal_5381.h"
#include "x_ldr_env.h"
#include "x_hal_arm.h"
#include "x_dram.h"
#include "loader_imghdr.h"
#ifdef CC_LZMA_DECODE
#include "../lzma/LzmaEnc.h"
#include "../lzma/LzmaLib.h"
#endif
#include "gcpu_if.h"
//#include "sha256.h"

//---------------------------------------------------------------------
// External declarations
//---------------------------------------------------------------------
#ifndef CC_LOADER_MEM_OFFSET
#define CC_LOADER_MEM_OFFSET            (0xd00000)
#endif /* CC_LOADER_MEM_OFFSET */

#if defined(CC_MTK_PRELOADER) && defined(NDEBUG)
EXTERN void CHIP_DisplayString(const CHAR *szString);
EXTERN void CHIP_DisplayHex(UINT32);
EXTERN void CHIP_DisplayChar(UINT32);
#else /* defined(CC_MTK_PRELOADER) && defined(NDEBUG) */
#define CHIP_DisplayString(x)       Printf("%s", x);
#define CHIP_DisplayHex(x)          Printf("%08x", x);
#define CHIP_DisplayChar(x)         Printf("%c", x);
EXTERN INT32 Printf(const CHAR *format, ...);
#endif /* defined(CC_MTK_PRELOADER) && defined(NDEBUG) */
EXTERN UINT32 INT_Reset;
EXTERN UINT32 INT_Vectors;

#if !defined(__GCC__)
EXTERN UINT32 Image$$DRAMTAG$$Limit;
#else /*__KERNEL__*/
EXTERN UINT32 __bss_start;
EXTERN UINT32 __dramtag_end;
#endif /*__KERNEL__*/

//---------------------------------------------------------------------
// Internal declarations
//---------------------------------------------------------------------
#if defined(CC_NAND_BOOT)
extern BOOL _fgNandInit;
void CHIP_NANDCopyToDRAMLZHS(UINT32 *pu4DstBuf, UINT32 u4ByteOffset, UINT32 u4Len);
#endif /*CC_NAND_BOOT*/

#if defined(CC_EMMC_BOOT)
extern BOOL _fgMsdcInit;
void CHIP_eMMCCopyToDRAM(UINT32 *pu4DstBuf, UINT64 u8ByteOffset, UINT32 u4Len);
#endif /*CC_EMMC_BOOT*/

//---------------------------------------------------------------------
// Private functions
//---------------------------------------------------------------------

//---------------------------------------------------------------------
// Public functions
//---------------------------------------------------------------------

void __aeabi_unwind_cpp_pr1(void)
{
	 return;
}
void __aeabi_unwind_cpp_pr0(void)
{
    return;
}

VOID LinkSymbol(UINT32 fgDisplay)
{
    if (fgDisplay)
    {
        CHIP_DisplayHex(INT_Reset);
        CHIP_DisplayHex(INT_Vectors);
    }
}

VOID ShowHex(const CHAR *pu1Str, UINT32 u4Hex)
{
    CHIP_DisplayString(pu1Str);
    CHIP_DisplayHex(u4Hex);
    CHIP_DisplayChar('\n');
}

#ifdef CC_LZMA_DECODE
VOID CMain()
{
#ifdef CC_SECURE_BOOT
		UINT32 u4BSSLimit = PRELOADER_MAX_SIZE;
#else
		UINT32 u4BSSLimit = (UINT32)0x9800;
#endif
    PFN_IMAGE pfnImage;
    UINT32 u4DstSize, u4DstAddr, u4SrcSize, u4SrcAddr, u4ImageAddr;
    LZMA_FILE_HEADER* prLzmaHdr;
    LDR_ENV_T* prLdrEnv = (LDR_ENV_T*)CC_LDR_ENV_OFFSET;

    u4ImageAddr = (((u4BSSLimit + 0x3f) & 0xffffffc0) + 0x40);
    u4ImageAddr += 0xf8000000;
    ShowHex("LZMA loader flash addr:", u4ImageAddr);
    prLzmaHdr = (LZMA_FILE_HEADER*)u4ImageAddr;

    u4DstSize = prLzmaHdr->org_size;
    u4DstAddr = CC_LOADER_MEM_OFFSET;
    u4SrcAddr = u4ImageAddr + sizeof(LZMA_FILE_HEADER);
    u4SrcSize = prLzmaHdr->compressed_size;

	ShowHex("u4DstSize:", u4DstSize);
	ShowHex("u4DstAddr:", u4DstAddr);
	ShowHex("u4SrcAddr:", u4SrcAddr);
	ShowHex("u4SrcSize:", u4SrcSize);

    // store loader size,address for loader usage
    prLdrEnv->u4LoaderPlainSize = u4DstSize;
    prLdrEnv->u4LoaderLzhsSize = u4SrcSize;
    prLdrEnv->u4LoaderLzhsAddress = (((u4BSSLimit + 0x3f) & 0xffffffc0) + 0x40);

    HalInitMMU(TCMGET_CHANNELA_SIZE() * 0x100000 - 0x4000);

    UINT32 u4Ret = (UINT32)LzmaUncompress((unsigned char*)u4DstAddr, (size_t*)&u4DstSize,
        (unsigned char*)u4SrcAddr, (size_t*)&u4SrcSize,
        (unsigned char *)&prLzmaHdr->props, sizeof(CLzmaEncProps));

    HalFlushDCache();
    HalInvalidateAllCaches();
    HalDisableMMU();

    if (u4Ret != SZ_OK)
    {
        ShowHex("LZMA fail, code=%d\n", u4Ret);
        while (1) {}
    }
    pfnImage = (PFN_IMAGE)(CC_LOADER_MEM_OFFSET);
    pfnImage();
}

#else //CC_LZMA_DECODE

#define DRAM_BUFFER 0x100000
#define LZHS_HEADER 0x100
#define LAODER_PADDING 0x200

VOID CMain()
{

#ifdef CC_SECURE_BOOT
    UINT32 u4BSSLimit = PRELOADER_MAX_SIZE;
#else
    #ifdef __GCC__
        #ifdef CC_DRAM_TUNING
        UINT32 u4BSSLimit = (UINT32)&__dramtag_end;
        #else
        UINT32 u4BSSLimit = (UINT32)&__bss_start;
        #endif
    #else
        UINT32 u4BSSLimit = (UINT32)&Image$$DRAMTAG$$Limit; //set to DRAM tag end address
    #endif

    #if defined(CC_ROM_BOOT) && (defined(CC_NAND_BOOT) || defined(CC_EMMC_BOOT)) // ROM2NOR will be same with NOR
        u4BSSLimit -= SRAM_START;
    #endif /* CC_ROM_BOOT */
#endif
    UINT32 u4ImageAddr, u4Size, u4Ret, u4DstAddr;
    UINT8 u1Checksum;
    PFN_IMAGE pfnImage;
    BOOL fgIsLzhs = TRUE;
    //debug
    //#ifdef CC_SECURE_BOOT_SCRAMBLE
    //UINT8 au1Key[16] = {0x17, 0x70, 0xA8, 0xAD, 0x18, 0x53, 0x31, 0xF4, 0x80, 0xEC, 0x7B, 0x9F, 0xDF, 0xF4, 0x53, 0x79};
    //UINT8 au1Iv[16] = {0x28, 0xEC, 0x7A, 0xA6, 0x25, 0x8D, 0x3A, 0xF7, 0x53, 0xD8, 0x77, 0xCF, 0xAA, 0x71, 0xE6, 0xD4};
    //#endif // CC_SECURE_BOOT_SCRAMBLE

    LDR_ENV_T* prLdrEnv = (LDR_ENV_T*)CC_LDR_ENV_OFFSET;

    LinkSymbol(0);
    CHIP_DisplayHex(u4BSSLimit);
    CHIP_DisplayChar('\n');

#if defined(CC_NAND_BOOT) || defined(CC_EMMC_BOOT)
    // copy loader.bin.lzhs to dram.
    u4BSSLimit = ((u4BSSLimit + 0x3f) & 0xffffffc0);
    #if defined(CC_NAND_BOOT)
        CHIP_DisplayString("Nand boot\n");
        _fgNandInit = FALSE;
        CHIP_NANDCopyToDRAMLZHS((UINT32 *)DRAM_BUFFER, u4BSSLimit, LZHS_HEADER);
    #else
        CHIP_DisplayString("EMMC boot\n");
        _fgMsdcInit = FALSE;
        CHIP_eMMCCopyToDRAM((UINT32 *)DRAM_BUFFER, u4BSSLimit, LZHS_HEADER);
    #endif
    u4ImageAddr = (DRAM_BUFFER + 0x40);        //64 bytes image Header
    ShowHex("LZHS addr:", u4ImageAddr);
    #ifdef CC_SECURE_BOOT_SCRAMBLE
        GCPU_LoaderInit(0);
        // debug
        //GCPU_SetAesKey(au1Key, KEY_BIT_LEN_128, au1Iv);
        GCPU_AesDecrypt(u4ImageAddr, u4ImageAddr, LZHS_HEADER - 0x40);
    #endif // CC_SECURE_BOOT_SCRAMBLE
#else
    CHIP_DisplayString("Nor boot\n");
    u4ImageAddr = (((u4BSSLimit + 0x3f) & 0xffffffc0) + 0x40);
    ShowHex("LZHS addr:", u4ImageAddr);
    u4ImageAddr += 0xf8000000;
#endif

    u4Size = (*((UINT32 *)u4ImageAddr));

	if(u4Size == *((UINT32 *)(u4ImageAddr + 4)))
	{
		fgIsLzhs = FALSE;
        u4DstAddr = CC_LOADER_MEM_OFFSET - 0x50;
	}
	else
	{
		fgIsLzhs = TRUE;
        ShowHex("LZHS size:", u4Size);
        u1Checksum = (*((UINT8 *)(u4ImageAddr + 8)));
        ShowHex("LZHS checksum:", (UINT32)u1Checksum);
        u4DstAddr = DRAM_BUFFER;
	}

    // store loader size,address for loader usage
    prLdrEnv->u4LoaderPlainSize = u4Size;
    prLdrEnv->u4LoaderLzhsSize = *((UINT32 *)(u4ImageAddr + 4));
    prLdrEnv->u4LoaderLzhsAddress = ((u4BSSLimit + 0x3f) & ~0x3f) + 0x40 + 0x10;
    ShowHex("LZHS size:", u4Size);

    #if defined(CC_NAND_BOOT)
        CHIP_NANDCopyToDRAMLZHS((UINT32 *)u4DstAddr, u4BSSLimit, prLdrEnv->u4LoaderLzhsSize + LAODER_PADDING + LZHS_HEADER);
    #elif defined(CC_EMMC_BOOT)
        CHIP_eMMCCopyToDRAM((UINT32 *)u4DstAddr, u4BSSLimit, prLdrEnv->u4LoaderLzhsSize + LAODER_PADDING + LZHS_HEADER);
    #endif
    #ifdef CC_SECURE_BOOT_SCRAMBLE
        GCPU_AesDecrypt(u4DstAddr + 0x40, u4DstAddr + 0x40, prLdrEnv->u4LoaderLzhsSize + LAODER_PADDING + LZHS_HEADER - 0x40);
    #endif // CC_SECURE_BOOT_SCRAMBLE
    #ifdef CC_SECURE_BOOT_V2
        #if defined(CC_MTK_LOADER) && defined(CC_SECURE_BOOT_ALL)
        if (BIM_IS_SECURE_BOOT)
        {
            BIM_VerifyLoaderSig(u4DstAddr + 0x40);
        }
        #endif  // defined(CC_MTK_LOADER) && defined(CC_SECURE_BOOT_ALL)
    #endif // CC_SECURE_BOOT_V2

    BIM_SetTimeLog(1);
    if(!fgIsLzhs)
    {
    	CHIP_DisplayString("plaintext boot\n");
        pfnImage = (PFN_IMAGE)(u4DstAddr + 0x50);
        pfnImage();
    }
    else
    {
    	CHIP_DisplayString("LZHS begin \n");
        u4Ret = LZHS_Dec(u4ImageAddr+0x10, CC_LOADER_MEM_OFFSET, u4Size, &u1Checksum);
        if (u4Ret == 2)
        {
            CHIP_DisplayString("not secure loader\n");
        }
        else if (u4Ret != 0)
        {
            CHIP_DisplayString("LZHS failed\n");
        }
        else
        {
            CHIP_DisplayString("LZHS ok\n");
        }
    }

    while (1) { }
}
#endif //CC_LZMA_DECODE

