//------------------------------------------------------------------------------
static TEEC_Result _TEEC_UnwindOperation(
    _TEEC_TCI           *tci,
    mcSessionHandle_t   *handle,
    TEEC_Operation      *operation,
    bool                copyValues,
    uint32_t            *returnOrigin)
{
    uint32_t                    i;
    _TEEC_ParameterInternal     *imp;
    TEEC_Parameter              *ext;
    uint8_t                     *buffer;

    //operation can be NULL
    if (operation == NULL) return  TEEC_SUCCESS;

    LOG_I(" %s()", __func__);

    operation->started = 2;

    // Some sanity checks
    if (tci->returnOrigin == 0 ||
            ((tci->returnOrigin != TEEC_ORIGIN_TRUSTED_APP) && (tci->returnStatus != TEEC_SUCCESS))) {
        *returnOrigin = TEEC_ORIGIN_COMMS;
        return TEEC_ERROR_COMMUNICATION;
    }
    *returnOrigin = tci->returnOrigin;

    //Clear sVirtualLen to unMap further
    for (i = 0; i < _TEEC_PARAMETER_NUMBER; i++) {
        imp = &tci->operation.params[i];
        ext = &operation->params[i];
        buffer = NULL;

        switch (_TEEC_GET_PARAM_TYPE(operation->paramTypes, i)) {
        case TEEC_VALUE_INPUT:
            LOG_I("  cycle %d, TEEC_VALUE_INPUT", i);
            break;
        case TEEC_NONE:
            LOG_I("  cycle %d, TEEC_NONE", i);
            break;
        case TEEC_VALUE_OUTPUT:
        case TEEC_VALUE_INOUT: {
            LOG_I("  cycle %d, TEEC_VALUE_*OUT", i);
            if (copyValues) {
                ext->value.a = imp->value.a;
                ext->value.b = imp->value.b;
            }
            break;
        }
        case TEEC_MEMREF_TEMP_OUTPUT:
        case TEEC_MEMREF_TEMP_INPUT:
        case TEEC_MEMREF_TEMP_INOUT: {
            LOG_I("  cycle %d, TEEC_TEMP*", i);
            if ((copyValues) && (_TEEC_GET_PARAM_TYPE(operation->paramTypes, i) != TEEC_MEMREF_TEMP_INPUT)) {
                ext->tmpref.size = imp->memref.outputSize;
            }
            buffer = (uint8_t *)ext->tmpref.buffer;
            break;
        }
        case TEEC_MEMREF_WHOLE: {
            LOG_I("  cycle %d, TEEC_MEMREF_WHOLE", i);
            if ((copyValues) && (ext->memref.parent->flags != TEEC_MEM_INPUT)) {
                ext->memref.size = imp->memref.outputSize;
            }
            buffer = (uint8_t *)ext->memref.parent->buffer;
            break;
        }

        case TEEC_MEMREF_PARTIAL_OUTPUT:
        case TEEC_MEMREF_PARTIAL_INOUT:
        case TEEC_MEMREF_PARTIAL_INPUT: {
            LOG_I("  cycle %d, TEEC_MEMREF_PARTIAL*", i);
            if ((copyValues) && (_TEEC_GET_PARAM_TYPE(operation->paramTypes, i) != TEEC_MEMREF_PARTIAL_INPUT)) {
                ext->memref.size = imp->memref.outputSize;
            }
            buffer = (uint8_t *)ext->memref.parent->buffer + ext->memref.offset;
            break;
        }
        default:
            LOG_E("cycle %d, bad parameter", i);
            break;
        }

        if ((buffer != NULL) && (imp->memref.mapInfo.sVirtualLen != 0)) {
            // This function assumes that we cannot handle error of mcUnmap
            (void)mcUnmap(handle, buffer, &imp->memref.mapInfo);
        }
    }

    return tci->returnStatus;
}
rootpaerror_t executeOneCmpCommand(CMTHANDLE handle, CmpMessage* commandP, CmpMessage* responseP)
{
    LOGD(">>executeOneCmpCommand");
    if (unlikely( bad_write_ptr(handle,sizeof(CMTSTRUCT)))) 
    {
        return ROOTPA_ERROR_INTERNAL;
    }
    if(unlikely (commandP->contentP==NULL || commandP->length< sizeof(cmpCommandId_t)))
    {
        return ROOTPA_ERROR_INTERNAL;
    }

    mcResult_t mcRet=MC_DRV_OK;
    cmpCommandId_t commandId=getCmpCommandId(commandP->contentP);
        
    handle->mappedSize=getTotalMappedBufferSize(commandP);
    if(0==handle->mappedSize)
    {
        LOGE("<<executeOneCmpCommand, command %d not supported", commandId);
        return ROOTPA_COMMAND_NOT_SUPPORTED;
    }

    rootpaerror_t ret=ROOTPA_OK;
    while(true) 
    {
        handle->mappedP=malloc((size_t) handle->mappedSize);
        if(NULL==handle->mappedP)
        {
            ret=ROOTPA_ERROR_OUT_OF_MEMORY;
            break;
        }
        memset(handle->mappedP, 0,handle->mappedSize);
        mcRet=mcMap(&handle->session, handle->mappedP, handle->mappedSize, &handle->mapInfo);
        if(mcRet!=MC_DRV_OK)
        {
            LOGE("executeOneCmpCommand not able to map memory %d", mcRet);
            ret=ROOTPA_ERROR_MOBICORE_CONNECTION;
            commandP->hdr.intRet=mcRet;
            responseP->hdr.intRet=mcRet;
            break;
        }

        if((ret = prepareCommand(commandId, commandP, handle, responseP))!=ROOTPA_OK)
        {
            LOGE("prepareCommand failed %d", ret);
            break;
        }

        if (unlikely( !tltChannelTransmit(handle, NOTIFICATION_WAIT_TIMEOUT_MS)))
        {
            ret=ROOTPA_ERROR_MOBICORE_CONNECTION;
            commandP->hdr.intRet=handle->lasterror;
            responseP->hdr.intRet=handle->lasterror;
            break;
        }

        uint32_t neededBytes=getNeededBytesFromResponse(handle->wsmP);

        if(0==neededBytes)
        {
            break;
        }

        if(-1==neededBytes)
        {
            ret=ROOTPA_ERROR_MOBICORE_CONNECTION; 
            break;
        }

        if(neededBytes <= handle->mappedSize)
        {
            LOGE("executeOneCmpCommand, there is something wrong. CMTL is requesting smaller buffer than we originally had. Command: %d, original %d requested %d",  
	         commandId, handle->mappedSize, neededBytes);
            ret=ROOTPA_ERROR_MOBICORE_CONNECTION;
            break;
        }

        // this is Info level LOGI on purpose
        LOGI("executeOneCmpCommand, updating RootPA recommended (%d bytes was not enough for %d response, allocating %d bytes and retrying)", handle->mappedSize, commandId, neededBytes);
        mcRet=mcUnmap(&handle->session, handle->mappedP, &handle->mapInfo);
        if(mcRet!=MC_DRV_OK)
        {
            LOGE("executeOneCmpCommand not able to free mapped memory %d", mcRet);
            ret=ROOTPA_ERROR_MOBICORE_CONNECTION;
            commandP->hdr.intRet=mcRet;
            responseP->hdr.intRet=mcRet;
            break;
        }

        free(handle->mappedP);
        memset(&handle->mapInfo, 0 , sizeof(handle->mapInfo));
        handle->mappedSize=neededBytes;
    }

    if(ROOTPA_OK==ret)
    {
        ret=handleResponse(commandId, responseP, handle);
    }
    else
    {
        responseP->hdr.ret=ret;
    }
    LOGD("cleaning up mapped memory %ld",(long int) handle->mappedP);
    mcRet=mcUnmap(&handle->session, handle->mappedP, &handle->mapInfo);
    if(mcRet!=MC_DRV_OK)
    {
        LOGE("executeOneCmpCommand not able to free mapped memory %d", mcRet);
        ret=ROOTPA_ERROR_MOBICORE_CONNECTION;
    }
    LOGD("freeing mapped memory %ld", (long int) handle->mappedP);    
    free(handle->mappedP);    
    if(commandP->hdr.ret==ROOTPA_OK) commandP->hdr.ret=ret;
    if(responseP->hdr.ret==ROOTPA_OK) responseP->hdr.ret=ret;    
    LOGD("<<executeOneCmpCommand %d %d",commandId, ret);
    return ret;
}