/*
  @file loadalgo_ta.c
  @brief Trusted application that is used to decrypt a signed and encrypted algorithm elf.

*/
/*===========================================================================
  Copyright (c) 2019 by Qualcomm Technologies, Incorporated.  All Rights Reserved.
  ===========================================================================*/

/*===========================================================================

  EDIT HISTORY FOR FILE
  $Header:
  $DateTime:
  $Author: vikarthi

  # when       who     what, where, why
  # --------   ---     ---------------------------------------------------------
  # 05/14/19   vi      Initial version
  ===========================================================================*/

#include <stdint.h>
#include "com_dtypes.h"
#include "stringl.h"
#include "qsee_log.h"
#include "qsee_services.h"
#include "qsee_core.h"
#include "qsee_heap.h"
#include "dsc_api.h"
#include "loadalgota.h"
#include "ipprotector_api.h"

/*===========================================================================
    DEFINITIONS
===========================================================================*/

#define GAUSSIAN_WIDTH              1920
#define GAUSSIAN_HEIGHT             1080

/*===========================================================================
    GLOBAL VARIABLES
===========================================================================*/

static uint8_t qsee_log_mask;
static dsc_feat_handle_t *g_dsc_handle = NULL;
static buffer_vmid_perms_t g_buf_vmid_perms[] = {
    {MODEL, {{IAccessControl_VM_CP_CDSP, IAccessControl_PERM_TYPE_RW}}, 1},
    {AUTH, {{IAccessControl_VM_CP_CDSP, IAccessControl_PERM_TYPE_RW}}, 1},
    {CAMERA,
     {{IAccessControl_VM_CP_CDSP, IAccessControl_PERM_TYPE_RW},
      {IAccessControl_VM_CP_CAMERA, IAccessControl_PERM_TYPE_RW}},
     2},
    {ALGO, {{IAccessControl_VM_CP_CDSP, IAccessControl_PERM_TYPE_RW}}, 1},
	{HEAP, {{IAccessControl_VM_CP_CDSP, IAccessControl_PERM_TYPE_RW}}, 1},
	{POOL, {{IAccessControl_VM_CP_CDSP, IAccessControl_PERM_TYPE_RW}}, 1},
	{DATA, {{IAccessControl_VM_CP_CDSP, IAccessControl_PERM_TYPE_RW}}, 1},
};
static uint32_t nr_buf_vm_list = NUM_ELEMS(g_buf_vmid_perms);
static uint8_t *ref = NULL;

/*===========================================================================
    DECLARATIONS
===========================================================================*/
static int64_t RegisterSecureBuffer(
   uint64_t addr,
   uint32_t size,
   uint32_t buf_type
);

static int64_t DeregisterSecureBuffer(
   uint64_t addr,
   uint32_t size,
   uint32_t buf_type
);

static int64_t InitInputBuffer(
   uint64_t addr,
   uint64_t size,
   uint32_t buf_type
);

static int64_t MatchResult(
   uint64_t addr,
   uint64_t size,
   uint32_t buf_type
);

/* ======================================================================== */
/*  Reference C version of gaussian7x7u8()                                  */
/* ======================================================================== */
static const int32_t GAUSS_7x7[7*7] = {
   1,   6,  15,  20,  15,   6,  1,
   6,  36,  90, 120,  90,  36,  6,
  15,  90, 225, 300, 225,  90, 15,
  20, 120, 300, 400, 300, 120, 20,
  15,  90, 225, 300, 225,  90, 15,
   6,  36,  90, 120,  90,  36,  6,
   1,   6,  15,  20,  15,   6,  1
};

/**
   @brief
   Compute reference output for a given reference pattern.
   This reference output is used later for comparison with
   output generated within secure process on CDSP.   
*/
static void Gaussian7x7u8_ref(
    uint8_t       *src,
    int32_t        width,
    int32_t        height,
    int32_t        stride,
    uint8_t        *dst,
    int32_t        dstStride
    )
{
    int32_t x, y, s, t;
    int32_t sum, out;

    for (y = 3; y < height - 3; y++)
    {
        for (x = 3; x < width - 3; x++)
        {
            sum = 0;

            for (t = -3; t <= 3; t++)
            {
                for (s = -3; s <= 3; s++)
                {
                    sum += src[(y+t)*stride + x+s] * GAUSS_7x7[((t+3)*7)+(s+3)];
                }
            }

            out  = sum >> 12;

            out = out < 0 ? 0 : out > 255 ? 255 : out;
            dst[y*dstStride + x] = (uint8_t)out;
        }
    }
}

/**
   @brief
   Add any app specific initialization code here
   QSEE will call this function after secure app is loaded and
   authenticated
*/
void tz_app_init(void)
{
   /*  App specific initialization code */
   qsee_log(QSEE_LOG_MSG_DEBUG, "%s called", __func__);
    // Feature specific code
   (void)dsc_get_feat_handle(&g_dsc_handle);
}

void tz_constructor(void)
{
   qsee_log_mask = qsee_log_get_mask();
   qsee_log_set_mask(QSEE_LOG_MSG_ERROR | QSEE_LOG_MSG_FATAL | QSEE_LOG_MSG_DEBUG);
   qsee_log(QSEE_LOG_MSG_DEBUG, "%s called", __func__);
}

void tz_destructor(void)
{
   qsee_log(QSEE_LOG_MSG_DEBUG, "%s called", __func__);
   qsee_log_set_mask(qsee_log_mask);
   dsc_release_feat_handle(g_dsc_handle);
   g_dsc_handle = NULL;
}

/*
 * init_array handling calls each function pointer in the order the linker
 * adds the function pointers to the array
 */
__attribute__((section(".init_array"))) void *__tzctor = tz_constructor;
/*
 * fini_array handling calls each function pointer in the reverse order
 * the linker adds the function pointer to the array
 */
__attribute__((section(".fini_array"))) void *__tzdtor = tz_destructor;


/**
   @brief
   Checks if the given buffer ptr belongs to secure region or non-secure region.
*/

static int32_t ip_vmid_check(uint8_t *ptr, uint32_t len, uint32_t vmid)
{
   int32_t nErr = LOADALGO_SUCCESS;

   if (ptr == NULL) {
      nErr = LOADALGO_INVALID_POINTER;
      qsee_log(QSEE_LOG_MSG_FATAL, "%s: Buffer passed is NULL, nErr : %d\n", nErr); 
      goto bail;
   }

   if (vmid == AC_VM_CP_CDSP) {
      if (!qsee_is_s_tag_area(vmid, (uint64_t)ptr, (uint64_t)((uint64_t)ptr + len))) {
         nErr = LOADALGO_NOT_SECURE_TAGGED;
         qsee_log(QSEE_LOG_MSG_FATAL, "%s: qsee_is_s_tag_area : buffer:0x%llx of len:%d doesn't belong to vmid : %d, nErr:%d\n", __func__, ptr, len, vmid, nErr);
         goto bail;
      }
   } else {
      if (!qsee_is_ns_range(ptr, len)) {
         nErr = LOADALGO_NOT_NONSECURE_RANGE;
         qsee_log(QSEE_LOG_MSG_FATAL, "%s: qsee_is_ns_range : buffer:0x%llx of len:%d not in ns range, nErr:%d", __func__, ptr, len, nErr);
         goto bail;
      }
   }
bail:
    return nErr;
}

/**
   @brief
   Registers shared buffer with QSEE and prepare the shared
   buffer for access from securePD.
*/

static int32_t ip_shared_buffer_mem_ops(uint8_t *ptr, uint32_t len, uint8_t map)
{
   int32_t nErr = LOADALGO_SUCCESS;
   if (ptr == NULL) {
      nErr = LOADALGO_INVALID_POINTER;
      qsee_log(QSEE_LOG_MSG_FATAL,"%s: buffer is NULL, nErr:%d\n", __func__, nErr);
      goto bail;      
   }
   if (map) {
      nErr = qsee_register_shared_buffer(ptr, len);
      if (nErr) {
         qsee_log(QSEE_LOG_MSG_FATAL,"%s: qsee_register_shared_buffer for buffer:0x%x of len:%d failed with nErr:%d\n", __func__, ptr, len, nErr);
         goto bail;
      }
      nErr = qsee_prepare_shared_buf_for_secure_read(ptr, len);
      if (nErr) {
         qsee_log(QSEE_LOG_MSG_FATAL, "%s: qsee_prepare_shared_buf_for_secure_read for buffer:0x%x of len:%d failed with nErr:%d\n", __func__, ptr, len, nErr);
         goto bail;
      }
   } 
   else { // de-register
      nErr = qsee_prepare_shared_buf_for_nosecure_read(ptr, len);
      if (nErr) {
         qsee_log(QSEE_LOG_MSG_FATAL, "%s: qsee_prepare_shared_buf_for_nosecure_read for buffer:0x%x of len:%d failed with nErr:%d\n", __func__, ptr, len, nErr);
         goto bail;
      }
      nErr = qsee_deregister_shared_buffer(ptr);
      if (nErr) {
         qsee_log(QSEE_LOG_MSG_FATAL, "%s: qsee_deregister_shared_buffer for buffer:0x%x failed with nErr:%d\n", __func__, ptr, nErr);
         goto bail;
      }
   }
bail:
   return nErr;
}

/**
   @brief
   Print the public key in qsee log
*/

static int32_t __print_pub_key(uint8_t *public_key, size_t public_key_len)
{
	int32_t nErr = LOADALGO_SUCCESS;
    size_t key_index = 0;
    qsee_log(QSEE_LOG_MSG_DEBUG, "Public Key: 0x%02x", public_key[key_index]);
    for (key_index = 1; key_index < public_key_len; key_index++) {
    	qsee_log(QSEE_LOG_MSG_DEBUG, "            0x%02x", public_key[key_index]);
    }
    return nErr;
}

/**
   @brief
   Copy contents of non-secure buffer to secure buffer
*/
   
static int64_t CopyNonSecureBufferToSecureBuffer(send_cmd_t* req)
{
   int nErr = LOADALGO_SUCCESS;

   qsee_log(QSEE_LOG_MSG_DEBUG, "%s called", __func__);
   uint32_t cmd_id = req->cmd_id;
   uint64_t secureBuf = req->secureBuf;
   uint32_t secureBufsize = req->secureBufsize;
   uint32_t Type = req->type;
   uint64_t nonSecureBuf = req->nonSecureBuf;
   uint32_t nonSecureBufsize = req->nonSecureBufsize;

   //Map memory
   nErr= ip_shared_buffer_mem_ops((uint8_t *)nonSecureBuf, nonSecureBufsize, 1);
   if (nErr) {
      qsee_log(QSEE_LOG_MSG_FATAL, "%s: Failed to map nonSecureBuf:0x%llx of size:%u for secure read with nErr:%d\n", __func__, nonSecureBuf, nonSecureBufsize, nErr);
      goto bail;
   }
   nErr= ip_shared_buffer_mem_ops((uint8_t *)secureBuf, secureBufsize, 1);
   if (nErr) {
      qsee_log(QSEE_LOG_MSG_FATAL, "%s: Failed to map SecureBuf:0x%llx of size:%u for secure read with nErr:%d\n", __func__, secureBuf, secureBufsize, nErr);
      goto bail;
   }
   //Copy data
   memscpy((void *)secureBuf, secureBufsize, (void *)nonSecureBuf, nonSecureBufsize);
   
   nErr= ip_shared_buffer_mem_ops((uint8_t *)nonSecureBuf, nonSecureBufsize, 0);
   if (nErr) {
      qsee_log(QSEE_LOG_MSG_FATAL, "%s: Failed to unmap nonSecureBuf:0x%llx of size:%u with nErr:%d\n", __func__, nonSecureBuf, nonSecureBufsize, nErr);
      goto bail;
   }
   nErr= ip_shared_buffer_mem_ops((uint8_t *)secureBuf, secureBufsize, 0);
   if (nErr) {
      qsee_log(QSEE_LOG_MSG_FATAL, "%s: Failed to unmap SecureBuf:0x%llx of size:%u for secure read with nErr:%d\n", __func__, secureBuf, secureBufsize, nErr);
      goto bail;
   }
bail:
   return nErr;
}

/**
   @brief
   Decrypt the encrypted content stored in the non-secure buffer and copy the
   decrypted output to a secure buffer.
*/

static int64_t DecryptNonSecureBuffer(send_cmd_t* req)
{
   int nErr = LOADALGO_SUCCESS;
   uint32_t state;
   uint8_t public_key[64];
   uint32_t cmd_id = req->cmd_id;
   uint64_t secureBuf = req->secureBuf;
   uint32_t secureBufsize = req->secureBufsize;
   uint32_t Type = req->type;
   uint64_t nonSecureBuf = req->nonSecureBuf;
   uint32_t nonSecureBufsize = req->nonSecureBufsize;

   qsee_log(QSEE_LOG_MSG_DEBUG, "%s: securebuf:0x%llx (size:%u), nonsecure buf:0x%llx (size:%u), type:%u\n", __func__, secureBuf, secureBufsize, nonSecureBuf, nonSecureBufsize, Type);
   nErr = dsc_get_state(g_dsc_handle, &state);
   if (nErr != 0) {
      qsee_log(QSEE_LOG_MSG_FATAL, "%s: Failed to retrieve secure channel state with nErr:%d\n", __func__, nErr);
      goto bail;
   }
   if (state != RESET) {
      qsee_log(QSEE_LOG_MSG_FATAL, "%s: Secure Channel in an invalid state\n");
      goto bail;
   }
   nErr = dsc_set_state(g_dsc_handle, INIT);
   if (nErr != 0 ) {
      qsee_log(QSEE_LOG_MSG_FATAL, "%s: Failed to set state to INIT with nErr:%d\n", __func__, nErr);
      goto bail;
   }
   nErr = dsc_verify_buffer(g_dsc_handle, Type, secureBuf, secureBufsize);
   if (nErr != 0){
      qsee_log(QSEE_LOG_MSG_FATAL, "%s: SecureBuf:0x%llx of size:%u not registered with TA, nErr:%d\n", __func__, secureBuf, secureBufsize, nErr);
      goto bail;
   }
   qsee_log(QSEE_LOG_MSG_DEBUG, "Getting Public key\n");
   nErr = ipprotector_get_public_key(FEATURE_ID, public_key, sizeof(public_key));
   if (nErr) {
      qsee_log(QSEE_LOG_MSG_FATAL, "Failed to get public_key, nErr:%d\n", nErr);
      goto bail;
   }
   /*nErr = __print_pub_key(public_key, sizeof(public_key));
   if (nErr) {
	   qsee_log(QSEE_LOG_MSG_FATAL, "Failed to print public_key, nErr:%d\n", nErr);
	   goto bail;
   }*/
   nErr = ip_vmid_check((uint8_t*)nonSecureBuf, nonSecureBufsize, AC_VM_HLOS);
   if (nErr) {
      qsee_log(QSEE_LOG_MSG_FATAL, "%s: Failed to verify nonsecureBuf:0x%llx of size:%u and vmid:%d with nErr:%d\n", __func__, nonSecureBuf, nonSecureBufsize, AC_VM_HLOS, nErr);
      goto bail;
   }
   nErr = ip_vmid_check((uint8_t*)secureBuf, secureBufsize, AC_VM_CP_CDSP);
   if (nErr) {
   	   qsee_log(QSEE_LOG_MSG_FATAL, "%s: Failed to verify secureBuf:0x%llx of size:%u and vmid:%d with nErr:%d\n", __func__, secureBuf, secureBufsize, AC_VM_CP_CDSP, nErr);
   	   goto bail;
   }
   nErr = ip_shared_buffer_mem_ops((uint8_t*)secureBuf, secureBufsize, 1);
   if (nErr) {
      qsee_log(QSEE_LOG_MSG_FATAL, "%s: Failed to map secureBuf:0x%llx of size:%u with nErr:%d\n", __func__, secureBuf, secureBufsize, nErr);
      goto bail;
   }
   nErr = ip_shared_buffer_mem_ops((uint8_t*)nonSecureBuf, nonSecureBufsize, 1);
   if (nErr) {
      qsee_log(QSEE_LOG_MSG_FATAL, "%s: Failed to map nonsecureBuf:0x%llx of size:%u with nErr:%d\n", __func__, secureBuf, secureBufsize, nErr);
      goto bail;
   }
   // Decrypt IP
   nErr = ipprotector_decrypt(FEATURE_ID, (uint8_t *)nonSecureBuf, nonSecureBufsize, (uint8_t *)secureBuf, secureBufsize);
   if (nErr) {
      qsee_log(QSEE_LOG_MSG_FATAL, "%s: Failed to decrypt IP with nErr:%d\n", __func__, nErr);
      goto bail;
   }
   //Verify signature of decrypted IP
   nErr = ipprotector_verify_sig(FEATURE_ID, ANTI_ROLLBACK_VERSION, (uint8_t *)secureBuf, secureBufsize); //is this being done on the correct buffer? shouln't it be done on the non-secure buffer?
   if (nErr) {
      qsee_log(QSEE_LOG_MSG_FATAL, "%s: Failed to verify signature of decrypted IP with nErr:%d\n", __func__, nErr);
      goto bail;
   }
   //memscpy((void *)secureBuf, secureBufsize, (void *)nonSecureBuf, nonSecureBufsize);
   nErr = ip_shared_buffer_mem_ops((uint8_t*)nonSecureBuf, nonSecureBufsize, 0);
   if (nErr) {
      qsee_log(QSEE_LOG_MSG_FATAL, "%s: Failed to unmap nonsecureBuf:0x%llx of size:%u with nErr:%d\n", __func__, nonSecureBuf, nonSecureBufsize, nErr);
      goto bail; 
   }
   nErr = ip_shared_buffer_mem_ops((uint8_t*)secureBuf, secureBufsize, 0);
   if (nErr) {
      qsee_log(QSEE_LOG_MSG_FATAL, "%s: Failed to unmap secureBuf:0x%llx of size:%u with nErr:%d\n", __func__, secureBuf, secureBufsize, nErr);
      goto bail;
   }
   state = LOADED;
   nErr = dsc_set_state(g_dsc_handle, state);
   if (nErr) {
      qsee_log(QSEE_LOG_MSG_FATAL, "%s: Failed to set state to LOADED with nErr:%d\n", __func__, nErr);
      goto bail; 
   }
bail:
   return nErr;
}

/**
    @brief
	Register the given shared buffer addr of type buf_type with the secure channel.
*/

static int64_t RegisterSecureBuffer(uint64_t addr, uint32_t size,
                                     uint32_t buf_type)
{
    int32_t nErr = LOADALGO_SUCCESS;
    uint32_t i = 0;
    IAccessControl_vmidPermission *vmid_perms = NULL;
    uint32_t num_vmid_perms = 0;

    for (i = 0; i < nr_buf_vm_list; i++) {
        if (buf_type == g_buf_vmid_perms[i].buf_type) {
            vmid_perms = g_buf_vmid_perms[i].vmid_perms;
            num_vmid_perms = g_buf_vmid_perms[i].num_vmid_perms;
            break;
        }
    }

    if (vmid_perms == NULL) {
    	  nErr = LOADALGO_NO_VMLIST;
          qsee_log(QSEE_LOG_MSG_ERROR, "%s: Cannot find VM List for buf_type : %d, nErr:%d", __func__, buf_type, nErr);
          return nErr;
    }

    nErr = dsc_register_buffer(g_dsc_handle, buf_type, addr, size,
                                          vmid_perms, num_vmid_perms);
    if (nErr) {
       qsee_log(QSEE_LOG_MSG_FATAL, "%s: Failed to register buffer:0x%llx of size:%u and type:%u with secure channel with nErr:%d\n", __func__, addr, size, buf_type, nErr);
    }
    return nErr;
}

/**
    @brief
	Deregister the given shared buffer addr of type buf_type with the secure channel.
*/

static int64_t DeregisterSecureBuffer(
   uint64_t addr,
   uint32_t size,
   uint32_t buf_type
)
{
   qsee_log(QSEE_LOG_MSG_DEBUG, "%s: DeregisterSecureBuffer addr = 0x%llx, size = 0x%x", __func__, addr, size);
   return dsc_deregister_buffer(g_dsc_handle, buf_type, addr, size);
}

/**
  @brief 
  App specific commands should be handled in this function.

  @param[in]      cmd         Requested command structure
  @param[in]      cmdlen      length of request commad struct
  @param[in/out]  rsp         Resposnse structure
  @param[in/out]  rsplen      length of response commad struct
*/

void tz_app_cmd_handler(void* cmd, uint32_t cmdlen,
                        void* rsp, uint32_t rsplen)
{
   /* Request-response buffers are allocated by non-secure side*/
   /* Add code to process requests and set response (if any)*/
   int nErr = LOADALGO_SUCCESS;
   send_cmd_t *cmd_ptr = (send_cmd_t *)cmd;
   int64_t *rsp_ptr = (int64_t *)rsp;
   uint32_t state;

   /* sanity check */
   if ((!cmd_ptr) || (!rsp_ptr))
   {
	  nErr = LOADALGO_INVALID_POINTER;
      qsee_log(QSEE_LOG_MSG_FATAL, "%s: Command pointer or response pointer equals NULL, nErr:%d!", __func__, nErr);
      return;
   }

   /*Validate the command buffer and response buffer are the correct size.
     If not, the the MPU protection and ns_range checks done by QSEE kernel might be insufficient*/
   if(cmdlen < sizeof(send_cmd_t) || rsplen < sizeof(uint32_t))
   {
	  nErr = LOADALGO_INSUFFICIENT_LEN;
      qsee_log(QSEE_LOG_MSG_FATAL, "%s: Cmd/rsp buffer lens insufficient - 0x%x, 0x%x nErr:%d, ERROR OUT", __func__, cmdlen, rsplen, nErr);
      *rsp_ptr = -1;
      return;
   }

   qsee_log(QSEE_LOG_MSG_DEBUG, "%s: TZ App cmd handler, cmd_id = %d", __func__, cmd_ptr->cmd_id);

   if (!g_dsc_handle)
   {
	  nErr = LOADALGO_INVALID_HANDLE;
      qsee_log(QSEE_LOG_MSG_FATAL, "%s: DSC Handle initialization failed, nErr:%d!", __func__, nErr);
      *rsp_ptr = -1;
      return;
   }

   switch(cmd_ptr->cmd_id){
      case REGISTER_BUFFER:
         qsee_log(QSEE_LOG_MSG_DEBUG, "%s: Register SecureBuf:0x%llx of size:0x%x\n", __func__, cmd_ptr->secureBuf, cmd_ptr->secureBufsize);
         *rsp_ptr = RegisterSecureBuffer(cmd_ptr->secureBuf, cmd_ptr->secureBufsize, cmd_ptr->type);
          break;
      case DEREGISTER_BUFFER:
         qsee_log(QSEE_LOG_MSG_DEBUG, "%s: Deregister SecureBuf:0x%llx of size:0x%x\n", __func__, cmd_ptr->secureBuf, cmd_ptr->secureBufsize);
         *rsp_ptr = DeregisterSecureBuffer(cmd_ptr->secureBuf, cmd_ptr->secureBufsize, cmd_ptr->type);
          break;
      case DECRYPT_BUFFER:
         *rsp_ptr = DecryptNonSecureBuffer(cmd_ptr);
          break;
      case COPYNONSECURE_TO_SECURE:
         *rsp_ptr = CopyNonSecureBufferToSecureBuffer(cmd_ptr);  
          break;
	  case SEC_MATCH_RESULT:
         qsee_log(QSEE_LOG_MSG_DEBUG, "%s: match result buffer addr = 0x%x size = 0x%x", __func__, cmd_ptr->secureBuf, cmd_ptr->secureBufsize);
         *rsp_ptr = MatchResult(cmd_ptr->secureBuf, cmd_ptr->secureBufsize, cmd_ptr->type);
         break;

      case SEC_INPUT_BUFFER_INIT:
         qsee_log(QSEE_LOG_MSG_DEBUG, "%s: init secure input buffer addr = 0x%x size = 0x%x", __func__, cmd_ptr->secureBuf, cmd_ptr->secureBufsize);
         *rsp_ptr = InitInputBuffer(cmd_ptr->secureBuf, cmd_ptr->secureBufsize, cmd_ptr->type);
         break;
      default:
         *rsp_ptr = 0;
         qsee_log(QSEE_LOG_MSG_ERROR, "%s: Unsupported command: %d. Test not yet implemented or commented out.", __func__, cmd_ptr->cmd_id);
         break;
   }
}

/**
  @brief
  Initialize a shared buffer with some random pattern and compute a reference output.
*/

static int64_t InitInputBuffer(
   uint64_t addr,
   uint64_t size,
   uint32_t buf_type
)
{
   uint32_t nErr = LOADALGO_SUCCESS;
   uint8_t *src = NULL;
   uint32_t srcWidth = GAUSSIAN_WIDTH;
   uint32_t srcHeight = GAUSSIAN_HEIGHT;
   uint32_t srcStride = srcWidth;
   uint32_t i,j;

   nErr = dsc_verify_buffer(g_dsc_handle, buf_type, addr, size);
   if (nErr != 0) {
      qsee_log(QSEE_LOG_MSG_ERROR, "%s: verify buffer:0x%llx size:%lld type:%d failed with nErr:%d", __func__, addr, size, buf_type, nErr);
      return nErr;
   }
   qsee_log(QSEE_LOG_MSG_DEBUG, "%s: verified buffer:0x%llx of size:%lld type:%d in secure channel", __func__, addr, size, buf_type); 
   if (size < srcWidth * srcHeight) {
	  nErr = LOADALGO_INSUFFICIENT_LEN;
      qsee_log(QSEE_LOG_MSG_ERROR, "%s: buffer:0x%llx of size:%lld too small, nErr:%d\n", __func__, addr, size, nErr);
      return nErr;
   }
   nErr = qsee_register_shared_buffer((void *)addr, (uint32_t)size);
   if (nErr != 0) {
      qsee_log(QSEE_LOG_MSG_ERROR, "%s: Failed to register shared buffer:0x%llx of size:%lld with nErr:%d", __func__, addr, size, nErr);
      return nErr;
   }
   qsee_log(QSEE_LOG_MSG_DEBUG, "%s: registered shared buffer:0x%llx of size:%lld", __func__, addr, size); 
   nErr = qsee_prepare_shared_buf_for_secure_read((void *)addr, (uint32_t)size);
   if (nErr != 0) {
      qsee_log(QSEE_LOG_MSG_ERROR, "%s: Failed to prepare shared buffer:0x%llx of size:%lld for read in TA with nErr:%d", __func__, addr, size, nErr);
      return nErr;
   }
   qsee_log(QSEE_LOG_MSG_DEBUG, "%s: prepared shared buffer:0x%llx of size:%lld for read in TA", __func__, addr, size);
   src = (uint8_t *)addr;
   for (j = 0; j < srcHeight; j++)
   {
      uint8_t *ptr = &src[j * srcStride];
      for (i = 0; i < srcWidth; i++)
      {
         *ptr++ = i + j;        // some incrementing pattern fill
      }
   }
   if (ref != NULL) {
	  nErr = LOADALGO_INVALID_POINTER;
      qsee_log(QSEE_LOG_MSG_ERROR, "%s: Cannot use existing ref buffer, nErr:%d", __func__, nErr);
      return nErr;
   }
   ref = (uint8_t *)qsee_malloc(size);
   if (ref == NULL) {
	  nErr = LOADALGO_ALLOCFAILED;
      qsee_log(QSEE_LOG_MSG_ERROR, "%s: Unable to allocate ref buffer, nErr:%d", __func__, nErr);
      return nErr;
   }
   qsee_log(QSEE_LOG_MSG_DEBUG, "%s: calling Gaussian7x7u8_ref",__func__);
   Gaussian7x7u8_ref(src, srcWidth, srcHeight, srcStride, ref, srcStride);
   qsee_log(QSEE_LOG_MSG_DEBUG, "%s: returned from Gaussian7x7u8_ref", __func__);
   nErr = qsee_prepare_shared_buf_for_nosecure_read((void *)addr, (uint32_t)size);
   if (nErr != 0) {
      qsee_log(QSEE_LOG_MSG_ERROR, "%s: Failed to prepare shared buffer:0x%x of size:%d for read outside TA with nErr:%d", __func__, addr, size, nErr);
      return nErr;
   }
   qsee_log(QSEE_LOG_MSG_DEBUG, "%s: prepared shared buffer:0x%x of size:%d for read outside TA", __func__, addr, size);
   nErr = qsee_deregister_shared_buffer((void *)addr);
   if (nErr != 0) {
      qsee_log(QSEE_LOG_MSG_ERROR, "%s: Failed to deregister shared buffer:0x%x with nErr:%d", __func__, addr, nErr);
      return nErr;
   }
   qsee_log(QSEE_LOG_MSG_DEBUG, "%s: Deregistered shared buffer:0x%x", __func__, addr);
   nErr = dsc_set_state(g_dsc_handle, LOADED);
   if (nErr != 0) {
      qsee_log(QSEE_LOG_MSG_ERROR, "%s: Failed to set secure channel state to LOADED with nErr:%d", __func__, nErr);
      return nErr;
   }
   return nErr;
}

/**
  @brief
  Compare the refernece output computed by TA with
  that of secure process on CDSP.
*/

static int64_t MatchResult(
   uint64_t addr,
   uint64_t size,
   uint32_t buf_type
)
{
   uint32_t nErr = LOADALGO_SUCCESS;
   uint32_t state;
   uint8_t *dst = NULL;
   uint32_t bitexactErrors = 0;
   uint32_t dstWidth = GAUSSIAN_WIDTH;
   uint32_t dstHeight = GAUSSIAN_HEIGHT;
   uint32_t dstStride = dstWidth;
   uint32_t i,j;

   if (ref == NULL) {
	  nErr = LOADALGO_INVALID_POINTER;
      qsee_log(QSEE_LOG_MSG_ERROR, "%s: Ref buffer is NULL, nErr:%d", __func__, nErr);
      return nErr;
   }

   if (size < dstWidth * dstHeight) {
	  nErr = LOADALGO_INSUFFICIENT_LEN;
      qsee_log(QSEE_LOG_MSG_ERROR, "%s: buffer too small, nErr:%d", __func__, nErr);
      return nErr;
   }

   nErr = dsc_verify_buffer(g_dsc_handle, buf_type, addr, size);
   if (nErr != 0) {
      qsee_log(QSEE_LOG_MSG_ERROR, "%s: Unable to verify buffer:0x%llx size:%lld type:%d with nErr:%d", __func__, addr, size, buf_type, nErr);
      return nErr;
   }

   nErr = dsc_get_state(g_dsc_handle, &state);
   if (state != DONE) {
      qsee_log(QSEE_LOG_MSG_ERROR, "%s: expect state is DONE, actual state is %d , nErr:%d\n", __func__, state, nErr);
      return nErr;
   }

   nErr = qsee_register_shared_buffer((void *)addr, (uint32_t)size);
   if (nErr != 0) {
      qsee_log(QSEE_LOG_MSG_ERROR, "%s: Failed to register shared buffer:0x%llx of size:%lld with nErr:%d\n", __func__, addr, size, nErr);
      return nErr;
   }
   nErr = qsee_prepare_shared_buf_for_secure_read((void *)addr, (uint32_t)size);
   if (nErr != 0) {
      qsee_log(QSEE_LOG_MSG_ERROR, "%s: Failed to prepare shared buffer:0x%llx of size:%lld with nErr:%d\n", __func__, addr, size, nErr);
      return nErr;
   }

   dst = (uint8_t *)addr;
   for (j = 3; j < dstHeight-3; j++)
   {
       for (i = 3; i < dstWidth-3; i++)
       {
           if (ref[j * dstStride + i] != dst[j * dstStride + i])
           {
               bitexactErrors++;
           }
       }
   }

   qsee_log(QSEE_LOG_MSG_ERROR, "%s: Number of bit-exact errors: %d", __func__, bitexactErrors);
   qsee_free(ref);
   ref = NULL;

   nErr = qsee_prepare_shared_buf_for_nosecure_read((void *)addr, (uint32_t)size);
   if (nErr != 0) {
      qsee_log(QSEE_LOG_MSG_ERROR, "%s: Failed to prepare shared buffer:0x%llx of size:%lld for read outside TA with nErr:%d", __func__, addr, size, nErr);
      return nErr;
   }
   nErr = qsee_deregister_shared_buffer((void *)addr);
   if (nErr != 0) {
      qsee_log(QSEE_LOG_MSG_ERROR, "%s: Failed to deregister shared buffer:0x%llx with nErr:%d", __func__, nErr);
      return nErr;
   }

   if (bitexactErrors == 0) {
      qsee_log(QSEE_LOG_MSG_ERROR, "********Result match********");
      nErr = LOADALGO_SUCCESS;
   } else {
      qsee_log(QSEE_LOG_MSG_ERROR, "Result mismatch");
      nErr = LOADALGO_FAILURE;
   }
   return nErr;
}
