root/sys/dev/qat/qat_api/common/crypto/sym/lac_sym_alg_chain.c
/* SPDX-License-Identifier: BSD-3-Clause */
/* Copyright(c) 2007-2025 Intel Corporation */

/**
 ***************************************************************************
 * @file lac_sym_alg_chain.c      Algorithm Chaining Perform
 *
 * @ingroup LacAlgChain
 ***************************************************************************/

/*
*******************************************************************************
* Include public/global header files
*******************************************************************************
*/

#include "cpa.h"
#include "cpa_cy_sym.h"

#include "icp_accel_devices.h"
#include "icp_adf_init.h"
#include "icp_adf_transport.h"
#include "icp_adf_debug.h"

/*
*******************************************************************************
* Include private header files
*******************************************************************************
*/

#include "lac_mem.h"
#include "lac_log.h"
#include "lac_sym.h"
#include "lac_list.h"
#include "icp_qat_fw_la.h"
#include "lac_sal_types_crypto.h"
#include "lac_sal.h"
#include "lac_sal_ctrl.h"
#include "lac_sym_alg_chain.h"
#include "lac_sym_cipher.h"
#include "lac_sym_cipher_defs.h"
#include "lac_sym_hash.h"
#include "lac_sym_hash_defs.h"
#include "lac_sym_qat_cipher.h"
#include "lac_sym_qat_hash.h"
#include "lac_sym_stats.h"
#include "lac_sym_queue.h"
#include "lac_sym_cb.h"
#include "sal_string_parse.h"
#include "lac_sym_auth_enc.h"
#include "lac_sym_qat.h"
#include "sal_hw_gen.h"

/**
 * @ingroup LacAlgChain
 * This callback function will be invoked whenever a hash precompute
 * operation completes.  It will dequeue and send any QAT requests
 * which were queued up while the precompute was in progress.
 *
 * @param[in] callbackTag  Opaque value provided by user. This will
 *                         be a pointer to the session descriptor.
 *
 * @retval
 *     None
 *
 */
static void
LacSymAlgChain_HashPrecomputeDoneCb(void *callbackTag)
{
        LacSymCb_PendingReqsDequeue((lac_session_desc_t *)callbackTag);
}

/**
 * @ingroup LacAlgChain
 * Walk the buffer list and find the address for the given offset within
 * a buffer.
 *
 * @param[in] pBufferList   Buffer List
 * @param[in] packetOffset  Offset in the buffer list for which address
 *                          is to be found.
 * @param[out] ppDataPtr    This is where the sought pointer will be put
 * @param[out] pSpaceLeft   Pointer to a variable in which information about
 *                          available space from the given offset to the end
 *                          of the flat buffer it is located in will be returned
 *
 * @retval CPA_STATUS_SUCCESS Address with a given offset is found in the list
 * @retval CPA_STATUS_FAIL    Address with a given offset not found in the list.
 *
 */
static CpaStatus
LacSymAlgChain_PtrFromOffsetGet(const CpaBufferList *pBufferList,
                                const Cpa32U packetOffset,
                                Cpa8U **ppDataPtr)
{
        Cpa32U currentOffset = 0;
        Cpa32U i = 0;

        for (i = 0; i < pBufferList->numBuffers; i++) {
                Cpa8U *pCurrData = pBufferList->pBuffers[i].pData;
                Cpa32U currDataSize = pBufferList->pBuffers[i].dataLenInBytes;

                /* If the offset is within the address space of the current
                 * buffer */
                if ((packetOffset >= currentOffset) &&
                    (packetOffset < (currentOffset + currDataSize))) {
                        /* increment by offset of the address in the current
                         * buffer */
                        *ppDataPtr = pCurrData + (packetOffset - currentOffset);
                        return CPA_STATUS_SUCCESS;
                }

                /* Increment by the size of the buffer */
                currentOffset += currDataSize;
        }

        return CPA_STATUS_FAIL;
}

/**
 * @ingroup LacAlgChain
 * Function which checks for support of partial packets for symmetric
 * crypto operations
 *
 * @param[in] pService            Pointer to service descriptor
 * @param[in/out] pSessionDesc    Pointer to session descriptor
 *
 */
static void
LacSymCheck_IsPartialSupported(Cpa32U capabilitiesMask,
                               lac_session_desc_t *pSessionDesc)
{
        CpaBoolean isHashPartialSupported = CPA_FALSE;
        CpaBoolean isCipherPartialSupported = CPA_FALSE;
        CpaBoolean isPartialSupported = CPA_FALSE;

        switch (pSessionDesc->cipherAlgorithm) {
        /* Following ciphers don't support partial */
        case CPA_CY_SYM_CIPHER_KASUMI_F8:
        case CPA_CY_SYM_CIPHER_AES_F8:
        case CPA_CY_SYM_CIPHER_SNOW3G_UEA2:
        case CPA_CY_SYM_CIPHER_CHACHA:
        case CPA_CY_SYM_CIPHER_ZUC_EEA3:
                break;
        /* All others support partial */
        default:
                isCipherPartialSupported = CPA_TRUE;
                break;
        }
        switch (pSessionDesc->hashAlgorithm) {
        /* Following hash don't support partial */
        case CPA_CY_SYM_HASH_KASUMI_F9:
        case CPA_CY_SYM_HASH_SNOW3G_UIA2:
        case CPA_CY_SYM_HASH_POLY:
        case CPA_CY_SYM_HASH_ZUC_EIA3:
                break;
        /* Following hash may support partial based on device capabilities */
        case CPA_CY_SYM_HASH_SHA3_256:
                if (ICP_ACCEL_CAPABILITIES_SHA3_EXT & capabilitiesMask) {
                        isHashPartialSupported = CPA_TRUE;
                }
                break;
        /* All others support partial */
        default:
                isHashPartialSupported = CPA_TRUE;
                break;
        }
        switch (pSessionDesc->symOperation) {
        case CPA_CY_SYM_OP_CIPHER:
                isPartialSupported = isCipherPartialSupported;
                break;
        case CPA_CY_SYM_OP_HASH:
                isPartialSupported = isHashPartialSupported;
                break;
        case CPA_CY_SYM_OP_ALGORITHM_CHAINING:
                if (isCipherPartialSupported && isHashPartialSupported) {
                        isPartialSupported = CPA_TRUE;
                }
                break;
        case CPA_CY_SYM_OP_NONE:
                break;
        default:
                break;
        }

        if (ICP_QAT_FW_LA_USE_UCS_SLICE_TYPE == pSessionDesc->cipherSliceType) {
                /* UCS slice has no support for state flush and
                 * because of that is not able to do partial processing */
                isPartialSupported = CPA_FALSE;
        }

        pSessionDesc->isPartialSupported = isPartialSupported;
}

static void
LacAlgChain_CipherCDBuild_ForOptimisedCD(
    const CpaCySymCipherSetupData *pCipherData,
    lac_session_desc_t *pSessionDesc,
    icp_qat_fw_slice_t nextSlice,
    Cpa8U cipherOffsetInConstantsTable,
    Cpa8U *pOptimisedHwBlockBaseInDRAM,
    Cpa32U *pOptimisedHwBlockOffsetInDRAM)
{
        Cpa8U *pCipherKeyField = NULL;
        Cpa32U sizeInBytes = 0;
        pCipherKeyField = pOptimisedHwBlockBaseInDRAM;

        /* Need to build up the alternative CD for SHRAM Constants Table use
         * with an optimised content desc of 64 bytes for this case. Cipher key
         * will be in the Content desc in DRAM, The cipher config data
         * is now in the SHRAM constants table. */

        LacSymQat_CipherHwBlockPopulateKeySetup(
            pSessionDesc,
            pCipherData,
            pCipherData->cipherKeyLenInBytes,
            pSessionDesc->cipherSliceType,
            pCipherKeyField,
            &sizeInBytes);

        LacSymQat_CipherCtrlBlockWrite(&(pSessionDesc->shramReqCacheFtr),
                                       pSessionDesc->cipherAlgorithm,
                                       pSessionDesc->cipherKeyLenInBytes,
                                       pSessionDesc->cipherSliceType,
                                       nextSlice,
                                       cipherOffsetInConstantsTable);

        *pOptimisedHwBlockOffsetInDRAM += sizeInBytes;
}

static void
LacAlgChain_CipherCDBuild_ForSHRAM(const CpaCySymCipherSetupData *pCipherData,
                                   lac_session_desc_t *pSessionDesc,
                                   icp_qat_fw_slice_t nextSlice,
                                   Cpa8U cipherOffsetInConstantsTable)
{
        Cpa32U sizeInBytes = 0;
        Cpa8U *pCipherKeyField = NULL;
        /* Need to build up the alternative CD for SHRAM Constants Table use
         * Cipher key will be in the Request, The cipher config data is now in
         * the SHRAM constants table. And nothing is now stored in the content
         * desc */
        pCipherKeyField = (Cpa8U *)&(
            pSessionDesc->shramReqCacheHdr.cd_pars.s1.serv_specif_fields);

        LacSymQat_CipherHwBlockPopulateKeySetup(
            pSessionDesc,
            pCipherData,
            pCipherData->cipherKeyLenInBytes,
            pSessionDesc->cipherSliceType,
            pCipherKeyField,
            &sizeInBytes);

        LacSymQat_CipherCtrlBlockWrite(&(pSessionDesc->shramReqCacheFtr),
                                       pSessionDesc->cipherAlgorithm,
                                       pSessionDesc->cipherKeyLenInBytes,
                                       pSessionDesc->cipherSliceType,
                                       nextSlice,
                                       cipherOffsetInConstantsTable);
}

static void
LacAlgChain_CipherCDBuild(const CpaCySymCipherSetupData *pCipherData,
                          lac_session_desc_t *pSessionDesc,
                          icp_qat_fw_slice_t nextSlice,
                          Cpa8U cipherOffsetInConstantsTable,
                          icp_qat_fw_comn_flags *pCmnRequestFlags,
                          icp_qat_fw_serv_specif_flags *pLaCmdFlags,
                          Cpa8U *pHwBlockBaseInDRAM,
                          Cpa32U *pHwBlockOffsetInDRAM,
                          Cpa32U capabilitiesMask)
{
        Cpa8U *pCipherKeyField = NULL;
        Cpa8U cipherOffsetInReqQW = 0;
        Cpa32U sizeInBytes = 0;
        void *pCfgData = NULL;
        Cpa32U cfgOffset = 0;

        /* Construct the ContentDescriptor in DRAM */
        cipherOffsetInReqQW = (*pHwBlockOffsetInDRAM / LAC_QUAD_WORD_IN_BYTES);
        ICP_QAT_FW_LA_CIPH_AUTH_CFG_OFFSET_FLAG_SET(
            *pLaCmdFlags, ICP_QAT_FW_CIPH_AUTH_CFG_OFFSET_IN_CD_SETUP);

        /* construct cipherConfig in CD in DRAM */
        cfgOffset = *pHwBlockOffsetInDRAM;
        pCfgData = pHwBlockBaseInDRAM + cfgOffset;
        LacSymQat_CipherHwBlockPopulateCfgData(pSessionDesc,
                                               pCfgData,
                                               &sizeInBytes);

        ICP_QAT_FW_LA_SLICE_TYPE_SET(*pLaCmdFlags,
                                     pSessionDesc->cipherSliceType);

        *pHwBlockOffsetInDRAM += sizeInBytes;

        /* Cipher key will be in CD in DRAM.
         * The Request contains a ptr to the CD.
         * This ptr will be copied into the request later once the CD is
         * fully constructed, but the flag is set here.  */
        pCipherKeyField = pHwBlockBaseInDRAM + *pHwBlockOffsetInDRAM;
        ICP_QAT_FW_COMN_CD_FLD_TYPE_SET(*pCmnRequestFlags,
                                        QAT_COMN_CD_FLD_TYPE_64BIT_ADR);

        LacSymQat_CipherHwBlockPopulateKeySetup(
            pSessionDesc,
            pCipherData,
            pCipherData->cipherKeyLenInBytes,
            pSessionDesc->cipherSliceType,
            pCipherKeyField,
            &sizeInBytes);
        /* update offset */
        *pHwBlockOffsetInDRAM += sizeInBytes;

        LacSymQat_CipherCtrlBlockWrite(&(pSessionDesc->reqCacheFtr),
                                       pSessionDesc->cipherAlgorithm,
                                       pSessionDesc->cipherKeyLenInBytes,
                                       pSessionDesc->cipherSliceType,
                                       nextSlice,
                                       cipherOffsetInReqQW);
        if (NON_SPC != pSessionDesc->singlePassState) {
                LacSymQat_CipherCtrlBlockWrite(
                    &(pSessionDesc->reqSpcCacheFtr),
                    pSessionDesc->cipherAlgorithm,
                    pSessionDesc->cipherKeyLenInBytes,
                    pSessionDesc->cipherSliceType,
                    ICP_QAT_FW_SLICE_DRAM_WR,
                    cipherOffsetInReqQW);
        }
}

static void
LacAlgChain_HashCDBuild(
    const CpaCySymHashSetupData *pHashData,
    CpaInstanceHandle instanceHandle,
    lac_session_desc_t *pSessionDesc,
    icp_qat_fw_slice_t nextSlice,
    Cpa8U hashOffsetInConstantsTable,
    icp_qat_fw_comn_flags *pCmnRequestFlags,
    icp_qat_fw_serv_specif_flags *pLaCmdFlags,
    lac_sym_qat_hash_precompute_info_t *pPrecomputeData,
    lac_sym_qat_hash_precompute_info_t *pPrecomputeDataOptimisedCd,
    Cpa8U *pHwBlockBaseInDRAM,
    Cpa32U *pHwBlockOffsetInDRAM,
    Cpa8U *pOptimisedHwBlockBaseInDRAM,
    Cpa32U *pOptimisedHwBlockOffsetInDRAM)
{
        Cpa32U sizeInBytes = 0;
        Cpa32U hwBlockOffsetInQuadWords =
            *pHwBlockOffsetInDRAM / LAC_QUAD_WORD_IN_BYTES;

        /* build:
         * - the hash part of the ContentDescriptor in DRAM */
        /* - the hash part of the CD control block in the Request template */
        LacSymQat_HashContentDescInit(&(pSessionDesc->reqCacheFtr),
                                      instanceHandle,
                                      pHashData,
                                      pHwBlockBaseInDRAM,
                                      hwBlockOffsetInQuadWords,
                                      nextSlice,
                                      pSessionDesc->qatHashMode,
                                      CPA_FALSE,
                                      CPA_FALSE,
                                      pSessionDesc->useStatefulSha3ContentDesc,
                                      pPrecomputeData,
                                      &sizeInBytes);

        /* Using DRAM CD so update offset */
        *pHwBlockOffsetInDRAM += sizeInBytes;

        sizeInBytes = 0;

        if (pSessionDesc->useOptimisedContentDesc) {
                LacSymQat_HashContentDescInit(&(pSessionDesc->shramReqCacheFtr),
                                              instanceHandle,
                                              pHashData,
                                              pOptimisedHwBlockBaseInDRAM,
                                              hashOffsetInConstantsTable,
                                              nextSlice,
                                              pSessionDesc->qatHashMode,
                                              CPA_TRUE,
                                              CPA_TRUE,
                                              CPA_FALSE,
                                              pPrecomputeDataOptimisedCd,
                                              &sizeInBytes);

                *pOptimisedHwBlockOffsetInDRAM += sizeInBytes;
        } else if (pSessionDesc->useSymConstantsTable) {
                /* Need to build up the alternative CD for SHRAM Constants Table
                 * use */
                LacSymQat_HashContentDescInit(&(pSessionDesc->shramReqCacheFtr),
                                              instanceHandle,
                                              pHashData,
                                              pHwBlockBaseInDRAM,
                                              hashOffsetInConstantsTable,
                                              nextSlice,
                                              pSessionDesc->qatHashMode,
                                              CPA_TRUE,
                                              CPA_FALSE,
                                              CPA_FALSE,
                                              pPrecomputeData,
                                              &sizeInBytes);
        }
}
static Cpa16U
LacAlgChain_GetCipherConfigSize(lac_session_desc_t *pSessionDesc)
{
        if (ICP_QAT_FW_LA_USE_UCS_SLICE_TYPE == pSessionDesc->cipherSliceType) {
                return sizeof(icp_qat_hw_ucs_cipher_config_t);
        } else {
                return sizeof(icp_qat_hw_cipher_config_t);
        }
}

static Cpa16U
LacAlgChain_GetCipherConfigOffset(lac_session_desc_t *pSessionDesc)
{
        Cpa16U offset = 0;

        if (CPA_CY_SYM_OP_ALGORITHM_CHAINING == pSessionDesc->symOperation ||
            SPC == pSessionDesc->singlePassState) {
                icp_qat_fw_cipher_auth_cd_ctrl_hdr_t *cd_ctrl =
                    (icp_qat_fw_cipher_auth_cd_ctrl_hdr_t *)&pSessionDesc
                        ->reqCacheFtr.cd_ctrl;
                offset = cd_ctrl->cipher_cfg_offset;
        } else if (CPA_CY_SYM_OP_CIPHER == pSessionDesc->symOperation) {
                icp_qat_fw_cipher_cd_ctrl_hdr_t *cd_ctrl =
                    (icp_qat_fw_cipher_cd_ctrl_hdr_t *)&pSessionDesc
                        ->reqCacheFtr.cd_ctrl;
                offset = cd_ctrl->cipher_cfg_offset;
        }

        return offset * LAC_QUAD_WORD_IN_BYTES;
}

CpaStatus
LacAlgChain_SessionAADUpdate(lac_session_desc_t *pSessionDesc,
                             Cpa32U newAADLength)
{
        icp_qat_la_bulk_req_ftr_t *req_ftr = &pSessionDesc->reqCacheFtr;
        icp_qat_la_auth_req_params_t *req_params = &req_ftr->serv_specif_rqpars;

        if (!pSessionDesc)
                return CPA_STATUS_FAIL;

        pSessionDesc->aadLenInBytes = newAADLength;
        req_params->u2.aad_sz =
            LAC_ALIGN_POW2_ROUNDUP(newAADLength, LAC_HASH_AES_GCM_BLOCK_SIZE);

        if (SPC == pSessionDesc->singlePassState) {
                Cpa8U *pHwBlockBaseInDRAM = NULL;
                Cpa32U hwBlockOffsetInDRAM = 0;
                Cpa32U pSizeInBytes = 0;
                CpaCySymCipherAlgorithm cipher = pSessionDesc->cipherAlgorithm;

                pHwBlockBaseInDRAM =
                    (Cpa8U *)pSessionDesc->contentDescInfo.pData;
                if (pSessionDesc->cipherDirection ==
                    CPA_CY_SYM_CIPHER_DIRECTION_DECRYPT) {
                        if (LAC_CIPHER_IS_GCM(cipher)) {
                                hwBlockOffsetInDRAM = LAC_QUADWORDS_TO_BYTES(
                                    LAC_SYM_QAT_CIPHER_GCM_SPC_OFFSET_IN_DRAM);
                        } else {
                                hwBlockOffsetInDRAM = LAC_QUADWORDS_TO_BYTES(
                                    LAC_SYM_QAT_CIPHER_CHACHA_SPC_OFFSET_IN_DRAM);
                        }
                }
                LacSymQat_CipherHwBlockPopulateCfgData(pSessionDesc,
                                                       pHwBlockBaseInDRAM +
                                                           hwBlockOffsetInDRAM,
                                                       &pSizeInBytes);
        }

        return CPA_STATUS_SUCCESS;
}

CpaStatus
LacAlgChain_SessionCipherKeyUpdate(lac_session_desc_t *pSessionDesc,
                                   Cpa8U *pCipherKey)
{
        CpaStatus status = CPA_STATUS_SUCCESS;

        if (pSessionDesc == NULL || pCipherKey == NULL)
                return CPA_STATUS_FAIL;

        if (LAC_CIPHER_IS_ARC4(pSessionDesc->cipherAlgorithm)) {
                LacSymQat_CipherArc4StateInit(
                    pCipherKey,
                    pSessionDesc->cipherKeyLenInBytes,
                    pSessionDesc->cipherARC4InitialState);
        } else {
                CpaCySymCipherSetupData cipherSetupData = { 0 };
                Cpa32U sizeInBytes;
                Cpa8U *pCipherKeyField;
                Cpa16U cipherConfigSize;
                Cpa16U cipherConfigOffset;
                sal_qat_content_desc_info_t *pCdInfo =
                    &(pSessionDesc->contentDescInfo);

                cipherSetupData.cipherAlgorithm = pSessionDesc->cipherAlgorithm;
                cipherSetupData.cipherKeyLenInBytes =
                    pSessionDesc->cipherKeyLenInBytes;
                cipherSetupData.pCipherKey = pCipherKey;
                cipherSetupData.cipherDirection = pSessionDesc->cipherDirection;

                cipherConfigSize =
                    LacAlgChain_GetCipherConfigSize(pSessionDesc);
                cipherConfigOffset =
                    LacAlgChain_GetCipherConfigOffset(pSessionDesc);

                pCipherKeyField = (Cpa8U *)pCdInfo->pData + cipherConfigOffset +
                    cipherConfigSize;

                switch (pSessionDesc->symOperation) {
                case CPA_CY_SYM_OP_CIPHER: {
                        LacSymQat_CipherHwBlockPopulateKeySetup(
                            pSessionDesc,
                            &(cipherSetupData),
                            cipherSetupData.cipherKeyLenInBytes,
                            pSessionDesc->cipherSliceType,
                            pCipherKeyField,
                            &sizeInBytes);

                        if (pSessionDesc->useSymConstantsTable) {
                                pCipherKeyField = (Cpa8U *)&(
                                    pSessionDesc->shramReqCacheHdr.cd_pars.s1
                                        .serv_specif_fields);

                                LacSymQat_CipherHwBlockPopulateKeySetup(
                                    pSessionDesc,
                                    &(cipherSetupData),
                                    cipherSetupData.cipherKeyLenInBytes,
                                    pSessionDesc->cipherSliceType,
                                    pCipherKeyField,
                                    &sizeInBytes);
                        }
                } break;

                case CPA_CY_SYM_OP_ALGORITHM_CHAINING: {
                        LacSymQat_CipherHwBlockPopulateKeySetup(
                            pSessionDesc,
                            &(cipherSetupData),
                            cipherSetupData.cipherKeyLenInBytes,
                            pSessionDesc->cipherSliceType,
                            pCipherKeyField,
                            &sizeInBytes);
                } break;

                default:
                        LAC_LOG_ERROR("Invalid sym operation\n");
                        status = CPA_STATUS_INVALID_PARAM;
                        break;
                }
        }
        return status;
}

CpaStatus
LacAlgChain_SessionAuthKeyUpdate(lac_session_desc_t *pSessionDesc,
                                 Cpa8U *pAuthKey)
{
        CpaStatus status = CPA_STATUS_SUCCESS;
        Cpa8U *pHwBlockBaseInDRAM = NULL;
        Cpa8U *pOutHashSetup = NULL;
        Cpa8U *pInnerState1 = NULL;
        Cpa8U *pInnerState2 = NULL;
        CpaCySymSessionSetupData sessionSetup = { 0 };
        Cpa16U cipherConfigSize;

        if (pSessionDesc == NULL || pAuthKey == NULL)
                return CPA_STATUS_FAIL;

        cipherConfigSize = LacAlgChain_GetCipherConfigSize(pSessionDesc);

        icp_qat_fw_cipher_auth_cd_ctrl_hdr_t *cd_ctrl =
            (icp_qat_fw_cipher_auth_cd_ctrl_hdr_t *)&pSessionDesc->reqCacheFtr
                .cd_ctrl;

        pHwBlockBaseInDRAM = (Cpa8U *)pSessionDesc->contentDescInfo.pData;

        sessionSetup.hashSetupData.hashAlgorithm = pSessionDesc->hashAlgorithm;
        sessionSetup.hashSetupData.hashMode = pSessionDesc->hashMode;
        sessionSetup.hashSetupData.authModeSetupData.authKey = pAuthKey;
        sessionSetup.hashSetupData.authModeSetupData.authKeyLenInBytes =
            pSessionDesc->authKeyLenInBytes;
        sessionSetup.hashSetupData.authModeSetupData.aadLenInBytes =
            pSessionDesc->aadLenInBytes;
        sessionSetup.hashSetupData.digestResultLenInBytes =
            pSessionDesc->hashResultSize;

        sessionSetup.cipherSetupData.cipherAlgorithm =
            pSessionDesc->cipherAlgorithm;
        sessionSetup.cipherSetupData.cipherKeyLenInBytes =
            pSessionDesc->cipherKeyLenInBytes;

        /* Calculate hash states offsets */
        pInnerState1 = pHwBlockBaseInDRAM +
            cd_ctrl->hash_cfg_offset * LAC_QUAD_WORD_IN_BYTES +
            sizeof(icp_qat_hw_auth_setup_t);

        pInnerState2 = pInnerState1 + cd_ctrl->inner_state1_sz;

        pOutHashSetup = pInnerState2 + cd_ctrl->inner_state2_sz;

        /* Calculate offset of cipher key */
        if (pSessionDesc->laCmdId == ICP_QAT_FW_LA_CMD_CIPHER_HASH) {
                sessionSetup.cipherSetupData.pCipherKey =
                    (Cpa8U *)pHwBlockBaseInDRAM + cipherConfigSize;
        } else if (pSessionDesc->laCmdId == ICP_QAT_FW_LA_CMD_HASH_CIPHER) {
                sessionSetup.cipherSetupData.pCipherKey =
                    pOutHashSetup + cipherConfigSize;
        } else if (SPC == pSessionDesc->singlePassState) {
                CpaCySymCipherAlgorithm cipher = pSessionDesc->cipherAlgorithm;
                Cpa32U hwBlockOffsetInDRAM = 0;

                if (pSessionDesc->cipherDirection ==
                    CPA_CY_SYM_CIPHER_DIRECTION_ENCRYPT) {
                        sessionSetup.cipherSetupData.pCipherKey =
                            (Cpa8U *)pHwBlockBaseInDRAM + cipherConfigSize;
                } else {
                        if (LAC_CIPHER_IS_GCM(cipher))
                                hwBlockOffsetInDRAM = LAC_QUADWORDS_TO_BYTES(
                                    LAC_SYM_QAT_CIPHER_GCM_SPC_OFFSET_IN_DRAM);
                        else
                                hwBlockOffsetInDRAM = LAC_QUADWORDS_TO_BYTES(
                                    LAC_SYM_QAT_CIPHER_CHACHA_SPC_OFFSET_IN_DRAM);
                        sessionSetup.cipherSetupData.pCipherKey =
                            (Cpa8U *)pHwBlockBaseInDRAM + hwBlockOffsetInDRAM +
                            cipherConfigSize;
                }
        }

        if (!sessionSetup.cipherSetupData.pCipherKey)
                return CPA_STATUS_FAIL;

        if (CPA_CY_SYM_HASH_SHA3_256 == pSessionDesc->hashAlgorithm) {
                if (CPA_FALSE == pSessionDesc->isAuthEncryptOp) {
                        lac_sym_qat_hash_state_buffer_info_t
                            *pHashStateBufferInfo =
                                &(pSessionDesc->hashStateBufferInfo);

                        sal_crypto_service_t *pService =
                            (sal_crypto_service_t *)pSessionDesc->pInstance;

                        status = LacHash_StatePrefixAadBufferInit(
                            &(pService->generic_service_info),
                            &(sessionSetup.hashSetupData),
                            &(pSessionDesc->reqCacheFtr),
                            pSessionDesc->qatHashMode,
                            pSessionDesc->hashStatePrefixBuffer,
                            pHashStateBufferInfo);
                        /* SHRAM Constants Table not used for Auth-Enc */
                }
        } else if (CPA_CY_SYM_HASH_SNOW3G_UIA2 == pSessionDesc->hashAlgorithm) {
                Cpa8U *authKey = (Cpa8U *)pOutHashSetup + cipherConfigSize;
                memcpy(authKey, pAuthKey, pSessionDesc->authKeyLenInBytes);
        } else if (CPA_CY_SYM_HASH_ZUC_EIA3 == pSessionDesc->hashAlgorithm ||
                   CPA_CY_SYM_HASH_AES_CBC_MAC == pSessionDesc->hashAlgorithm) {
                memcpy(pInnerState2, pAuthKey, pSessionDesc->authKeyLenInBytes);
        } else if (CPA_CY_SYM_HASH_AES_CMAC == pSessionDesc->hashAlgorithm ||
                   CPA_CY_SYM_HASH_KASUMI_F9 == pSessionDesc->hashAlgorithm ||
                   IS_HASH_MODE_1(pSessionDesc->qatHashMode)) {
                if (CPA_CY_SYM_HASH_AES_CMAC == pSessionDesc->hashAlgorithm) {
                        memset(pInnerState2, 0, cd_ctrl->inner_state2_sz);
                }

                /* Block messages until precompute is completed */
                pSessionDesc->nonBlockingOpsInProgress = CPA_FALSE;

                status = LacHash_PrecomputeDataCreate(
                    pSessionDesc->pInstance,
                    (CpaCySymSessionSetupData *)&(sessionSetup),
                    LacSymAlgChain_HashPrecomputeDoneCb,
                    pSessionDesc,
                    pSessionDesc->hashStatePrefixBuffer,
                    pInnerState1,
                    pInnerState2);
        }

        return status;
}

static void
buildCmdData(sal_crypto_service_t *pService,
             lac_session_desc_t *pSessionDesc,
             CpaCySymAlgChainOrder *chainOrder,
             Cpa16U *proto,
             icp_qat_fw_serv_specif_flags *laCmdFlags,
             icp_qat_fw_comn_flags *cmnRequestFlags)
{
        /* LW 28 is used to set hash flags for AlgChaining. */
        icp_qat_fw_cipher_auth_cd_ctrl_hdr_t *cd_ctrl =
            (icp_qat_fw_cipher_auth_cd_ctrl_hdr_t *)&pSessionDesc->reqCacheFtr
                .cd_ctrl;

        /* proto refers to Protocol Flags, which is legacy FW <=> IA interface
         * for ZUC and Snow3G. Use extended protocol flags for AlgChaining.
         */
        *proto = ICP_QAT_FW_LA_NO_PROTO; /* no CCM/GCM/Snow3G */

        switch (pSessionDesc->symOperation) {
        case CPA_CY_SYM_OP_CIPHER:
                pSessionDesc->laCmdId = ICP_QAT_FW_LA_CMD_CIPHER;

                if (CPA_CY_SYM_CIPHER_SNOW3G_UEA2 ==
                    pSessionDesc->cipherAlgorithm) {
                        *proto = ICP_QAT_FW_LA_SNOW_3G_PROTO;
                } else if (CPA_CY_SYM_CIPHER_ZUC_EEA3 ==
                           pSessionDesc->cipherAlgorithm) {
                        *proto = ICP_QAT_FW_LA_ZUC_3G_PROTO;
                }
                if (LAC_CIPHER_IS_CCM(pSessionDesc->cipherAlgorithm)) {
                        *proto = ICP_QAT_FW_LA_CCM_PROTO;
                }
                break;

        case CPA_CY_SYM_OP_HASH:
                pSessionDesc->laCmdId = ICP_QAT_FW_LA_CMD_AUTH;
                if (CPA_CY_SYM_HASH_SNOW3G_UIA2 ==
                    pSessionDesc->hashAlgorithm) {
                        *proto = ICP_QAT_FW_LA_SNOW_3G_PROTO;
                } else if (CPA_CY_SYM_HASH_ZUC_EIA3 ==
                           pSessionDesc->hashAlgorithm) {
                        *proto = ICP_QAT_FW_LA_ZUC_3G_PROTO;
                }
                break;

        case CPA_CY_SYM_OP_ALGORITHM_CHAINING:
                if (LAC_CIPHER_IS_CCM(pSessionDesc->cipherAlgorithm)) {
                        *proto = ICP_QAT_FW_LA_CCM_PROTO;

                        /* Derive chainOrder from direction for isAuthEncryptOp
                         * cases */
                        /* For CCM & GCM modes: force digest verify flag _TRUE
                           for decrypt and _FALSE for encrypt. For all other
                           cases use user defined value */

                        if (CPA_CY_SYM_CIPHER_DIRECTION_ENCRYPT ==
                            pSessionDesc->cipherDirection) {
                                *chainOrder =
                                    CPA_CY_SYM_ALG_CHAIN_ORDER_HASH_THEN_CIPHER;
                                pSessionDesc->digestVerify = CPA_FALSE;
                        } else {
                                *chainOrder =
                                    CPA_CY_SYM_ALG_CHAIN_ORDER_CIPHER_THEN_HASH;
                                if (CPA_TRUE == pService->forceAEADMacVerify) {
                                        pSessionDesc->digestVerify = CPA_TRUE;
                                }
                        }
                } else if (LAC_CIPHER_IS_GCM(pSessionDesc->cipherAlgorithm)) {
                        *proto = ICP_QAT_FW_LA_GCM_PROTO;

                        /* Derive chainOrder from direction for isAuthEncryptOp
                         * cases */
                        /* For CCM & GCM modes: force digest verify flag _TRUE
                           for decrypt and _FALSE for encrypt. For all other
                           cases use user defined value */

                        if (CPA_CY_SYM_CIPHER_DIRECTION_ENCRYPT ==
                            pSessionDesc->cipherDirection) {
                                *chainOrder =
                                    CPA_CY_SYM_ALG_CHAIN_ORDER_CIPHER_THEN_HASH;
                                pSessionDesc->digestVerify = CPA_FALSE;
                        } else {
                                *chainOrder =
                                    CPA_CY_SYM_ALG_CHAIN_ORDER_HASH_THEN_CIPHER;
                                if (CPA_TRUE == pService->forceAEADMacVerify) {
                                        pSessionDesc->digestVerify = CPA_TRUE;
                                }
                        }
                } else if (LAC_CIPHER_IS_CHACHA(
                               pSessionDesc->cipherAlgorithm)) {
                        if (CPA_CY_SYM_CIPHER_DIRECTION_ENCRYPT ==
                            pSessionDesc->cipherDirection) {
                                *chainOrder =
                                    CPA_CY_SYM_ALG_CHAIN_ORDER_CIPHER_THEN_HASH;
                        } else {
                                *chainOrder =
                                    CPA_CY_SYM_ALG_CHAIN_ORDER_HASH_THEN_CIPHER;
                        }
                } else {
                        pSessionDesc->isAuthEncryptOp = CPA_FALSE;

                        if (CPA_CY_SYM_CIPHER_SNOW3G_UEA2 ==
                            pSessionDesc->cipherAlgorithm) {
                                *proto = ICP_QAT_FW_LA_SNOW_3G_PROTO;
                        } else if (CPA_CY_SYM_CIPHER_ZUC_EEA3 ==
                                   pSessionDesc->cipherAlgorithm) {
                                *proto = ICP_QAT_FW_LA_ZUC_3G_PROTO;
                        }

                        if (CPA_CY_SYM_HASH_SNOW3G_UIA2 ==
                            pSessionDesc->hashAlgorithm) {
                                /* Need to set LW 28 hash flags as well. */
                                ICP_QAT_FW_HASH_FLAG_SNOW3G_UIA2_SET(
                                    cd_ctrl->hash_flags, QAT_FW_LA_SNOW3G_UIA2);
                        } else if (CPA_CY_SYM_HASH_ZUC_EIA3 ==
                                   pSessionDesc->hashAlgorithm) {
                                /* Need to set LW 28 hash flags as well. */
                                ICP_QAT_FW_HASH_FLAG_ZUC_EIA3_SET(
                                    cd_ctrl->hash_flags, QAT_FW_LA_ZUC_EIA3);
                        }
                }

                if (CPA_CY_SYM_ALG_CHAIN_ORDER_CIPHER_THEN_HASH ==
                    *chainOrder) {
                        pSessionDesc->laCmdId = ICP_QAT_FW_LA_CMD_CIPHER_HASH;
                } else if (CPA_CY_SYM_ALG_CHAIN_ORDER_HASH_THEN_CIPHER ==
                           *chainOrder) {
                        pSessionDesc->laCmdId = ICP_QAT_FW_LA_CMD_HASH_CIPHER;
                }
                break;

        default:
                break;
        }

        /*
         * Build the header flags with the default settings for this session.
         */
        if (pSessionDesc->isDPSession == CPA_TRUE) {
                *cmnRequestFlags =
                    ICP_QAT_FW_COMN_FLAGS_BUILD(QAT_COMN_CD_FLD_TYPE_64BIT_ADR,
                                                LAC_SYM_DP_QAT_PTR_TYPE);
        } else {
                *cmnRequestFlags =
                    ICP_QAT_FW_COMN_FLAGS_BUILD(QAT_COMN_CD_FLD_TYPE_64BIT_ADR,
                                                LAC_SYM_DEFAULT_QAT_PTR_TYPE);
        }

        LacSymQat_LaSetDefaultFlags(laCmdFlags, pSessionDesc->symOperation);

        return;
}

static void
updateLaCmdFlags(lac_session_desc_t *pSessionDesc,
                 Cpa16U proto,
                 icp_qat_fw_serv_specif_flags *laCmdFlags)
{
        if (pSessionDesc->isAuth) {
                if (pSessionDesc->digestVerify) {
                        ICP_QAT_FW_LA_CMP_AUTH_SET(*laCmdFlags,
                                                   ICP_QAT_FW_LA_CMP_AUTH_RES);
                        ICP_QAT_FW_LA_RET_AUTH_SET(
                            *laCmdFlags, ICP_QAT_FW_LA_NO_RET_AUTH_RES);
                } else {
                        ICP_QAT_FW_LA_RET_AUTH_SET(*laCmdFlags,
                                                   ICP_QAT_FW_LA_RET_AUTH_RES);
                        ICP_QAT_FW_LA_CMP_AUTH_SET(
                            *laCmdFlags, ICP_QAT_FW_LA_NO_CMP_AUTH_RES);
                }
        }

        if ((CPA_CY_SYM_CIPHER_ZUC_EEA3 == pSessionDesc->cipherAlgorithm) ||
            (CPA_CY_SYM_HASH_ZUC_EIA3 == pSessionDesc->hashAlgorithm)) {
                /* New bit position (12) for ZUC. The FW provides a specific
                 * macro to use to set the ZUC proto flag. With the new FW I/F
                 * this needs to be set for both Cipher and Auth */
                ICP_QAT_FW_LA_ZUC_3G_PROTO_FLAG_SET(*laCmdFlags, proto);
        } else {
                /* Configure the common header */
                ICP_QAT_FW_LA_PROTO_SET(*laCmdFlags, proto);
        }

        /* set Append flag, if digest is appended */
        if (pSessionDesc->digestIsAppended) {
                ICP_QAT_FW_LA_DIGEST_IN_BUFFER_SET(
                    *laCmdFlags, ICP_QAT_FW_LA_DIGEST_IN_BUFFER);
        } else {
                ICP_QAT_FW_LA_DIGEST_IN_BUFFER_SET(
                    *laCmdFlags, ICP_QAT_FW_LA_NO_DIGEST_IN_BUFFER);
        }
}

static lac_single_pass_state_t
LacSymAlgChain_GetSpcState(CpaCySymCipherAlgorithm cipher,
                           CpaCySymHashAlgorithm hash,
                           Cpa32U capabilitiesMask)
{
        lac_single_pass_state_t state = NON_SPC;
        if (capabilitiesMask & ICP_ACCEL_CAPABILITIES_CHACHA_POLY) {
                switch (cipher) {
                case CPA_CY_SYM_CIPHER_CHACHA: {
                        if (CPA_CY_SYM_HASH_POLY == hash)
                                state = SPC;
                        break;
                }
                case CPA_CY_SYM_CIPHER_AES_GCM: {
                        if ((CPA_CY_SYM_HASH_AES_GCM == hash) ||
                            (CPA_CY_SYM_HASH_AES_GMAC == hash))
                                state = LIKELY_SPC;
                        break;
                }
                case CPA_CY_SYM_CIPHER_AES_CCM: {
                        if (LAC_CIPHER_AES_V2(capabilitiesMask))
                                state = SPC;
                }
                default:
                        /* Do Nothing as it is NON_SPC */
                        break;
                }
        }
        return state;
}

static CpaBoolean
LacAlgChain_UseStatefulSha3ContentDesc(CpaBoolean partialsNotRequired,
                                       Cpa32U capabilitiesMask,
                                       lac_session_desc_t *pSessionDesc)
{
        CpaBoolean hasSha3Ext =
            ICP_ACCEL_CAPABILITIES_SHA3_EXT & capabilitiesMask;
        CpaBoolean useStatefulSha3DescFlag = CPA_FALSE;

        if (hasSha3Ext && !partialsNotRequired &&
            (pSessionDesc->symOperation == CPA_CY_SYM_OP_HASH) &&
            LAC_HASH_IS_SHA3(pSessionDesc->hashAlgorithm)) {
                useStatefulSha3DescFlag = CPA_TRUE;
        }
        return useStatefulSha3DescFlag;
}

/** @ingroup LacAlgChain */
CpaStatus
LacAlgChain_SessionInit(const CpaInstanceHandle instanceHandle,
                        const CpaCySymSessionSetupData *pSessionSetupData,
                        lac_session_desc_t *pSessionDesc)
{
        CpaStatus stat, status = CPA_STATUS_SUCCESS;
        sal_qat_content_desc_info_t *pCdInfo = NULL;
        sal_qat_content_desc_info_t *pCdInfoOptimised = NULL;
        sal_crypto_service_t *pService = (sal_crypto_service_t *)instanceHandle;
        Cpa32U capabilitiesMask =
            pService->generic_service_info.capabilitiesMask;
        Cpa8U *pHwBlockBaseInDRAM = NULL;
        Cpa8U *pOptimisedHwBlockBaseInDRAM = NULL;
        Cpa32U hwBlockOffsetInDRAM = 0;
        Cpa32U optimisedHwBlockOffsetInDRAM = 0;
        Cpa8U cipherOffsetInConstantsTable = 0;
        Cpa8U hashOffsetInConstantsTable = 0;
        icp_qat_fw_comn_flags cmnRequestFlags = 0;
        icp_qat_fw_comn_req_t *pMsg = NULL;
        icp_qat_fw_comn_req_t *pMsgS = NULL;
        const CpaCySymCipherSetupData *pCipherData;
        const CpaCySymHashSetupData *pHashData;
        Cpa16U proto = ICP_QAT_FW_LA_NO_PROTO; /* no CCM/GCM/Snow3G */
        CpaCySymAlgChainOrder chainOrder = 0;
        lac_sym_qat_hash_precompute_info_t precomputeData = { 0 };
        lac_sym_qat_hash_precompute_info_t precomputeDataOptimisedCd = { 0 };

        pCipherData = &(pSessionSetupData->cipherSetupData);
        pHashData = &(pSessionSetupData->hashSetupData);

        /*-------------------------------------------------------------------------
         * Populate session data
         *-----------------------------------------------------------------------*/

        /* Initialise Request Queue */
        stat = LAC_SPINLOCK_INIT(&pSessionDesc->requestQueueLock);
        if (CPA_STATUS_SUCCESS != stat) {
                LAC_LOG_ERROR("Spinlock init failed for sessionLock");
                return CPA_STATUS_RESOURCE;
        }

        pSessionDesc->pRequestQueueHead = NULL;
        pSessionDesc->pRequestQueueTail = NULL;
        pSessionDesc->nonBlockingOpsInProgress = CPA_TRUE;
        pSessionDesc->pInstance = instanceHandle;
        pSessionDesc->digestIsAppended = pSessionSetupData->digestIsAppended;
        pSessionDesc->digestVerify = pSessionSetupData->verifyDigest;

        /* Reset the pending callback counter */
        qatUtilsAtomicSet(0, &pSessionDesc->u.pendingCbCount);
        qatUtilsAtomicSet(0, &pSessionDesc->u.pendingDpCbCount);

        /* Partial state must be set to full, to indicate that next packet
         * expected on the session is a full packet or the start of a
         * partial packet. */
        pSessionDesc->partialState = CPA_CY_SYM_PACKET_TYPE_FULL;

        pSessionDesc->symOperation = pSessionSetupData->symOperation;
        switch (pSessionDesc->symOperation) {
        case CPA_CY_SYM_OP_CIPHER:
                pSessionDesc->isCipher = CPA_TRUE;
                pSessionDesc->isAuth = CPA_FALSE;
                pSessionDesc->isAuthEncryptOp = CPA_FALSE;
                pSessionDesc->singlePassState = NON_SPC;
                break;
        case CPA_CY_SYM_OP_HASH:
                pSessionDesc->isCipher = CPA_FALSE;
                pSessionDesc->isAuth = CPA_TRUE;
                pSessionDesc->isAuthEncryptOp = CPA_FALSE;
                pSessionDesc->singlePassState = NON_SPC;
                break;
        case CPA_CY_SYM_OP_ALGORITHM_CHAINING: {
                pSessionDesc->isCipher = CPA_TRUE;
                pSessionDesc->isAuth = CPA_TRUE;
                pSessionDesc->singlePassState =
                    LacSymAlgChain_GetSpcState(pCipherData->cipherAlgorithm,
                                               pHashData->hashAlgorithm,
                                               capabilitiesMask);

                switch (pSessionSetupData->cipherSetupData.cipherAlgorithm) {
                case CPA_CY_SYM_CIPHER_AES_CCM: {
                        pSessionDesc->isAuthEncryptOp = CPA_TRUE;
                        pSessionDesc->digestIsAppended = CPA_TRUE;
                } break;
                case CPA_CY_SYM_CIPHER_AES_GCM:
                case CPA_CY_SYM_CIPHER_CHACHA:
                        pSessionDesc->isAuthEncryptOp = CPA_TRUE;
                        break;
                default: {
                        pSessionDesc->isAuthEncryptOp = CPA_FALSE;
                        /* Use the chainOrder passed in */
                        chainOrder = pSessionSetupData->algChainOrder;
                        if ((chainOrder !=
                             CPA_CY_SYM_ALG_CHAIN_ORDER_HASH_THEN_CIPHER) &&
                            (chainOrder !=
                             CPA_CY_SYM_ALG_CHAIN_ORDER_CIPHER_THEN_HASH)) {
                                LAC_INVALID_PARAM_LOG("algChainOrder");
                                return CPA_STATUS_INVALID_PARAM;
                        }
                } break;
                }
        } break;
        default:
                pSessionDesc->singlePassState = NON_SPC;
                break;
        }

        if (pSessionDesc->isCipher) {
                /* Populate cipher specific session data */

                status = LacCipher_SessionSetupDataCheck(pCipherData,
                                                         capabilitiesMask);

                if (CPA_STATUS_SUCCESS == status) {
                        pSessionDesc->cipherAlgorithm =
                            pCipherData->cipherAlgorithm;
                        pSessionDesc->cipherKeyLenInBytes =
                            pCipherData->cipherKeyLenInBytes;
                        pSessionDesc->cipherDirection =
                            pCipherData->cipherDirection;

                        /* ARC4 base key isn't added to the content descriptor,
                         * because we don't need to pass it directly to the QAT
                         * engine. Instead an initial cipher state & key matrix
                         * is derived from the base key and provided to the QAT
                         * through the state pointer in the request params.
                         * We'll store this initial state in the session
                         * descriptor. */

                        if (LAC_CIPHER_IS_ARC4(pSessionDesc->cipherAlgorithm)) {
                                LacSymQat_CipherArc4StateInit(
                                    pCipherData->pCipherKey,
                                    pSessionDesc->cipherKeyLenInBytes,
                                    pSessionDesc->cipherARC4InitialState);

                                pSessionDesc->cipherARC4InitialStatePhysAddr =
                                    LAC_OS_VIRT_TO_PHYS_EXTERNAL(
                                        pService->generic_service_info,
                                        pSessionDesc->cipherARC4InitialState);

                                if (0 ==
                                    pSessionDesc
                                        ->cipherARC4InitialStatePhysAddr) {
                                        LAC_LOG_ERROR(
                                            "Unable to get the physical address of "
                                            "the initial state for ARC4\n");
                                        status = CPA_STATUS_FAIL;
                                }
                        }
                }
        }

        if ((CPA_STATUS_SUCCESS == status) && pSessionDesc->isAuth) {
                /* Populate auth-specific session data */
                const CpaCySymHashSetupData *pHashData =
                    &pSessionSetupData->hashSetupData;

                status = LacHash_HashContextCheck(instanceHandle, pHashData);
                if (CPA_STATUS_SUCCESS == status) {
                        pSessionDesc->hashResultSize =
                            pHashData->digestResultLenInBytes;
                        pSessionDesc->hashMode = pHashData->hashMode;
                        pSessionDesc->hashAlgorithm = pHashData->hashAlgorithm;

                        /* Save the authentication key length for further update
                         */
                        if (CPA_CY_SYM_HASH_MODE_AUTH == pHashData->hashMode) {
                                pSessionDesc->authKeyLenInBytes =
                                    pHashData->authModeSetupData
                                        .authKeyLenInBytes;
                        }
                        if (CPA_TRUE == pSessionDesc->isAuthEncryptOp ||
                            (pHashData->hashAlgorithm ==
                                 CPA_CY_SYM_HASH_SNOW3G_UIA2 ||
                             pHashData->hashAlgorithm ==
                                 CPA_CY_SYM_HASH_ZUC_EIA3)) {
                                pSessionDesc->aadLenInBytes =
                                    pHashData->authModeSetupData.aadLenInBytes;
                        }

                        /* Set the QAT hash mode */
                        if ((pHashData->hashMode ==
                             CPA_CY_SYM_HASH_MODE_NESTED) ||
                            (pHashData->hashMode ==
                             CPA_CY_SYM_HASH_MODE_PLAIN) ||
                            (pHashData->hashMode == CPA_CY_SYM_HASH_MODE_AUTH &&
                             pHashData->hashAlgorithm ==
                                 CPA_CY_SYM_HASH_AES_CBC_MAC)) {
                                pSessionDesc->qatHashMode =
                                    ICP_QAT_HW_AUTH_MODE0;
                        } else /* CPA_CY_SYM_HASH_MODE_AUTH
                                  && anything except CPA_CY_SYM_HASH_AES_CBC_MAC
                                */
                        {
                                if (IS_HMAC_ALG(pHashData->hashAlgorithm)) {
                                        /* SHA3 HMAC and SM3  do not support
                                         * precompute, force MODE2 for AUTH */
                                        if (LAC_HASH_IS_SHA3(
                                                pHashData->hashAlgorithm) ||
                                            (CPA_CY_SYM_HASH_SM3 ==
                                             pHashData->hashAlgorithm)) {
                                                pSessionDesc->qatHashMode =
                                                    ICP_QAT_HW_AUTH_MODE2;
                                        } else {
                                                pSessionDesc->qatHashMode =
                                                    pService->qatHmacMode;
                                        }
                                } else if (CPA_CY_SYM_HASH_ZUC_EIA3 ==
                                           pHashData->hashAlgorithm) {
                                        pSessionDesc->qatHashMode =
                                            ICP_QAT_HW_AUTH_MODE0;
                                } else {
                                        pSessionDesc->qatHashMode =
                                            ICP_QAT_HW_AUTH_MODE1;
                                }
                        }
                }
        }

        /*-------------------------------------------------------------------------
         * build the message templates
         * create two content descriptors in the case we can support using SHRAM
         * constants and an optimised content descriptor. we have to do this in
         * case of partials. 64 byte content descriptor is used in the SHRAM
         * case for AES-128-HMAC-SHA1
         *-----------------------------------------------------------------------*/
        if (CPA_STATUS_SUCCESS == status) {
                pSessionDesc->cipherSliceType =
                    LacCipher_GetCipherSliceType(pService,
                                                 pSessionDesc->cipherAlgorithm,
                                                 pSessionDesc->hashAlgorithm);

                LacSymCheck_IsPartialSupported(capabilitiesMask, pSessionDesc);
                pSessionDesc->useOptimisedContentDesc = CPA_FALSE;
                pSessionDesc->useStatefulSha3ContentDesc = CPA_FALSE;

                /* Build configuration data */
                buildCmdData(pService,
                             pSessionDesc,
                             &chainOrder,
                             &proto,
                             &pSessionDesc->laCmdFlags,
                             &cmnRequestFlags);

                if (ICP_QAT_FW_LA_USE_UCS_SLICE_TYPE ==
                    pSessionDesc->cipherSliceType)
                        pSessionDesc->useSymConstantsTable = CPA_FALSE;
                else
                        pSessionDesc->useSymConstantsTable =
                            LacSymQat_UseSymConstantsTable(
                                pSessionDesc,
                                &cipherOffsetInConstantsTable,
                                &hashOffsetInConstantsTable);

                /* for a certain combination of Algorithm Chaining we want to
                   use an optimised cd block */

                if (pSessionDesc->symOperation ==
                        CPA_CY_SYM_OP_ALGORITHM_CHAINING &&
                    pSessionDesc->useSymConstantsTable == CPA_TRUE) {
                        pSessionDesc->useOptimisedContentDesc =
                            LacSymQat_UseOptimisedContentDesc(pSessionDesc);
                }

                /* check whether we need to construct content desc for stateful
                 * SHA3 */
                pSessionDesc->useStatefulSha3ContentDesc =
                    LacAlgChain_UseStatefulSha3ContentDesc(
                        pSessionSetupData->partialsNotRequired,
                        capabilitiesMask,
                        pSessionDesc);

                /* setup some convenience pointers */
                pCdInfo = &(pSessionDesc->contentDescInfo);
                pHwBlockBaseInDRAM = (Cpa8U *)pCdInfo->pData;
                hwBlockOffsetInDRAM = 0;

                /* set up the pointer for the optimised content desc if this is
                 * possible we still have to support both cd types in case of
                 * partials so we construct both */
                if (pSessionDesc->useOptimisedContentDesc == CPA_TRUE) {
                        pCdInfoOptimised =
                            &(pSessionDesc->contentDescOptimisedInfo);
                        pOptimisedHwBlockBaseInDRAM =
                            (Cpa8U *)pCdInfoOptimised->pData;
                        optimisedHwBlockOffsetInDRAM = 0;
                }

                switch (pSessionDesc->symOperation) {
                case CPA_CY_SYM_OP_CIPHER: {
                        LacAlgChain_CipherCDBuild(
                            pCipherData,
                            pSessionDesc,
                            ICP_QAT_FW_SLICE_DRAM_WR,
                            cipherOffsetInConstantsTable,
                            &pSessionDesc->cmnRequestFlags,
                            &pSessionDesc->laCmdFlags,
                            pHwBlockBaseInDRAM,
                            &hwBlockOffsetInDRAM,
                            capabilitiesMask);

                        if (pSessionDesc->useSymConstantsTable) {
                                LacAlgChain_CipherCDBuild_ForSHRAM(
                                    pCipherData,
                                    pSessionDesc,
                                    ICP_QAT_FW_SLICE_DRAM_WR,
                                    cipherOffsetInConstantsTable);
                        }
                } break;
                case CPA_CY_SYM_OP_HASH:
                        LacAlgChain_HashCDBuild(pHashData,
                                                instanceHandle,
                                                pSessionDesc,
                                                ICP_QAT_FW_SLICE_NULL,
                                                hashOffsetInConstantsTable,
                                                &pSessionDesc->cmnRequestFlags,
                                                &pSessionDesc->laCmdFlags,
                                                &precomputeData,
                                                &precomputeDataOptimisedCd,
                                                pHwBlockBaseInDRAM,
                                                &hwBlockOffsetInDRAM,
                                                NULL,
                                                NULL);
                        break;
                case CPA_CY_SYM_OP_ALGORITHM_CHAINING:
                        /* For CCM/GCM, CPM firmware currently expects the
                         * cipher and hash h/w setup blocks to be arranged
                         * according to the chain order (Except for GCM/CCM,
                         * order doesn't actually matter as long as the config
                         * offsets are set correctly in CD control blocks
                         */
                        if (CPA_CY_SYM_ALG_CHAIN_ORDER_HASH_THEN_CIPHER ==
                            chainOrder) {
                                LacAlgChain_HashCDBuild(
                                    pHashData,
                                    instanceHandle,
                                    pSessionDesc,
                                    ICP_QAT_FW_SLICE_CIPHER,
                                    hashOffsetInConstantsTable,
                                    &pSessionDesc->cmnRequestFlags,
                                    &pSessionDesc->laCmdFlags,
                                    &precomputeData,
                                    &precomputeDataOptimisedCd,
                                    pHwBlockBaseInDRAM,
                                    &hwBlockOffsetInDRAM,
                                    pOptimisedHwBlockBaseInDRAM,
                                    &optimisedHwBlockOffsetInDRAM);

                                LacAlgChain_CipherCDBuild(
                                    pCipherData,
                                    pSessionDesc,
                                    ICP_QAT_FW_SLICE_DRAM_WR,
                                    cipherOffsetInConstantsTable,
                                    &pSessionDesc->cmnRequestFlags,
                                    &pSessionDesc->laCmdFlags,
                                    pHwBlockBaseInDRAM,
                                    &hwBlockOffsetInDRAM,
                                    capabilitiesMask);

                                if (pSessionDesc->useOptimisedContentDesc) {
                                        LacAlgChain_CipherCDBuild_ForOptimisedCD(
                                            pCipherData,
                                            pSessionDesc,
                                            ICP_QAT_FW_SLICE_DRAM_WR,
                                            cipherOffsetInConstantsTable,
                                            pOptimisedHwBlockBaseInDRAM,
                                            &optimisedHwBlockOffsetInDRAM);
                                }

                                if (NON_SPC != pSessionDesc->singlePassState) {
                                        pCdInfo->hwBlkSzQuadWords =
                                            (LAC_BYTES_TO_QUADWORDS(
                                                hwBlockOffsetInDRAM));
                                        pMsg = (icp_qat_fw_comn_req_t *)&(
                                            pSessionDesc->reqSpcCacheHdr);
                                        SalQatMsg_ContentDescHdrWrite(
                                            (icp_qat_fw_comn_req_t *)pMsg,
                                            pCdInfo);
                                }
                        } else /* CPA_CY_SYM_ALG_CHAIN_ORDER_CIPHER_THEN_HASH */
                        {
                                LacAlgChain_CipherCDBuild(
                                    pCipherData,
                                    pSessionDesc,
                                    ICP_QAT_FW_SLICE_AUTH,
                                    cipherOffsetInConstantsTable,
                                    &pSessionDesc->cmnRequestFlags,
                                    &pSessionDesc->laCmdFlags,
                                    pHwBlockBaseInDRAM,
                                    &hwBlockOffsetInDRAM,
                                    capabilitiesMask);

                                if (pSessionDesc->useOptimisedContentDesc) {
                                        LacAlgChain_CipherCDBuild_ForOptimisedCD(
                                            pCipherData,
                                            pSessionDesc,
                                            ICP_QAT_FW_SLICE_AUTH,
                                            cipherOffsetInConstantsTable,
                                            pOptimisedHwBlockBaseInDRAM,
                                            &optimisedHwBlockOffsetInDRAM);
                                }

                                if (NON_SPC != pSessionDesc->singlePassState) {
                                        pCdInfo->hwBlkSzQuadWords =
                                            LAC_BYTES_TO_QUADWORDS(
                                                hwBlockOffsetInDRAM);
                                        pMsg = (icp_qat_fw_comn_req_t *)&(
                                            pSessionDesc->reqSpcCacheHdr);
                                        SalQatMsg_ContentDescHdrWrite(
                                            (icp_qat_fw_comn_req_t *)pMsg,
                                            pCdInfo);
                                }
                                LacAlgChain_HashCDBuild(
                                    pHashData,
                                    instanceHandle,
                                    pSessionDesc,
                                    ICP_QAT_FW_SLICE_DRAM_WR,
                                    hashOffsetInConstantsTable,
                                    &pSessionDesc->cmnRequestFlags,
                                    &pSessionDesc->laCmdFlags,
                                    &precomputeData,
                                    &precomputeDataOptimisedCd,
                                    pHwBlockBaseInDRAM,
                                    &hwBlockOffsetInDRAM,
                                    pOptimisedHwBlockBaseInDRAM,
                                    &optimisedHwBlockOffsetInDRAM);
                        }
                        break;
                default:
                        LAC_LOG_ERROR("Invalid sym operation\n");
                        status = CPA_STATUS_INVALID_PARAM;
                }
        }

        if ((CPA_STATUS_SUCCESS == status) && pSessionDesc->isAuth) {
                lac_sym_qat_hash_state_buffer_info_t *pHashStateBufferInfo =
                    &(pSessionDesc->hashStateBufferInfo);
                CpaBoolean hashStateBuffer = CPA_TRUE;

                /* set up fields in both the cd_ctrl and reqParams which
                 * describe the ReqParams block */
                LacSymQat_HashSetupReqParamsMetaData(
                    &(pSessionDesc->reqCacheFtr),
                    instanceHandle,
                    pHashData,
                    hashStateBuffer,
                    pSessionDesc->qatHashMode,
                    pSessionDesc->digestVerify);

                if (pSessionDesc->useSymConstantsTable) {
                        /* Need to set up for SHRAM Constants Table use also */
                        LacSymQat_HashSetupReqParamsMetaData(
                            &(pSessionDesc->shramReqCacheFtr),
                            instanceHandle,
                            pHashData,
                            hashStateBuffer,
                            pSessionDesc->qatHashMode,
                            pSessionDesc->digestVerify);
                }

                /* populate the hash state prefix buffer info structure
                 * (part of user allocated session memory & the
                 * buffer itself. For CCM/GCM the buffer is stored in the
                 * cookie and is not initialised here) */
                if (CPA_FALSE == pSessionDesc->isAuthEncryptOp) {
                        LAC_CHECK_64_BYTE_ALIGNMENT(
                            &(pSessionDesc->hashStatePrefixBuffer[0]));
                        status = LacHash_StatePrefixAadBufferInit(
                            &(pService->generic_service_info),
                            pHashData,
                            &(pSessionDesc->reqCacheFtr),
                            pSessionDesc->qatHashMode,
                            pSessionDesc->hashStatePrefixBuffer,
                            pHashStateBufferInfo);
                        /* SHRAM Constants Table not used for Auth-Enc */
                }

                if (CPA_STATUS_SUCCESS == status) {
                        if (IS_HASH_MODE_1(pSessionDesc->qatHashMode) ||
                            CPA_CY_SYM_HASH_ZUC_EIA3 ==
                                pHashData->hashAlgorithm) {
                                LAC_CHECK_64_BYTE_ALIGNMENT(
                                    &(pSessionDesc->hashStatePrefixBuffer[0]));

                                /* Block messages until precompute is completed
                                 */
                                pSessionDesc->nonBlockingOpsInProgress =
                                    CPA_FALSE;
                                status = LacHash_PrecomputeDataCreate(
                                    instanceHandle,
                                    (CpaCySymSessionSetupData *)
                                        pSessionSetupData,
                                    LacSymAlgChain_HashPrecomputeDoneCb,
                                    pSessionDesc,
                                    pSessionDesc->hashStatePrefixBuffer,
                                    precomputeData.pState1,
                                    precomputeData.pState2);
                                if (pSessionDesc->useOptimisedContentDesc) {
                                        status = LacHash_PrecomputeDataCreate(
                                            instanceHandle,
                                            (CpaCySymSessionSetupData *)
                                                pSessionSetupData,
                                            LacSymAlgChain_HashPrecomputeDoneCb,
                                            pSessionDesc,
                                            pSessionDesc->hashStatePrefixBuffer,
                                            precomputeDataOptimisedCd.pState1,
                                            precomputeDataOptimisedCd.pState2);
                                }
                        } else if (pHashData->hashAlgorithm ==
                                   CPA_CY_SYM_HASH_AES_CBC_MAC) {
                                if (NULL != precomputeData.pState2) {
                                        LAC_OS_BZERO(precomputeData.pState2,
                                                     precomputeData.state2Size);
                                        memcpy(precomputeData.pState2,
                                               pHashData->authModeSetupData
                                                   .authKey,
                                               pHashData->authModeSetupData
                                                   .authKeyLenInBytes);
                                }
                        }
                }
        }

        if (CPA_STATUS_SUCCESS == status) {

                /* Configure the ContentDescriptor field
                    in the request if not done already */
                pCdInfo->hwBlkSzQuadWords =
                    LAC_BYTES_TO_QUADWORDS(hwBlockOffsetInDRAM);
                pMsg = (icp_qat_fw_comn_req_t *)&(pSessionDesc->reqCacheHdr);
                SalQatMsg_ContentDescHdrWrite((icp_qat_fw_comn_req_t *)pMsg,
                                              pCdInfo);

                pMsgS =
                    (icp_qat_fw_comn_req_t *)&(pSessionDesc->shramReqCacheHdr);
                /*If we are using the optimised CD then
                  we have to set this up correctly in the SHARM reqCache*/
                if (pSessionDesc->useOptimisedContentDesc) {
                        pCdInfoOptimised->hwBlkSzQuadWords =
                            LAC_BYTES_TO_QUADWORDS(
                                optimisedHwBlockOffsetInDRAM);
                        SalQatMsg_ContentDescHdrWrite(
                            (icp_qat_fw_comn_req_t *)pMsgS, pCdInfoOptimised);
                }

                /* Updates command flags basing on configured alg */
                updateLaCmdFlags(pSessionDesc,
                                 proto,
                                 &pSessionDesc->laCmdFlags);

                SalQatMsg_CmnHdrWrite((icp_qat_fw_comn_req_t *)pMsg,
                                      ICP_QAT_FW_COMN_REQ_CPM_FW_LA,
                                      pSessionDesc->laCmdId,
                                      pSessionDesc->cmnRequestFlags,
                                      pSessionDesc->laCmdFlags);

                /* Need to duplicate if SHRAM Constants Table used */
                if (pSessionDesc->useSymConstantsTable) {
                        ICP_QAT_FW_LA_CIPH_AUTH_CFG_OFFSET_FLAG_SET(
                            pSessionDesc->laCmdFlags,
                            ICP_QAT_FW_CIPH_AUTH_CFG_OFFSET_IN_SHRAM_CP);

                        if (pSessionDesc->isCipher &&
                            !pSessionDesc->useOptimisedContentDesc) {
                                ICP_QAT_FW_COMN_CD_FLD_TYPE_SET(
                                    cmnRequestFlags,
                                    QAT_COMN_CD_FLD_TYPE_16BYTE_DATA);
                        }

                        SalQatMsg_CmnHdrWrite((icp_qat_fw_comn_req_t *)pMsgS,
                                              ICP_QAT_FW_COMN_REQ_CPM_FW_LA,
                                              pSessionDesc->laCmdId,
                                              cmnRequestFlags,
                                              pSessionDesc->laCmdFlags);
                }
        }

        return status;
}

static void
LacAlgChain_StatefulSha3_SkipStateLoadFlags(icp_qat_fw_la_bulk_req_t *pMsg,
                                            Cpa32U packetType,
                                            icp_qat_hw_auth_mode_t qatHashMode)
{
        icp_qat_fw_auth_cd_ctrl_hdr_t *pAuthCdCtrlHdr = NULL;

        pAuthCdCtrlHdr = (icp_qat_fw_auth_cd_ctrl_hdr_t *)&(pMsg->cd_ctrl);

        if (IS_HASH_MODE_2(qatHashMode)) {
                if ((ICP_QAT_FW_LA_PARTIAL_START == packetType) ||
                    (ICP_QAT_FW_LA_PARTIAL_NONE == packetType)) {
                        ICP_QAT_FW_HASH_FLAG_SKIP_INNER_STATE1_LOAD_SET(
                            pAuthCdCtrlHdr->hash_flags,
                            QAT_FW_LA_SKIP_INNER_STATE1_LOAD);
                        ICP_QAT_FW_HASH_FLAG_SKIP_OUTER_STATE1_LOAD_SET(
                            pAuthCdCtrlHdr->hash_flags,
                            QAT_FW_LA_SKIP_OUTER_STATE1_LOAD);
                } else if (ICP_QAT_FW_LA_PARTIAL_END == packetType) {
                        ICP_QAT_FW_HASH_FLAG_SKIP_OUTER_STATE1_LOAD_SET(
                            pAuthCdCtrlHdr->hash_flags,
                            QAT_FW_LA_SKIP_OUTER_STATE1_LOAD);
                }
        } else {
                if ((ICP_QAT_FW_LA_PARTIAL_START == packetType) ||
                    (ICP_QAT_FW_LA_PARTIAL_NONE == packetType)) {
                        ICP_QAT_FW_HASH_FLAG_SKIP_INNER_STATE1_LOAD_SET(
                            pAuthCdCtrlHdr->hash_flags,
                            QAT_FW_LA_SKIP_INNER_STATE1_LOAD);
                }
        }
}

/** @ingroup LacAlgChain */
CpaStatus
LacAlgChain_Perform(const CpaInstanceHandle instanceHandle,
                    lac_session_desc_t *pSessionDesc,
                    void *pCallbackTag,
                    const CpaCySymOpData *pOpData,
                    const CpaBufferList *pSrcBuffer,
                    CpaBufferList *pDstBuffer,
                    CpaBoolean *pVerifyResult)
{
        CpaStatus status = CPA_STATUS_SUCCESS;
        sal_crypto_service_t *pService = (sal_crypto_service_t *)instanceHandle;
        Cpa32U capabilitiesMask =
            pService->generic_service_info.capabilitiesMask;
        lac_sym_bulk_cookie_t *pCookie = NULL;
        lac_sym_cookie_t *pSymCookie = NULL;
        icp_qat_fw_la_bulk_req_t *pMsg = NULL;
        Cpa8U *pMsgDummy = NULL;
        Cpa8U *pCacheDummyHdr = NULL;
        Cpa8U *pCacheDummyFtr = NULL;
        Cpa32U qatPacketType = 0;
        CpaBufferList *pBufferList = NULL;
        Cpa8U *pDigestResult = NULL;
        Cpa64U srcAddrPhys = 0;
        Cpa64U dstAddrPhys = 0;
        icp_qat_fw_la_cmd_id_t laCmdId;
        sal_qat_content_desc_info_t *pCdInfo = NULL;
        Cpa8U *pHwBlockBaseInDRAM = NULL;
        Cpa32U hwBlockOffsetInDRAM = 0;
        Cpa32U sizeInBytes = 0;
        icp_qat_fw_cipher_cd_ctrl_hdr_t *pSpcCdCtrlHdr = NULL;
        CpaCySymCipherAlgorithm cipher;
        CpaCySymHashAlgorithm hash;
        Cpa8U paddingLen = 0;
        Cpa8U blockLen = 0;
        CpaBoolean digestIsAppended = CPA_FALSE;
        Cpa32U aadLenInBytes = 0;
        Cpa64U srcPktSize = 0;
        Cpa64U dstPktSize = 0;

        /* Set the command id */
        laCmdId = pSessionDesc->laCmdId;

        cipher = pSessionDesc->cipherAlgorithm;
        hash = pSessionDesc->hashAlgorithm;

        CpaBoolean isSpCcm =
            (LAC_CIPHER_IS_CCM(cipher) && LAC_CIPHER_AES_V2(capabilitiesMask));

        if (CPA_CY_SYM_HASH_AES_GMAC == hash) {
                pSessionDesc->aadLenInBytes = pOpData->messageLenToHashInBytes;
                if (pOpData->messageLenToHashInBytes == 0 ||
                    pOpData->pAdditionalAuthData != NULL) {
                        LAC_INVALID_PARAM_LOG(
                            "For AES_GMAC, AAD Length "
                            "(messageLenToHashInBytes) must "
                            "be non zero and pAdditionalAuthData "
                            "must be NULL");
                        return CPA_STATUS_INVALID_PARAM;
                }
        }

        aadLenInBytes = pSessionDesc->aadLenInBytes;

        /* Convert Alg Chain Request to Cipher Request for CCP and
         * AES_GCM single pass */
        if ((NON_SPC != pSessionDesc->singlePassState) &&
            (isSpCcm || (LAC_CIPHER_SPC_IV_SIZE == pOpData->ivLenInBytes))) {
                pSessionDesc->laCmdId = ICP_QAT_FW_LA_CMD_CIPHER;
                laCmdId = pSessionDesc->laCmdId;
                pSessionDesc->symOperation = CPA_CY_SYM_OP_CIPHER;
                pSessionDesc->singlePassState = SPC;
                pSessionDesc->isCipher = CPA_TRUE;
                pSessionDesc->isAuthEncryptOp = CPA_FALSE;
                pSessionDesc->isAuth = CPA_FALSE;

                if (CPA_CY_SYM_HASH_AES_GMAC == hash) {
                        if (ICP_QAT_FW_SPC_AAD_SZ_MAX < aadLenInBytes) {
                                LAC_INVALID_PARAM_LOG(
                                    "aadLenInBytes for AES_GMAC");
                                return CPA_STATUS_INVALID_PARAM;
                        }
                }
                /* New bit position (13) for SINGLE PASS.
                 * The FW provides a specific macro to use to set the proto flag
                 */
                ICP_QAT_FW_LA_SINGLE_PASS_PROTO_FLAG_SET(
                    pSessionDesc->laCmdFlags, ICP_QAT_FW_LA_SINGLE_PASS_PROTO);
                if (isCyGen2x(pService)) {
                        ICP_QAT_FW_LA_PROTO_SET(pSessionDesc->laCmdFlags, 0);
                }

                pCdInfo = &(pSessionDesc->contentDescInfo);
                pHwBlockBaseInDRAM = (Cpa8U *)pCdInfo->pData;
                if (CPA_CY_SYM_CIPHER_DIRECTION_DECRYPT ==
                    pSessionDesc->cipherDirection) {
                        if (LAC_CIPHER_IS_GCM(cipher))
                                hwBlockOffsetInDRAM = LAC_QUADWORDS_TO_BYTES(
                                    LAC_SYM_QAT_CIPHER_GCM_SPC_OFFSET_IN_DRAM);
                        else if (LAC_CIPHER_IS_CHACHA(cipher))
                                hwBlockOffsetInDRAM = LAC_QUADWORDS_TO_BYTES(
                                    LAC_SYM_QAT_CIPHER_CHACHA_SPC_OFFSET_IN_DRAM);
                } else if (isSpCcm) {
                        hwBlockOffsetInDRAM = LAC_QUADWORDS_TO_BYTES(
                            LAC_SYM_QAT_CIPHER_CCM_SPC_OFFSET_IN_DRAM);
                }

                /* Update cipher slice type */
                pSessionDesc->cipherSliceType =
                    LacCipher_GetCipherSliceType(pService,
                                                 pSessionDesc->cipherAlgorithm,
                                                 pSessionDesc->hashAlgorithm);

                ICP_QAT_FW_LA_SLICE_TYPE_SET(pSessionDesc->laCmdFlags,
                                             pSessionDesc->cipherSliceType);

                /* construct cipherConfig in CD in DRAM */
                LacSymQat_CipherHwBlockPopulateCfgData(pSessionDesc,
                                                       pHwBlockBaseInDRAM +
                                                           hwBlockOffsetInDRAM,
                                                       &sizeInBytes);
                SalQatMsg_CmnHdrWrite((icp_qat_fw_comn_req_t *)&(
                                          pSessionDesc->reqSpcCacheHdr),
                                      ICP_QAT_FW_COMN_REQ_CPM_FW_LA,
                                      laCmdId,
                                      pSessionDesc->cmnRequestFlags,
                                      pSessionDesc->laCmdFlags);
        } else if ((SPC == pSessionDesc->singlePassState) &&
                   (LAC_CIPHER_SPC_IV_SIZE != pOpData->ivLenInBytes)) {
                pSessionDesc->symOperation = CPA_CY_SYM_OP_ALGORITHM_CHAINING;
                pSessionDesc->singlePassState = LIKELY_SPC;
                pSessionDesc->isCipher = CPA_TRUE;
                pSessionDesc->isAuthEncryptOp = CPA_TRUE;
                pSessionDesc->isAuth = CPA_TRUE;
                pCdInfo = &(pSessionDesc->contentDescInfo);
                pHwBlockBaseInDRAM = (Cpa8U *)pCdInfo->pData;

                if (CPA_CY_SYM_CIPHER_DIRECTION_ENCRYPT ==
                    pSessionDesc->cipherDirection) {
                        pSessionDesc->laCmdId = ICP_QAT_FW_LA_CMD_CIPHER_HASH;
                } else {
                        pSessionDesc->laCmdId = ICP_QAT_FW_LA_CMD_HASH_CIPHER;
                }

                laCmdId = pSessionDesc->laCmdId;
                ICP_QAT_FW_LA_SINGLE_PASS_PROTO_FLAG_SET(
                    pSessionDesc->laCmdFlags, 0);
                ICP_QAT_FW_LA_PROTO_SET(pSessionDesc->laCmdFlags,
                                        ICP_QAT_FW_LA_GCM_PROTO);

                LacSymQat_CipherHwBlockPopulateCfgData(pSessionDesc,
                                                       pHwBlockBaseInDRAM +
                                                           hwBlockOffsetInDRAM,
                                                       &sizeInBytes);

                SalQatMsg_CmnHdrWrite((icp_qat_fw_comn_req_t *)&(
                                          pSessionDesc->reqCacheHdr),
                                      ICP_QAT_FW_COMN_REQ_CPM_FW_LA,
                                      laCmdId,
                                      pSessionDesc->cmnRequestFlags,
                                      pSessionDesc->laCmdFlags);
        }

        else if (LAC_CIPHER_IS_CHACHA(cipher) &&
                 (LAC_CIPHER_SPC_IV_SIZE != pOpData->ivLenInBytes)) {
                LAC_INVALID_PARAM_LOG("IV for CHACHA");
                return CPA_STATUS_INVALID_PARAM;
        }

        if ((CPA_TRUE == pSessionDesc->isAuthEncryptOp) || isSpCcm) {
                if (CPA_CY_SYM_HASH_AES_CCM == hash) {
                        status = LacSymAlgChain_CheckCCMData(
                            pOpData->pAdditionalAuthData,
                            pOpData->pIv,
                            pOpData->messageLenToCipherInBytes,
                            pOpData->ivLenInBytes);
                        if (CPA_STATUS_SUCCESS == status) {
                                LacSymAlgChain_PrepareCCMData(
                                    pSessionDesc,
                                    pOpData->pAdditionalAuthData,
                                    pOpData->pIv,
                                    pOpData->messageLenToCipherInBytes,
                                    pOpData->ivLenInBytes);
                        }
                } else if (CPA_CY_SYM_HASH_AES_GCM == hash) {
                        if (aadLenInBytes != 0 &&
                            pOpData->pAdditionalAuthData == NULL) {
                                LAC_INVALID_PARAM_LOG("pAdditionalAuthData");
                                status = CPA_STATUS_INVALID_PARAM;
                        }
                        if (CPA_STATUS_SUCCESS == status) {
                                LacSymAlgChain_PrepareGCMData(
                                    pSessionDesc, pOpData->pAdditionalAuthData);
                        }
                }
        }

        /* allocate cookie (used by callback function) */
        if (CPA_STATUS_SUCCESS == status) {
                pSymCookie = (lac_sym_cookie_t *)Lac_MemPoolEntryAlloc(
                    pService->lac_sym_cookie_pool);
                if (pSymCookie == NULL) {
                        LAC_LOG_ERROR("Cannot allocate cookie - NULL");
                        status = CPA_STATUS_RESOURCE;
                } else if ((void *)CPA_STATUS_RETRY == pSymCookie) {
                        pSymCookie = NULL;
                        status = CPA_STATUS_RETRY;
                } else {
                        pCookie = &(pSymCookie->u.bulkCookie);
                }
        }

        if (CPA_STATUS_SUCCESS == status) {
                /* write the buffer descriptors */
                if (IS_ZERO_LENGTH_BUFFER_SUPPORTED(cipher, hash)) {
                        status =
                            LacBuffDesc_BufferListDescWriteAndAllowZeroBuffer(
                                (CpaBufferList *)pSrcBuffer,
                                &srcAddrPhys,
                                CPA_FALSE,
                                &(pService->generic_service_info));
                        if (CPA_STATUS_SUCCESS != status) {
                                LAC_LOG_ERROR(
                                    "Unable to write src buffer descriptors");
                        }
                        /* For out of place operations */
                        if ((pSrcBuffer != pDstBuffer) &&
                            (CPA_STATUS_SUCCESS == status)) {
                                status =
                                    LacBuffDesc_BufferListDescWriteAndAllowZeroBuffer(
                                        pDstBuffer,
                                        &dstAddrPhys,
                                        CPA_FALSE,
                                        &(pService->generic_service_info));
                                if (CPA_STATUS_SUCCESS != status) {
                                        LAC_LOG_ERROR(
                                            "Unable to write dest buffer descriptors");
                                }
                        }
                } else {
                        status = LacBuffDesc_BufferListDescWrite(
                            (CpaBufferList *)pSrcBuffer,
                            &srcAddrPhys,
                            CPA_FALSE,
                            &(pService->generic_service_info));
                        if (CPA_STATUS_SUCCESS != status) {
                                LAC_LOG_ERROR(
                                    "Unable to write src buffer descriptors in "
                                    "LacBuffDesc_BufferListDescWrite");
                        }
                        /* For out of place operations */
                        if ((pSrcBuffer != pDstBuffer) &&
                            (CPA_STATUS_SUCCESS == status)) {
                                status = LacBuffDesc_BufferListDescWrite(
                                    pDstBuffer,
                                    &dstAddrPhys,
                                    CPA_FALSE,
                                    &(pService->generic_service_info));
                                if (CPA_STATUS_SUCCESS != status) {
                                        LAC_LOG_ERROR(
                                            "Unable to write dest buffer descriptors in "
                                            "LacBuffDesc_BufferListDescWrite");
                                }
                        }
                }
        }
        if (CPA_STATUS_SUCCESS == status) {
                /* populate the cookie */
                pCookie->pCallbackTag = pCallbackTag;
                pCookie->sessionCtx = pOpData->sessionCtx;
                pCookie->pOpData = (const CpaCySymOpData *)pOpData;
                pCookie->pDstBuffer = pDstBuffer;
                pCookie->updateSessionIvOnSend = CPA_FALSE;
                pCookie->updateUserIvOnRecieve = CPA_FALSE;
                pCookie->updateKeySizeOnRecieve = CPA_FALSE;
                pCookie->pNext = NULL;
                pCookie->instanceHandle = pService;

                /* get the qat packet type for LAC packet type */
                LacSymQat_packetTypeGet(pOpData->packetType,
                                        pSessionDesc->partialState,
                                        &qatPacketType);
                /*
                 * For XTS mode, the key size must be updated after
                 * the first partial has been sent. Set a flag here so the
                 * response knows to do this.
                 */
                if (LAC_CIPHER_IS_XTS_MODE(cipher) &&
                    (laCmdId != ICP_QAT_FW_LA_CMD_AUTH) &&
                    (CPA_CY_SYM_PACKET_TYPE_PARTIAL == pOpData->packetType) &&
                    (qatPacketType == ICP_QAT_FW_LA_PARTIAL_START)) {
                        pCookie->updateKeySizeOnRecieve = CPA_TRUE;
                }

                /*
                 * Now create the Request.
                 * Start by populating it from the cache in the session
                 * descriptor.
                 */
                pMsg = &(pCookie->qatMsg);
                pMsgDummy = (Cpa8U *)pMsg;

                if (SPC == pSessionDesc->singlePassState) {
                        pCacheDummyHdr =
                            (Cpa8U *)&(pSessionDesc->reqSpcCacheHdr);
                        pCacheDummyFtr =
                            (Cpa8U *)&(pSessionDesc->reqSpcCacheFtr);
                } else {
                        /* Normally, we want to use the SHRAM Constants Table if
                         * possible for best performance (less DRAM accesses
                         * incurred by CPM).  But we can't use it for
                         * partial-packet hash operations.  This is why we build
                         * 2 versions of the message template at sessionInit,
                         * one for SHRAM Constants Table usage and the other
                         * (default) for Content Descriptor h/w setup data in
                         * DRAM.  And we chose between them here on a
                         * per-request basis, when we know the packetType
                         */
                        if ((!pSessionDesc->useSymConstantsTable) ||
                            (pSessionDesc->isAuth &&
                             (CPA_CY_SYM_PACKET_TYPE_FULL !=
                              pOpData->packetType))) {
                                pCacheDummyHdr =
                                    (Cpa8U *)&(pSessionDesc->reqCacheHdr);
                                pCacheDummyFtr =
                                    (Cpa8U *)&(pSessionDesc->reqCacheFtr);
                        } else {
                                pCacheDummyHdr =
                                    (Cpa8U *)&(pSessionDesc->shramReqCacheHdr);
                                pCacheDummyFtr =
                                    (Cpa8U *)&(pSessionDesc->shramReqCacheFtr);
                        }
                }
                memcpy(pMsgDummy,
                       pCacheDummyHdr,
                       (LAC_LONG_WORD_IN_BYTES * LAC_SIZE_OF_CACHE_HDR_IN_LW));
                memset((pMsgDummy +
                        (LAC_LONG_WORD_IN_BYTES * LAC_SIZE_OF_CACHE_HDR_IN_LW)),
                       0,
                       (LAC_LONG_WORD_IN_BYTES *
                        LAC_SIZE_OF_CACHE_TO_CLEAR_IN_LW));
                memcpy(pMsgDummy +
                           (LAC_LONG_WORD_IN_BYTES *
                            LAC_START_OF_CACHE_FTR_IN_LW),
                       pCacheDummyFtr,
                       (LAC_LONG_WORD_IN_BYTES * LAC_SIZE_OF_CACHE_FTR_IN_LW));
                /*
                 * Populate the comn_mid section
                 */
                SalQatMsg_CmnMidWrite(pMsg,
                                      pCookie,
                                      LAC_SYM_DEFAULT_QAT_PTR_TYPE,
                                      srcAddrPhys,
                                      dstAddrPhys,
                                      0,
                                      0);

                /*
                 * Populate the serv_specif_flags field of the Request header
                 * Some of the flags are set up here.
                 * Others are set up later when the RequestParams are set up.
                 */

                LacSymQat_LaPacketCommandFlagSet(
                    qatPacketType,
                    laCmdId,
                    cipher,
                    &pMsg->comn_hdr.serv_specif_flags,
                    pOpData->ivLenInBytes);

                if (SPC == pSessionDesc->singlePassState) {
                        ICP_QAT_FW_LA_GCM_IV_LEN_FLAG_SET(
                            pMsg->comn_hdr.serv_specif_flags,
                            ICP_QAT_FW_LA_GCM_IV_LEN_NOT_12_OCTETS);

                        if (CPA_CY_SYM_PACKET_TYPE_PARTIAL ==
                            pOpData->packetType) {
                                ICP_QAT_FW_LA_RET_AUTH_SET(
                                    pMsg->comn_hdr.serv_specif_flags,
                                    ICP_QAT_FW_LA_NO_RET_AUTH_RES);

                                ICP_QAT_FW_LA_CMP_AUTH_SET(
                                    pMsg->comn_hdr.serv_specif_flags,
                                    ICP_QAT_FW_LA_NO_CMP_AUTH_RES);
                        }
                }

                ICP_QAT_FW_LA_SLICE_TYPE_SET(pMsg->comn_hdr.serv_specif_flags,
                                             pSessionDesc->cipherSliceType);

                LacBuffDesc_BufferListTotalSizeGet(pSrcBuffer, &srcPktSize);
                LacBuffDesc_BufferListTotalSizeGet(pDstBuffer, &dstPktSize);

                /*
                 * Populate the CipherRequestParams section of the Request
                 */
                if (laCmdId != ICP_QAT_FW_LA_CMD_AUTH) {

                        Cpa8U *pIvBuffer = NULL;

                        status = LacCipher_PerformParamCheck(cipher,
                                                             pOpData,
                                                             srcPktSize);
                        if (CPA_STATUS_SUCCESS != status) {
                                /* free the cookie */
                                Lac_MemPoolEntryFree(pCookie);
                                return status;
                        }

                        if (CPA_STATUS_SUCCESS == status) {
                                /* align cipher IV */
                                status = LacCipher_PerformIvCheck(
                                    &(pService->generic_service_info),
                                    pCookie,
                                    qatPacketType,
                                    &pIvBuffer);
                        }
                        if ((SPC == pSessionDesc->singlePassState) &&
                            ((ICP_QAT_FW_LA_PARTIAL_MID == qatPacketType) ||
                             (ICP_QAT_FW_LA_PARTIAL_END == qatPacketType))) {
                                /* For SPC stateful cipher state size for mid
                                 * and end partial packet is 48 bytes
                                 */
                                pSpcCdCtrlHdr =
                                    (icp_qat_fw_cipher_cd_ctrl_hdr_t *)&(
                                        pMsg->cd_ctrl);
                                pSpcCdCtrlHdr->cipher_state_sz =
                                    LAC_BYTES_TO_QUADWORDS(
                                        LAC_SYM_QAT_CIPHER_SPC_STATE_SIZE);
                        }
                        /*populate the cipher request parameters */
                        if (CPA_STATUS_SUCCESS == status) {
                                Cpa64U ivBufferPhysAddr = 0;

                                if (pIvBuffer != NULL) {
                                        /* User OpData memory being used for IV
                                         * buffer */
                                        /* get the physical address */
                                        ivBufferPhysAddr =
                                            LAC_OS_VIRT_TO_PHYS_EXTERNAL(
                                                pService->generic_service_info,
                                                pIvBuffer);
                                        if (0 == ivBufferPhysAddr) {
                                                LAC_LOG_ERROR(
                                                    "Unable to get the physical address "
                                                    "of the IV\n");
                                                status = CPA_STATUS_FAIL;
                                        }
                                }

                                if (status == CPA_STATUS_SUCCESS) {
                                        status =
                                            LacSymQat_CipherRequestParamsPopulate(
                                                pSessionDesc,
                                                pMsg,
                                                pOpData
                                                    ->cryptoStartSrcOffsetInBytes,
                                                pOpData
                                                    ->messageLenToCipherInBytes,
                                                ivBufferPhysAddr,
                                                pIvBuffer);
                                }
                        }

                        if ((SPC == pSessionDesc->singlePassState) &&
                            CPA_STATUS_SUCCESS == status) {
                                Cpa64U aadBufferPhysAddr = 0;

                                /* For CHACHA and AES-GCM there is an AAD buffer
                                 * if aadLenInBytes is nonzero In case of
                                 * AES-GMAC, AAD buffer passed in the src
                                 * buffer.
                                 */
                                if ((0 != aadLenInBytes &&
                                     CPA_CY_SYM_HASH_AES_GMAC != hash) ||
                                    isSpCcm) {
                                        LAC_CHECK_NULL_PARAM(
                                            pOpData->pAdditionalAuthData);
                                        Cpa32U aadDataLen =
                                            pSessionDesc->aadLenInBytes;

                                        /* In case of AES_CCM, B0 block size and
                                         * 2 bytes of AAD len encoding need to
                                         * be added to total AAD data len */
                                        if (isSpCcm)
                                                aadDataLen +=
                                                    LAC_CIPHER_CCM_AAD_OFFSET;

                                        blockLen =
                                            LacSymQat_CipherBlockSizeBytesGet(
                                                cipher);
                                        if ((aadDataLen % blockLen) != 0) {
                                                paddingLen = blockLen -
                                                    (aadDataLen % blockLen);
                                                memset(
                                                    &pOpData
                                                         ->pAdditionalAuthData
                                                             [aadDataLen],
                                                    0,
                                                    paddingLen);
                                        }

                                        /* User OpData memory being used for AAD
                                         * buffer */
                                        /* get the physical address */
                                        aadBufferPhysAddr =
                                            LAC_OS_VIRT_TO_PHYS_EXTERNAL(
                                                pService->generic_service_info,
                                                pOpData->pAdditionalAuthData);
                                        if (0 == aadBufferPhysAddr) {
                                                LAC_LOG_ERROR(
                                                    "Unable to get the physical address "
                                                    "of the aad\n");
                                                status = CPA_STATUS_FAIL;
                                        }
                                }

                                if (CPA_STATUS_SUCCESS == status) {
                                        icp_qat_fw_la_cipher_req_params_t *pCipherReqParams =
                                            (icp_qat_fw_la_cipher_req_params_t
                                                 *)((Cpa8U *)&(
                                                        pMsg->serv_specif_rqpars) +
                                                    ICP_QAT_FW_CIPHER_REQUEST_PARAMETERS_OFFSET);

                                        icp_qat_fw_la_cipher_20_req_params_t
                                            *pCipher20ReqParams =
                                                (void
                                                     *)((Cpa8U *)&(
                                                            pMsg->serv_specif_rqpars) +
                                                        ICP_QAT_FW_CIPHER_REQUEST_PARAMETERS_OFFSET);

                                        if (isCyGen4x(pService)) {
                                                pCipher20ReqParams
                                                    ->spc_aad_addr =
                                                    aadBufferPhysAddr;
                                                pCipher20ReqParams->spc_aad_sz =
                                                    pSessionDesc->aadLenInBytes;
                                                pCipher20ReqParams
                                                    ->spc_aad_offset = 0;
                                                if (isSpCcm)
                                                        pCipher20ReqParams
                                                            ->spc_aad_sz +=
                                                            LAC_CIPHER_CCM_AAD_OFFSET;
                                        } else {
                                                pCipherReqParams->spc_aad_addr =
                                                    aadBufferPhysAddr;
                                                pCipherReqParams->spc_aad_sz =
                                                    (Cpa16U)pSessionDesc
                                                        ->aadLenInBytes;
                                        }

                                        if (CPA_TRUE !=
                                            pSessionDesc->digestIsAppended) {
                                                Cpa64U digestBufferPhysAddr = 0;
                                                /* User OpData memory being used
                                                 * for digest buffer */
                                                /* get the physical address */
                                                digestBufferPhysAddr =
                                                    LAC_OS_VIRT_TO_PHYS_EXTERNAL(
                                                        pService
                                                            ->generic_service_info,
                                                        pOpData->pDigestResult);
                                                if (0 != digestBufferPhysAddr) {
                                                        if (isCyGen4x(
                                                                pService)) {
                                                                pCipher20ReqParams
                                                                    ->spc_auth_res_addr =
                                                                    digestBufferPhysAddr;
                                                                pCipher20ReqParams
                                                                    ->spc_auth_res_sz =
                                                                    (Cpa8U)pSessionDesc
                                                                        ->hashResultSize;
                                                        } else {
                                                                pCipherReqParams
                                                                    ->spc_auth_res_addr =
                                                                    digestBufferPhysAddr;
                                                                pCipherReqParams
                                                                    ->spc_auth_res_sz =
                                                                    (Cpa8U)pSessionDesc
                                                                        ->hashResultSize;
                                                        }
                                                } else {
                                                        LAC_LOG_ERROR(
                                                            "Unable to get the physical address "
                                                            "of the digest\n");
                                                        status =
                                                            CPA_STATUS_FAIL;
                                                }
                                        } else {
                                                /* Check if the dest buffer can
                                                 * handle the digest, only for
                                                 * last packet */
                                                if (((ICP_QAT_FW_LA_PARTIAL_NONE ==
                                                      qatPacketType) ||
                                                     (ICP_QAT_FW_LA_PARTIAL_END ==
                                                      qatPacketType))) {
                                                        if (dstPktSize <
                                                            (pOpData
                                                                 ->cryptoStartSrcOffsetInBytes +
                                                             pOpData
                                                                 ->messageLenToCipherInBytes +
                                                             pSessionDesc
                                                                 ->hashResultSize))
                                                                status =
                                                                    CPA_STATUS_INVALID_PARAM;
                                                }
                                                if (isCyGen4x(pService)) {
                                                        pCipher20ReqParams
                                                            ->spc_auth_res_sz =
                                                            (Cpa8U)pSessionDesc
                                                                ->hashResultSize;
                                                } else {
                                                        pCipherReqParams
                                                            ->spc_auth_res_sz =
                                                            (Cpa8U)pSessionDesc
                                                                ->hashResultSize;
                                                }
                                        }
                                }
                        }
                }

                /*
                 * Set up HashRequestParams part of Request
                 */
                if ((status == CPA_STATUS_SUCCESS) &&
                    (laCmdId != ICP_QAT_FW_LA_CMD_CIPHER)) {
                        Cpa32U authOffsetInBytes =
                            pOpData->hashStartSrcOffsetInBytes;
                        Cpa32U authLenInBytes =
                            pOpData->messageLenToHashInBytes;

                        status = LacHash_PerformParamCheck(instanceHandle,
                                                           pSessionDesc,
                                                           pOpData,
                                                           srcPktSize,
                                                           pVerifyResult);
                        if (CPA_STATUS_SUCCESS != status) {
                                /* free the cookie */
                                Lac_MemPoolEntryFree(pCookie);
                                return status;
                        }
                        if (CPA_STATUS_SUCCESS == status) {
                                /* Info structure for CCM/GCM */
                                lac_sym_qat_hash_state_buffer_info_t
                                    hashStateBufferInfo = { 0 };
                                lac_sym_qat_hash_state_buffer_info_t
                                    *pHashStateBufferInfo =
                                        &(pSessionDesc->hashStateBufferInfo);

                                if (CPA_TRUE == pSessionDesc->isAuthEncryptOp) {
                                        icp_qat_fw_la_auth_req_params_t *pHashReqParams =
                                            (icp_qat_fw_la_auth_req_params_t
                                                 *)((Cpa8U *)&(
                                                        pMsg->serv_specif_rqpars) +
                                                    ICP_QAT_FW_HASH_REQUEST_PARAMETERS_OFFSET);

                                        hashStateBufferInfo.pData =
                                            pOpData->pAdditionalAuthData;
                                        if (pOpData->pAdditionalAuthData ==
                                            NULL) {
                                                hashStateBufferInfo.pDataPhys =
                                                    0;
                                        } else {
                                                hashStateBufferInfo
                                                    .pDataPhys = LAC_MEM_CAST_PTR_TO_UINT64(
                                                    LAC_OS_VIRT_TO_PHYS_EXTERNAL(
                                                        pService
                                                            ->generic_service_info,
                                                        pOpData
                                                            ->pAdditionalAuthData));
                                        }

                                        hashStateBufferInfo
                                            .stateStorageSzQuadWords = 0;
                                        hashStateBufferInfo
                                            .prefixAadSzQuadWords =
                                            LAC_BYTES_TO_QUADWORDS(
                                                pHashReqParams->u2.aad_sz);

                                        /* Overwrite hash state buffer info
                                         * structure pointer with the one
                                         * created for CCM/GCM */
                                        pHashStateBufferInfo =
                                            &hashStateBufferInfo;

                                        /* Aad buffer could be null in the GCM
                                         * case */
                                        if (0 ==
                                                hashStateBufferInfo.pDataPhys &&
                                            CPA_CY_SYM_HASH_AES_GCM != hash &&
                                            CPA_CY_SYM_HASH_AES_GMAC != hash) {
                                                LAC_LOG_ERROR(
                                                    "Unable to get the physical address"
                                                    "of the AAD\n");
                                                status = CPA_STATUS_FAIL;
                                        }

                                        /* for CCM/GCM the hash and cipher data
                                         * regions are equal */
                                        authOffsetInBytes =
                                            pOpData
                                                ->cryptoStartSrcOffsetInBytes;

                                        /* For authenticated encryption,
                                         * authentication length is determined
                                         * by messageLenToCipherInBytes for
                                         * AES-GCM and AES-CCM, and by
                                         * messageLenToHashInBytes for AES-GMAC.
                                         * You don't see the latter here, as
                                         * that is the initial value of
                                         * authLenInBytes. */
                                        if (hash != CPA_CY_SYM_HASH_AES_GMAC)
                                                authLenInBytes =
                                                    pOpData
                                                        ->messageLenToCipherInBytes;
                                } else if (CPA_CY_SYM_HASH_SNOW3G_UIA2 ==
                                               hash ||
                                           CPA_CY_SYM_HASH_ZUC_EIA3 == hash) {
                                        hashStateBufferInfo.pData =
                                            pOpData->pAdditionalAuthData;
                                        hashStateBufferInfo.pDataPhys =
                                            LAC_OS_VIRT_TO_PHYS_EXTERNAL(
                                                pService->generic_service_info,
                                                hashStateBufferInfo.pData);
                                        hashStateBufferInfo
                                            .stateStorageSzQuadWords = 0;
                                        hashStateBufferInfo
                                            .prefixAadSzQuadWords =
                                            LAC_BYTES_TO_QUADWORDS(
                                                aadLenInBytes);

                                        pHashStateBufferInfo =
                                            &hashStateBufferInfo;

                                        if (0 ==
                                            hashStateBufferInfo.pDataPhys) {
                                                LAC_LOG_ERROR(
                                                    "Unable to get the physical address"
                                                    "of the AAD\n");
                                                status = CPA_STATUS_FAIL;
                                        }
                                }
                                if (CPA_CY_SYM_HASH_AES_CCM == hash) {
                                        if (CPA_CY_SYM_CIPHER_DIRECTION_DECRYPT ==
                                            pSessionDesc->cipherDirection) {
                                                /* On a decrypt path pSrcBuffer
                                                 * is used as this is where
                                                 * encrypted digest is located.
                                                 * Firmware uses encrypted
                                                 * digest for
                                                 * compare/verification*/
                                                pBufferList =
                                                    (CpaBufferList *)pSrcBuffer;
                                        } else {
                                                /* On an encrypt path pDstBuffer
                                                 * is used as this is where
                                                 * encrypted digest will be
                                                 * written */
                                                pBufferList =
                                                    (CpaBufferList *)pDstBuffer;
                                        }
                                        status = LacSymAlgChain_PtrFromOffsetGet(
                                            pBufferList,
                                            pOpData->cryptoStartSrcOffsetInBytes +
                                                pOpData
                                                    ->messageLenToCipherInBytes,
                                            &pDigestResult);
                                        if (CPA_STATUS_SUCCESS != status) {
                                                LAC_LOG_ERROR(
                                                    "Cannot set digest pointer within the"
                                                    " buffer list - offset out of bounds");
                                        }
                                } else {
                                        pDigestResult = pOpData->pDigestResult;
                                }

                                if (CPA_TRUE ==
                                    pSessionDesc->useStatefulSha3ContentDesc) {
                                        LacAlgChain_StatefulSha3_SkipStateLoadFlags(
                                            pMsg,
                                            qatPacketType,
                                            pSessionDesc->qatHashMode);
                                }

                                if (CPA_CY_SYM_OP_ALGORITHM_CHAINING ==
                                    pSessionDesc->symOperation) {
                                        /* In alg chaining mode, packets are not
                                         * seen as partials for hash operations.
                                         * Override to NONE.
                                         */
                                        qatPacketType =
                                            ICP_QAT_FW_LA_PARTIAL_NONE;
                                }
                                digestIsAppended =
                                    pSessionDesc->digestIsAppended;
                                if (CPA_TRUE == digestIsAppended) {
                                        /*Check if the destination buffer can
                                         * handle the digest if digestIsAppend
                                         * is true*/
                                        if (srcPktSize <
                                            (authOffsetInBytes +
                                             authLenInBytes +
                                             pSessionDesc->hashResultSize)) {
                                                status =
                                                    CPA_STATUS_INVALID_PARAM;
                                        }
                                }
                                if (CPA_STATUS_SUCCESS == status) {
                                        /* populate the hash request parameters
                                         */
                                        status =
                                            LacSymQat_HashRequestParamsPopulate(
                                                pMsg,
                                                authOffsetInBytes,
                                                authLenInBytes,
                                                &(pService
                                                      ->generic_service_info),
                                                pHashStateBufferInfo,
                                                qatPacketType,
                                                pSessionDesc->hashResultSize,
                                                pSessionDesc->digestVerify,
                                                digestIsAppended ?
                                                    NULL :
                                                    pDigestResult,
                                                hash,
                                                NULL);
                                }
                        }
                }
        }

        /*
         * send the message to the QAT
         */
        if (CPA_STATUS_SUCCESS == status) {
                qatUtilsAtomicInc(&(pSessionDesc->u.pendingCbCount));

                status = LacSymQueue_RequestSend(instanceHandle,
                                                 pCookie,
                                                 pSessionDesc);

                if (CPA_STATUS_SUCCESS != status) {
                        /* Decrease pending callback counter on send fail. */
                        qatUtilsAtomicDec(&(pSessionDesc->u.pendingCbCount));
                }
        }
        /* Case that will catch all error status's for this function */
        if (CPA_STATUS_SUCCESS != status) {
                /* free the cookie */
                if (NULL != pSymCookie) {
                        Lac_MemPoolEntryFree(pSymCookie);
                }
        }
        return status;
}