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

/*-----------------------------------------------------------------------------
 *
 * $Author: xiaojun.zhu $
 * $Date: 2015/08/08 $
 * $RCSfile: dmx_dtcp_api.c,v $
 * $Revision: #2 $
 *
 *---------------------------------------------------------------------------*/

/** @file dmx_mm_api.c
 *  Demux program stream driver
 */

//-----------------------------------------------------------------------------
// Include files
//-----------------------------------------------------------------------------
#include "dmx_mm.h"
#include "dmx_debug.h"
#include "dmx_drm_drvif.h"
#include "dmx_if.h"
#include "x_hal_arm.h"

#include "gcpu_if.h"

#include "x_assert.h"
#include "x_os.h"
#include "x_hal_926.h"
#include "x_hal_5381.h"
#include "x_rand.h"

#ifndef CC_MTK_LOADER
#include "dmx_drm_drvif.h"
#endif
//-----------------------------------------------------------------------------
// Configurations
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// Constant definitions
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// Type definitions
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// Macro definitions
//-----------------------------------------------------------------------------
//#define SNAPSHOT_DEBUG
#define GCPU_SHA_1_HASHVALUE_SIZE   (20) //(160 bit)
#define AES_KEY_SIZE				(16)

//-----------------------------------------------------------------------------
// Static variables
//-----------------------------------------------------------------------------
static AES_PARAM_T _arAesStruct[AES_MAX_ENGINE_INDEX];
static BOOL _afgAesUsed[AES_MAX_ENGINE_INDEX];
static GCPU_STRUCT_T *prGcpuMtd = NULL;
static UINT8   _au1Hash[32] = {0};
static UINT32  _au4CompleteSize = 0x0;

//-----------------------------------------------------------------------------
// Static functions
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// Extern functions
//-----------------------------------------------------------------------------
EXTERN BOOL _MtkAesDecryption(UPTR u4DstAddr, UPTR u4SrcAddr, USIZE u4Size);

//-----------------------------------------------------------------------------
/** _DMX_GetAesStruct
*/
//-----------------------------------------------------------------------------
AES_PARAM_T* _DMX_GetAesStruct(UINT8 u1Idx)
{
    ASSERT(u1Idx < AES_MAX_ENGINE_INDEX);

    return &_arAesStruct[u1Idx];
}

//-----------------------------------------------------------------------------
// Inter-file functions
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
/** _DMX_DTCP_SetAES
*/
//-----------------------------------------------------------------------------
BOOL _DMX_DTCP_SetAES(UINT32 u4Flags, const DMX_MM_DRM_AES_T *prDRM)
{
    return _DMX_AES_SetDecryptInfo(u4Flags, prDRM);
}


//-----------------------------------------------------------------------------
/** _DMX_DTCP_MoveData
*/
//-----------------------------------------------------------------------------
BOOL _DMX_DTCP_MoveData(const DMX_DTCP_DATA_T *prData)
{
    return _DMX_AES_MoveData((DMX_AES_DATA_T *)prData);
}


//-----------------------------------------------------------------------------
/** _DMX_DTCP_MoveBlockData
*/
//-----------------------------------------------------------------------------
BOOL _DMX_DTCP_MoveBlockData(const DMX_DTCP_BLKDATA_T *prBlkData)
{
    return _DMX_AES_MoveBlockData((DMX_AES_BLKDATA_T *)prBlkData);
}


//-----------------------------------------------------------------------------
/** _DMX_AES_SetDecryptInfo
*   Use DTCP as general AES decryption engine
*/
//-----------------------------------------------------------------------------
BOOL _DMX_AES_SetDecryptInfo(UINT32 u4Flags, const DMX_MM_DRM_AES_T *prDRM)
{
    AES_PARAM_T *prParam;

    if(prDRM == NULL)
    {
        return FALSE;
    }

    if (u4Flags == DMX_MM_DRM_FLAG_NONE)
    {
        return TRUE;
    }

    if ((u4Flags & DMX_MM_DRM_FLAG_OFFSET) != 0)
    {
        if(prDRM->u4Offset > 0)     // Parser hardware limitation
        {
            ASSERT(0);
        }
    }

    prParam = _DMX_GetAesStruct(AES_LEGACY_INDEX);

    if ((u4Flags & DMX_MM_DRM_FLAG_CBC) != 0)
    {
        prParam->eMode = (prDRM->fgCBC)?BLOCK_CIPHER_MODE_CBC:BLOCK_CIPHER_MODE_ECB;
        prParam->fgEncrypt = prDRM->fgEncrypt;
        x_memcpy(prParam->au1Iv, prDRM->au1IV, 16);
    }

    if ((u4Flags & DMX_MM_DRM_FLAG_KEY) != 0)
    {
        prParam->eKeyBitLen = (KEY_BIT_LEN)(prDRM->u4KeyBitLen & 0xFFFF);
        x_memcpy(prParam->au1Key, prDRM->au1Key, 32);
    }

    if ((u4Flags & DMX_MM_DRM_FLAG_ENCRYPT) != 0)
    {
        prParam->fgEncrypt = prDRM->fgEncrypt;
    }

    return TRUE;
}


//-----------------------------------------------------------------------------
/** _DMX_AES_MoveData
*   Use DTCP as general AES decryption engine
*/
//-----------------------------------------------------------------------------
BOOL _DMX_AES_MoveData(const DMX_AES_DATA_T *prData)
{
    AES_PARAM_T *prParam;

    if(prData == NULL)
    {
        return FALSE;
    }

    prParam = _DMX_GetAesStruct(AES_LEGACY_INDEX);

    prParam->u4SrcStartAddr = prData->u4SrcStart;
    prParam->u4SrcBufStart = prData->u4SrcBufStart;
    prParam->u4SrcBufEnd = prData->u4SrcBufEnd;
    prParam->u4DstStartAddr = prData->u4DstStart;
    prParam->u4DstBufStart = prData->u4DstBufStart;
    prParam->u4DstBufEnd = prData->u4DstBufEnd;
    prParam->u4DatLen = prData->u4Size;

    return (GCPU_Cmd(0, GCPU_AES, prParam) == S_GCPU_OK)?TRUE:FALSE;
}


//-----------------------------------------------------------------------------
/** _DMX_AES_MoveBlockData
*   Use DTCP as general AES decryption engine
*/
//-----------------------------------------------------------------------------
BOOL _DMX_AES_MoveBlockData(const DMX_AES_BLKDATA_T *prBlkData)
{
    AES_PARAM_T *prParam;

    if(prBlkData == NULL)
    {
        return FALSE;
    }

    prParam = _DMX_GetAesStruct(AES_LEGACY_INDEX);

    prParam->u4SrcStartAddr = prBlkData->u4SrcAddr;
    prParam->u4SrcBufStart = GCPU_LINER_BUFFER_START(prBlkData->u4SrcAddr);
    prParam->u4SrcBufEnd = GCPU_LINER_BUFFER_END(prBlkData->u4SrcAddr + prBlkData->u4Size);
    prParam->u4DstStartAddr = prBlkData->u4DstAddr;
    prParam->u4DstBufStart = GCPU_LINER_BUFFER_START(prBlkData->u4DstAddr);
    prParam->u4DstBufEnd = GCPU_LINER_BUFFER_END(prBlkData->u4DstAddr + prBlkData->u4Size);
    prParam->u4DatLen = prBlkData->u4Size;

    return (GCPU_Cmd(0, GCPU_AES, prParam) == S_GCPU_OK)?TRUE:FALSE;
}


//-----------------------------------------------------------------------------
/** _DMX_AES_AllocEngine
*/
//-----------------------------------------------------------------------------
UINT8 _DMX_AES_AllocEngine()
{
    UINT8 i;

    for(i = AES_RESERVED_ENGINE_NUM; i < AES_MAX_ENGINE_INDEX; i++)
    {
        if(!_afgAesUsed[i])
        {
            break;
        }
    }

    if(i >= AES_MAX_ENGINE_INDEX)
    {
        return AES_NULL_ENGINE_INDEX;
    }

    _afgAesUsed[i] = TRUE;

    return i;
}


//-----------------------------------------------------------------------------
/** _DMX_AES_FreeEngine
*/
//-----------------------------------------------------------------------------
BOOL _DMX_AES_FreeEngine(UINT8 u1Idx)
{
    if(u1Idx < AES_RESERVED_ENGINE_NUM || u1Idx >= AES_MAX_ENGINE_INDEX)
    {
        return FALSE;
    }

    _afgAesUsed[u1Idx] = FALSE;

    return TRUE;
}


//-----------------------------------------------------------------------------
/** _DMX_AES_SetDecryptInfo
*   Use DTCP as general AES decryption engine
*/
//-----------------------------------------------------------------------------
BOOL _DMX_AES_SetDecryptInfoEx(UINT8 u1Idx, UINT32 u4Flags, const DMX_MM_DRM_AES_T *prDRM)
{
    if (prDRM == NULL)
    {
        return FALSE;
    }

    if (u4Flags == DMX_MM_DRM_FLAG_NONE)
    {
        return TRUE;
    }

    if (u1Idx >= AES_MAX_ENGINE_INDEX)
    {
        return FALSE;
    }

    if ((u4Flags & DMX_MM_DRM_FLAG_OFFSET) != 0)
    {
        if(prDRM->u4Offset > 0)     // Parser hardware limitation
        {
            ASSERT(0);
        }
    }

    if ((u4Flags & DMX_MM_DRM_FLAG_CBC) != 0)
    {
        _arAesStruct[u1Idx].eMode = (prDRM->fgCBC)?BLOCK_CIPHER_MODE_CBC:BLOCK_CIPHER_MODE_ECB;
        x_memcpy(_arAesStruct[u1Idx].au1Iv, prDRM->au1IV, 16);
    }

    if ((u4Flags & DMX_MM_DRM_FLAG_KEY) != 0)
    {
        // Always set to Key 0
        _arAesStruct[u1Idx].eKeyBitLen = (KEY_BIT_LEN)(prDRM->u4KeyBitLen & 0xFFFF);
        x_memcpy(_arAesStruct[u1Idx].au1Key, prDRM->au1Key, 32);
    }

    if ((u4Flags & DMX_MM_DRM_FLAG_ENCRYPT) != 0)
    {
        _arAesStruct[u1Idx].fgEncrypt = prDRM->fgEncrypt;
    }

    return TRUE;
}


//-----------------------------------------------------------------------------
/** _DMX_AES_MoveDataEx
*   Use DTCP as general AES decryption engine
*/
//-----------------------------------------------------------------------------
BOOL _DMX_AES_MoveDataEx(UINT8 u1Idx, const DMX_AES_DATA_T *prData)
{
    BOOL fgRet;

    if(prData == NULL)
    {
        return FALSE;
    }

    if (u1Idx >= AES_MAX_ENGINE_INDEX)
    {
        return FALSE;
    }

    _arAesStruct[u1Idx].u4SrcStartAddr = prData->u4SrcStart;
    _arAesStruct[u1Idx].u4SrcBufStart = prData->u4SrcBufStart;
    _arAesStruct[u1Idx].u4SrcBufEnd = prData->u4SrcBufEnd;
    _arAesStruct[u1Idx].u4DstStartAddr = prData->u4DstStart;
    _arAesStruct[u1Idx].u4DstBufStart = prData->u4DstBufStart;
    _arAesStruct[u1Idx].u4DstBufEnd = prData->u4DstBufEnd;
    _arAesStruct[u1Idx].u4DatLen = prData->u4Size;

    fgRet = (GCPU_Cmd(0, GCPU_AES, &_arAesStruct[u1Idx]) == S_GCPU_OK)?TRUE:FALSE;

    return fgRet;
}


//-----------------------------------------------------------------------------
/** _DMX_AES_MoveDataEx
*   Use DTCP as general AES decryption engine
*/
//-----------------------------------------------------------------------------
BOOL _DMX_SW_AES_MoveDataEx(UINT8 u1Idx, const DMX_AES_DATA_T *prData)
{
    BOOL fgRet;

    if(prData == NULL)
    {
        return FALSE;
    }

    if (u1Idx >= AES_MAX_ENGINE_INDEX)
    {
        return FALSE;
    }

    _arAesStruct[u1Idx].u4SrcStartAddr = prData->u4SrcStart;
    _arAesStruct[u1Idx].u4SrcBufStart = prData->u4SrcBufStart;
    _arAesStruct[u1Idx].u4SrcBufEnd = prData->u4SrcBufEnd;
    _arAesStruct[u1Idx].u4DstStartAddr = prData->u4DstStart;
    _arAesStruct[u1Idx].u4DstBufStart = prData->u4DstBufStart;
    _arAesStruct[u1Idx].u4DstBufEnd = prData->u4DstBufEnd;
    _arAesStruct[u1Idx].u4DatLen = prData->u4Size;

    fgRet = (GCPU_CmdSw(0, GCPU_AES, &_arAesStruct[u1Idx]) == S_GCPU_OK)?TRUE:FALSE;

    return fgRet;
}


//-----------------------------------------------------------------------------
/** _DMX_AES_MoveBlockDataEx
*   Use DTCP as general AES decryption engine
*/
//-----------------------------------------------------------------------------
BOOL _DMX_AES_MoveBlockDataEx(UINT8 u1Idx, const DMX_AES_BLKDATA_T *prBlkData)
{
    BOOL fgRet;

    if(prBlkData == NULL)
    {
        return FALSE;
    }

    if (u1Idx >= AES_MAX_ENGINE_INDEX)
    {
        return FALSE;
    }

    _arAesStruct[u1Idx].u4SrcStartAddr = prBlkData->u4SrcAddr;
    _arAesStruct[u1Idx].u4SrcBufStart = GCPU_LINER_BUFFER_START(prBlkData->u4SrcAddr);
    _arAesStruct[u1Idx].u4SrcBufEnd = GCPU_LINER_BUFFER_END(prBlkData->u4SrcAddr + prBlkData->u4Size);
    _arAesStruct[u1Idx].u4DstStartAddr = prBlkData->u4DstAddr;
    _arAesStruct[u1Idx].u4DstBufStart = GCPU_LINER_BUFFER_START(prBlkData->u4DstAddr);
    _arAesStruct[u1Idx].u4DstBufEnd = GCPU_LINER_BUFFER_END(prBlkData->u4DstAddr + prBlkData->u4Size);
    _arAesStruct[u1Idx].u4DatLen = prBlkData->u4Size;

    fgRet = (GCPU_Cmd(0, GCPU_AES, &_arAesStruct[u1Idx]) == S_GCPU_OK)?TRUE:FALSE;

    return fgRet;
}


//-----------------------------------------------------------------------------
/** DMX_Marlin_SetDecryptInfo
*/
//-----------------------------------------------------------------------------
BOOL _DMX_Marlin_SetDecryptInfo(UINT8 u1Idx, UINT32 u4Flags, const DMX_MM_DRM_AES_T *prDRM)
{
    #ifndef CC_MTK_LOADER
    // decrypt the marlin key by MTK secure key
    _MtkAesDecryption((UPTR)prDRM->au1Key, (UPTR)prDRM->au1Key, prDRM->u4KeyBitLen / 8);
    #endif

    return _DMX_AES_SetDecryptInfoEx(u1Idx, u4Flags, prDRM);
}


//-----------------------------------------------------------------------------
/** _DMX_Marlin_MoveData
*/
//-----------------------------------------------------------------------------
BOOL _DMX_Marlin_MoveData(UINT8 u1Idx, const DMX_DTCP_DATA_T *prData)
{
    BOOL fgRet = FALSE;
#ifndef CC_MTK_LOADER
    UINT32 u4ResidualLen, u4TmpAddr;
    UINT8 au1ResidualIv[AES_BLOCK_LEN], au1ResidualInput[AES_BLOCK_LEN], au1ResidualOutput[AES_BLOCK_LEN];

    if(prData == NULL)
    {
        return FALSE;
    }

    if (u1Idx >= AES_MAX_ENGINE_INDEX)
    {
        return FALSE;
    }

    u4ResidualLen = (prData->u4Size) % AES_BLOCK_LEN;

    if(u4ResidualLen > 0)
    {
        // prepare residual iv for decryption
        if(_arAesStruct[u1Idx].fgEncrypt == FALSE)
        {
            u4TmpAddr = ADDR_INCR_IN_RING(prData->u4SrcStart,
                prData->u4Size - AES_BLOCK_LEN - u4ResidualLen,
                prData->u4SrcBufStart, prData->u4SrcBufEnd);
            UNUSED(_DMX_CopyRingBuffer((UPTR)au1ResidualIv, (UPTR)au1ResidualIv,
                (UPTR)(au1ResidualIv + AES_BLOCK_LEN), u4TmpAddr, prData->u4SrcBufStart,
                prData->u4SrcBufEnd, AES_BLOCK_LEN));
        }

        // no matter encrypt or decrypt, we need to save residual source input
        u4TmpAddr = ADDR_INCR_IN_RING(prData->u4SrcStart,
            prData->u4Size - u4ResidualLen,
            prData->u4SrcBufStart, prData->u4SrcBufEnd);
        UNUSED(_DMX_CopyRingBuffer((UPTR)au1ResidualInput, (UPTR)au1ResidualInput,
            (UPTR)(au1ResidualInput + AES_BLOCK_LEN), u4TmpAddr, prData->u4SrcBufStart,
            prData->u4SrcBufEnd, u4ResidualLen));
    }

    fgRet = _DMX_SW_AES_MoveDataEx(u1Idx, (DMX_AES_DATA_T *)prData);
    if(fgRet != TRUE)
    {
        LOG(5, "%s: fail to move marlin data\n", __FUNCTION__);
        return fgRet;
    }

    // prepare residual iv for encryption
    if((u4ResidualLen > 0) && (_arAesStruct[u1Idx].fgEncrypt == TRUE))
    {
        u4TmpAddr = ADDR_INCR_IN_RING(prData->u4DstStart,
            prData->u4Size - AES_BLOCK_LEN - u4ResidualLen,
            prData->u4DstBufStart, prData->u4DstBufEnd);
        UNUSED(_DMX_CopyRingBuffer((UPTR)au1ResidualIv, (UPTR)au1ResidualIv,
            (UPTR)(au1ResidualIv + AES_BLOCK_LEN), u4TmpAddr, prData->u4DstBufStart,
            prData->u4DstBufEnd, AES_BLOCK_LEN));
    }

    // residual termination block processing...
    if((u4ResidualLen > 0) && (_arAesStruct[u1Idx].fgEncrypt == FALSE))
    {
        aes_context rCtx;
        if(_arAesStruct[u1Idx].fgEncrypt == FALSE)
        {
            aes_setkey_dec(&rCtx, _arAesStruct[u1Idx].au1Key, _arAesStruct[u1Idx].eKeyBitLen);
        }
        else
        {
            aes_setkey_enc(&rCtx, _arAesStruct[u1Idx].au1Key, _arAesStruct[u1Idx].eKeyBitLen);
        }
        aes_crypt_cbc_scte52(&rCtx, AES_ENCRYPT,AES_BLOCK_LEN, au1ResidualIv,
            au1ResidualInput, au1ResidualOutput);

        u4TmpAddr = ADDR_INCR_IN_RING(prData->u4DstStart,
            prData->u4Size - u4ResidualLen,
            prData->u4DstBufStart, prData->u4DstBufEnd);
        UNUSED(_DMX_CopyRingBuffer(u4TmpAddr, prData->u4DstBufStart,
            prData->u4DstBufEnd, (UPTR)au1ResidualOutput, (UPTR)au1ResidualOutput,
            (UPTR)(au1ResidualOutput + AES_BLOCK_LEN), u4ResidualLen));
    }
#endif
    return fgRet;
}


BOOL _DMX_NAND_AES_INIT(UINT8 *pu1Key, UINT16 u2KeyLen)
{
    // only use AES 128 bit key
    if(u2KeyLen > 128)
    {
        return FALSE;
    }

    GCPU_Init(0);
    if(prGcpuMtd == NULL)
    {
        prGcpuMtd = (GCPU_STRUCT_T *)GCPU_CreateHandle();
    }
    _arAesStruct[AES_MTD_INDEX].eMode = BLOCK_CIPHER_MODE_ECB;
    _arAesStruct[AES_MTD_INDEX].eKeyBitLen = KEY_BIT_LEN_128;
    prGcpuMtd->u4AesKeyPtr = AES_MTD_SECURE_KEY_PTR;

    #if defined(CC_MTK_LOADER) && !defined(CC_UBOOT)
    GCPU_SetSecureSlot(prGcpuMtd->u4AesKeyPtr, pu1Key, 16);
    #endif

    return TRUE;
}


//-----------------------------------------------------------------------------
/** _DMX_NAND_AES_Encryption
 */
//-----------------------------------------------------------------------------
BOOL _DMX_NAND_AES_Encryption(UPTR u4InBufStart, UPTR u4OutBufStart, USIZE u4BufSize)
{
    #if !defined(CC_MTK_LOADER) || defined(CC_UBOOT)
    _DMX_NAND_AES_INIT(NULL, 128);
    #endif

    _arAesStruct[AES_MTD_INDEX].u4SrcStartAddr = u4InBufStart;
    _arAesStruct[AES_MTD_INDEX].u4SrcBufStart = GCPU_LINER_BUFFER_START(u4InBufStart);
    _arAesStruct[AES_MTD_INDEX].u4SrcBufEnd = GCPU_LINER_BUFFER_END(u4InBufStart + u4BufSize);
    _arAesStruct[AES_MTD_INDEX].u4DstStartAddr = u4OutBufStart;
    _arAesStruct[AES_MTD_INDEX].u4DstBufStart = GCPU_LINER_BUFFER_START(u4OutBufStart);
    _arAesStruct[AES_MTD_INDEX].u4DstBufEnd = GCPU_LINER_BUFFER_END(u4OutBufStart + u4BufSize);
    _arAesStruct[AES_MTD_INDEX].u4DatLen = u4BufSize;
    _arAesStruct[AES_MTD_INDEX].fgEncrypt = TRUE;

    return (GCPU_Cmd((UPTR)prGcpuMtd, GCPU_AES, &_arAesStruct[AES_MTD_INDEX]) == S_GCPU_OK)?TRUE:FALSE;
}


//-----------------------------------------------------------------------------
/** _DMX_NAND_AES_Decryption
 */
//-----------------------------------------------------------------------------

BOOL _DMX_NAND_AES_Decryption(UPTR u4InBufStart, UPTR u4OutBufStart, USIZE u4BufSize)
{
    #if !defined(CC_MTK_LOADER) || defined(CC_UBOOT)
    _DMX_NAND_AES_INIT(NULL, 128);
    #endif

    _arAesStruct[AES_MTD_INDEX].u4SrcStartAddr = u4InBufStart;
    _arAesStruct[AES_MTD_INDEX].u4SrcBufStart = GCPU_LINER_BUFFER_START(u4InBufStart);
    _arAesStruct[AES_MTD_INDEX].u4SrcBufEnd = GCPU_LINER_BUFFER_END(u4InBufStart + u4BufSize);
    _arAesStruct[AES_MTD_INDEX].u4DstStartAddr = u4OutBufStart;
    _arAesStruct[AES_MTD_INDEX].u4DstBufStart = GCPU_LINER_BUFFER_START(u4OutBufStart);
    _arAesStruct[AES_MTD_INDEX].u4DstBufEnd = GCPU_LINER_BUFFER_END(u4OutBufStart + u4BufSize);
    _arAesStruct[AES_MTD_INDEX].u4DatLen = u4BufSize;
    _arAesStruct[AES_MTD_INDEX].fgEncrypt = FALSE;

    return (GCPU_Cmd((UPTR)prGcpuMtd, GCPU_AES, &_arAesStruct[AES_MTD_INDEX]) == S_GCPU_OK)?TRUE:FALSE;
}

//-----------------------------------------------------------------------------
/** _DMX_AES_Encryption
 */
//-----------------------------------------------------------------------------

BOOL _DMX_AES_Encryption(UPTR u4InBufStart, UPTR u4OutBufStart, USIZE u4BufSize, const UINT8 *u1KeyAddr)
{
	AES_PARAM_T rParam;
	UINT8 i;

	if (u1KeyAddr == NULL)
	{
		//Printf("Ivalid parameters!\n"); 
		return FALSE;
	}
	x_memset((void*)&rParam, 0, sizeof(rParam));

	for(i=0; i<16; i++)
    {
        rParam.au1Key[i] = *(u1KeyAddr + i);
    }
	rParam.eMode = BLOCK_CIPHER_MODE_ECB;
    rParam.eKeyBitLen = KEY_BIT_LEN_128;
    rParam.u4SrcStartAddr = u4InBufStart;
    rParam.u4SrcBufStart = GCPU_LINER_BUFFER_START(u4InBufStart);
    rParam.u4SrcBufEnd = GCPU_LINER_BUFFER_END(u4InBufStart + u4BufSize);
    rParam.u4DstStartAddr = u4OutBufStart;
    rParam.u4DstBufStart = GCPU_LINER_BUFFER_START(u4OutBufStart);
    rParam.u4DstBufEnd = GCPU_LINER_BUFFER_END(u4OutBufStart + u4BufSize);
    rParam.u4DatLen = u4BufSize;
    rParam.fgEncrypt = TRUE;

    return (GCPU_Cmd(0, GCPU_AES, &rParam) == S_GCPU_OK)?TRUE:FALSE;
}


//-----------------------------------------------------------------------------
/** _DMX_AES_Decryption
 */
//-----------------------------------------------------------------------------
BOOL _DMX_AES_Decryption(UPTR u4InBufStart, UPTR u4OutBufStart, USIZE u4BufSize, const UINT8 *u1KeyAddr)
{
	AES_PARAM_T rParam;
	UINT8 i;

	if (u1KeyAddr == NULL)
	{
		//Printf("Ivalid parameters!\n");
		return FALSE;
	}

	x_memset((void*)&rParam, 0, sizeof(rParam));

	for(i=0; i<16; i++)
    {
        rParam.au1Key[i] = *(u1KeyAddr + i);
    }
	rParam.eMode = BLOCK_CIPHER_MODE_ECB;
    rParam.eKeyBitLen = KEY_BIT_LEN_128;
    rParam.u4SrcStartAddr = u4InBufStart;
    rParam.u4SrcBufStart = GCPU_LINER_BUFFER_START(u4InBufStart);
    rParam.u4SrcBufEnd = GCPU_LINER_BUFFER_END(u4InBufStart + u4BufSize);
    rParam.u4DstStartAddr = u4OutBufStart;
    rParam.u4DstBufStart = GCPU_LINER_BUFFER_START(u4OutBufStart);
    rParam.u4DstBufEnd = GCPU_LINER_BUFFER_END(u4OutBufStart + u4BufSize);
    rParam.u4DatLen = u4BufSize;
    rParam.fgEncrypt = FALSE;

    return (GCPU_Cmd(0, GCPU_AES, &rParam) == S_GCPU_OK)?TRUE:FALSE;
}


//-----------------------------------------------------------------------------
/** _DMX_SHA1_Onece
 */
//-----------------------------------------------------------------------------
BOOL _DMX_SHA1_Onece(UPTR *u4BufStart, USIZE u4BufSize, UINT8 *u1HashValue, UINT32 *u4HashValueSize)
{

    MD_PARAM_T rParam;

	if ((u4BufStart == NULL) || (u1HashValue == NULL) || (u4HashValueSize == NULL))
	{
		//Printf("Ivalid parameters!\n");
		return FALSE;
	}

	if((BSP_GetIcVersion() == IC_VER_5396_AA) || (BSP_GetIcVersion() == IC_VER_5368_AA))
    {
        //Printf("Please use the ECO IC,Thanks!\n");
        ASSERT(0);
    }

    rParam.u4SrcStartAddr = (UPTR)u4BufStart;
    rParam.u4SrcBufStart = GCPU_LINER_BUFFER_START((UPTR)u4BufStart);
    rParam.u4SrcBufEnd = GCPU_LINER_BUFFER_END((UPTR)u4BufStart + u4BufSize);
    rParam.u4DatLen  = u4BufSize;
    rParam.fgFirstPacket = TRUE;
    rParam.fgLastPacket = TRUE;
    rParam.u8BitCnt = 0;

    GCPU_Cmd(0, GCPU_SHA_1, &rParam);

	//return hash value and hash value size
	x_memcpy(u1HashValue, rParam.au1Hash ,GCPU_SHA_1_HASHVALUE_SIZE);
	*u4HashValueSize = GCPU_SHA_1_HASHVALUE_SIZE;

    return TRUE;
}

//-----------------------------------------------------------------------------
/** _DMX_SHA1_Manytimes
	u4BufSize =  multi 64 byte
 */
//-----------------------------------------------------------------------------
BOOL _DMX_SHA1_Manytimes(BOOL fgFirstPacket, BOOL fgLastPacket, UPTR *u4BufStart,
									USIZE u4BufSize, UINT8 *u1HashValue, UINT32 *u4HashValueSize)
{

    MD_PARAM_T rParam;

	if ((u4BufStart == NULL) || (u1HashValue == NULL) || (u4HashValueSize == NULL))
	{
		//Printf("Ivalid parameters!\n");
		return FALSE;
	}

	if((BSP_GetIcVersion() == IC_VER_5396_AA) || (BSP_GetIcVersion() == IC_VER_5368_AA))
    {
        //Printf("Please use the ECO IC,Thanks!\n");
        ASSERT(0);
    }

	if (fgFirstPacket && fgLastPacket)
	{
		x_memset(_au1Hash, 0 , 32);
		_au4CompleteSize = 0;
		rParam.fgFirstPacket = TRUE;
   		rParam.fgLastPacket = TRUE;
		rParam.u8BitCnt = 0;
	}
	else if (fgFirstPacket)
	{
		if((u4BufSize  % 64) != 0)
	    {
	        //printf("%s: illegal Buffer size\n", __FUNCTION__);
	        return FALSE;
	    }
		x_memset(_au1Hash, 0 , 32);
		_au4CompleteSize += u4BufSize;
		rParam.fgFirstPacket = TRUE;
   		rParam.fgLastPacket = FALSE;
		rParam.u8BitCnt = 0;
	}
	else if(fgLastPacket)
	{
		rParam.fgFirstPacket = FALSE;
   		rParam.fgLastPacket = TRUE;
		rParam.u8BitCnt = (UINT64)(_au4CompleteSize << 3);
		x_memcpy(rParam.au1Hash, _au1Hash , 32);
		_au4CompleteSize = 0;
	}
	else
	{
		if((u4BufSize  % 64) != 0)
	    {
	        //printf("%s: illegal Buffer size\n", __FUNCTION__);
	        return FALSE;
	    }
		_au4CompleteSize += u4BufSize;
		rParam.fgFirstPacket = FALSE;
   		rParam.fgLastPacket = FALSE;
		rParam.u8BitCnt = 0;
		x_memcpy(rParam.au1Hash, _au1Hash , 32);

	}

    rParam.u4SrcStartAddr = (UPTR)u4BufStart;
    rParam.u4SrcBufStart = GCPU_LINER_BUFFER_START((UPTR)u4BufStart);
    rParam.u4SrcBufEnd = GCPU_LINER_BUFFER_END((UPTR)u4BufStart + u4BufSize);
    rParam.u4DatLen  = u4BufSize;

    GCPU_Cmd(0, GCPU_SHA_1, &rParam);

	//save the return hash value
	x_memcpy(_au1Hash, rParam.au1Hash ,32);

	//return hash value and hash value size
	if (fgLastPacket)
	{
		x_memcpy(u1HashValue, rParam.au1Hash ,32);
		*u4HashValueSize = 32;
	}

    return TRUE;
}

//-----------------------------------------------------------------------------
/** _DMX_Encrypt_Snapshot
 */
//-----------------------------------------------------------------------------
BOOL _DMX_Encrypt_Snapshot(UINT8 *u1HashValueIn, UINT32 u4HashValueSize, UINT8 *u1HashValueOut,
									UINT8 *u1AeskeyAddr, UINT32 *u4AeskeySize)
{
	UINT8 u1AesKey[16];

	#if defined(SNAPSHOT_DEBUG) || !defined(CC_MTK_LOADER)
	UINT8 i;
	#endif

	#ifndef CC_MTK_LOADER
	HAL_RAW_TIME_T rRawTime1;
	#endif

	if((u1HashValueIn == NULL) || (u1HashValueOut == NULL) ||(u1AeskeyAddr == NULL)
		|| (u4AeskeySize == NULL) || (u4HashValueSize <= 0) || (u4HashValueSize > 32))
	{
		//Printf("Ivalid parameters!\n");
		return FALSE;
	}

	if((BSP_GetIcVersion() == IC_VER_5396_AA) || (BSP_GetIcVersion() == IC_VER_5368_AA))
    {
        //Printf("Please use the ECO IC,Thanks!\n");
        ASSERT(0);
    }

    x_memset(u1AesKey, 0x0, 16);

	//Generate the random aes key
	#ifndef CC_MTK_LOADER
	HAL_GetRawTime(&rRawTime1);
	srand(rRawTime1.u4Cycles);
	for(i=0; i<16; i++)
    {
        u1AesKey[i] = (UINT8)mtk_random(256);
    }
	#endif

	#ifdef SNAPSHOT_DEBUG
	//Printf("---Random aes key:\n");
	for(i = 0; i < 16; i++)
	{
		//Printf("%02x  ",u1AesKey[i]);
		 if ((i % 8) == 7)
          {
               //Printf("\n");
          }
	}
	#endif

	#ifdef SNAPSHOT_DEBUG
	//Printf("---Input Hash value:\n");
	for(i = 0; i < 32; i++)
	{
		//Printf("%02x  ",u1HashValueIn[i]);
		 if ((i % 16) == 15)
          {
               //Printf("\n");
          }
	}
	#endif

	//Encrypt HASH value by AES key
	if(!_DMX_AES_Encryption((UPTR)u1HashValueIn, (UPTR)u1HashValueOut, 32, u1AesKey))
	{
		//Printf("_DMX_AES_Encryption return fail!\n");
		return FALSE;
	}

	#ifdef SNAPSHOT_DEBUG
	//Printf("---Encrypt HASH value by Random AES key:\n");
	for(i = 0; i < 32; i++)
	{
		//Printf("%02x  ",u1HashValueOut[i]);
		 if ((i % 16) == 15)
          {
               //Printf("\n");
          }
	}
	#endif

	//Encrypt AES key by unique key
	if(!_DMX_NAND_AES_Encryption((UPTR)u1AesKey, (UPTR)u1AeskeyAddr, AES_KEY_SIZE))
	{
		//Printf("_DMX_NAND_AES_Encryption return fail!\n");
		return FALSE;
	}

	//return AES size
	*u4AeskeySize = AES_KEY_SIZE;

	#ifdef SNAPSHOT_DEBUG
	//Printf("---Encrypt AES key by unique key:\n");
	for(i = 0; i < 16; i++)
	{
		//Printf("%02x  ",u1AeskeyAddr[i]);
		 if ((i % 8) == 7)
          {
               //Printf("\n");
          }
	}
	#endif

    return TRUE;


}

//-----------------------------------------------------------------------------
/** _DMX_Decrypt_Snapshot
 */
//-----------------------------------------------------------------------------
BOOL _DMX_Decrypt_Snapshot(UINT8 *u1HashValue_Encrypt, UINT32 u4HashValueSize, UINT8 *u1HashValue_Clear,
									UINT8 *u1AeskeyAddr, UINT32 u4AeskeySize)
{
	UINT8 u1AesKey[16]; //random aes key
	UINT8 u1HashValueAddr_R[32]= {0}; //decrypt use random aes key

	#ifdef SNAPSHOT_DEBUG
	UINT8 i;
	#endif

	if ((u1HashValue_Encrypt == NULL) || (u1HashValue_Clear == NULL) || (u1AeskeyAddr == NULL))
	{
		//Printf("Ivalid parameters!\n");
		return FALSE;
	}

	if((BSP_GetIcVersion() == IC_VER_5396_AA) || (BSP_GetIcVersion() == IC_VER_5368_AA))
    {
        //Printf("Please use the ECO IC,Thanks!\n");
        ASSERT(0);
    }

    x_memset(u1AesKey, 0x0, 16);

	//Use unique key to decrypt the AES key
	if(!_DMX_NAND_AES_Decryption((UPTR)u1AeskeyAddr, (UPTR)u1AesKey, AES_KEY_SIZE))
	{
		//Printf("_DMX_NAND_AES_Decryption return fail!\n");
		return FALSE;
	}

	#ifdef SNAPSHOT_DEBUG
	//Printf("---Use unique key to decrypt the RandomAES key:\n");
	for(i = 0; i < 16; i++)
	{
		//Printf("%02x  ",u1AesKey[i]);
		 if ((i % 8) == 7)
          {
               //Printf("\n");
          }
	}
	#endif


	//Use Random AES key to decrypt HASH value
	if(!_DMX_AES_Decryption((UPTR)u1HashValue_Encrypt, (UPTR)u1HashValueAddr_R, 32, u1AesKey))
	{
		//Printf("_DMX_AES_Decryption return fail!\n");
		return FALSE;
	}

	#ifdef SNAPSHOT_DEBUG
	//Printf("---Use AES key to decrypt HASH value:\n");
	for(i = 0; i < 20; i++)
	{
		//Printf("%02x  ",u1HashValueAddr_R[i]);
		 if ((i % 10) == 9)
          {
               //Printf("\n");
          }
	}
	#endif

	#ifdef SNAPSHOT_DEBUG
	//Printf("---Clear Hash value:\n");
	for(i = 0; i < 32; i++)
	{
		//Printf("%02x  ",u1HashValue_Clear[i]);
		 if ((i % 16) == 15)
          {
               //Printf("\n");
          }
	}
	#endif

	//Use SHA to do integrity checking of signature image
	if(x_memcmp(u1HashValue_Clear, u1HashValueAddr_R, GCPU_SHA_1_HASHVALUE_SIZE) != 0)
    {
        //Printf("Integrity check fail! \n");
        return FALSE;
    }
    else
    {
        //Printf("Integrity check pass! \n");
		return TRUE;
    }

}

