/******************************************************************************
* Copyright 2015-2022 Xilinx, Inc.
* Copyright 2022-2023 Advanced Micro Devices, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/

/*
-------------------------------------------------------------------------------
************************************************* H E A D E R   F I L E S   ***
-------------------------------------------------------------------------------
*/
#ifdef ENABLE_WDI
#include <isl/iostreams/filtering_stream.hpp>
#include <isl/iostreams/util.hpp>
#endif
#include <memory>
#include "imageheadertable-versal.h"
#include "authentication-versal.h"
#include "bootimage.h"
#include "bootheader-versal.h"
#include "elftools.h"
#include <string.h>
#include <sstream>
#include "stringutils.h"
#include "fileutils.h"
#include "bitutils.h"
#include "bitutils-versal.h"
#include "partitionheadertable-versal.h"
#include "encryptutils.h"
#include "cJSON.h"
extern "C" {
#include "cdo-npi.h"
#include "cdo-source.h"
#include "cdo-command.h"
#include "cdo-binary.h"
#include "cdo-load.h"
#include "cdo-overlay.h"
};

#include "cdoutils.h"

static uint8_t bufferIndex = 0;
std::list<std::unique_ptr<CdoCommandDmaWrite>> VersalImageHeader::cdoSections;  // Smart pointers

/*
-------------------------------------------------------------------------------
*****************************************************   F U N C T I O N S   ***
-------------------------------------------------------------------------------
*/

/******************************************************************************/
VersalImageHeaderTable::VersalImageHeaderTable()
    : kekIvMust(false)
    , bootDevice(0)
    , creatorId(0)
    , parentId(0)
    , pdiId(0)
    , idCode(0)
    , extendedIdCode(0)
    , bypassIdCode(false)
    , prebuilt(false)
    , dpacm(DpaCM::DpaCMDisable)
    , isCorePsmFwPresent(false)
{
    auto temp_section = std::make_unique<Section>("MetaHeader", sizeof(VersalImageHeaderTableStructure));
    section = temp_section.release();  // Transfer ownership to raw pointer member
    iHTable = (VersalImageHeaderTableStructure*)section->Data.get();
}

/******************************************************************************/
VersalImageHeaderTable::VersalImageHeaderTable(std::ifstream& src)
    : isCorePsmFwPresent(false)
{
    prebuilt = true;
    auto temp_section = std::make_unique<Section>("MetaHeader", sizeof(VersalImageHeaderTableStructure));
    section = temp_section.release();  // Transfer ownership to raw pointer member
    iHTable = (VersalImageHeaderTableStructure*)section->Data.get();
    
    /* Import the Image Header Table from a boot image file */
    src.read((char*)section->Data.get(), section->Length);
    creatorId = (iHTable->imageHeaderTableAttributes >> vihtImageCreatorIdShift) & vihtImageCreatorIdMask;
    parentId = iHTable->parentId;
    pdiId = iHTable->pdiId;
    idCode = iHTable->idCode;
    extendedIdCode = iHTable->extendedIdCode;
    iht_optional_data_length = iHTable->optionalDataSize * 4;
    bypassIdCode = (iHTable->imageHeaderTableAttributes >> vihtSiliconRevisionIdCodeCheckShift) & vihtSiliconRevisionIdCodeCheckMask;
    bootDevice = (iHTable->imageHeaderTableAttributes >> vihtSecBootDeviceShift) & vihtSecBootDeviceMask;
    dpacm = ((iHTable->imageHeaderTableAttributes >> vihtDpacmEnableShift) & vihtDpacmEnableMask) == 3 ? DpaCM::DpaCMEnable : DpaCM::DpaCMDisable;
    pufHDLoc = ((iHTable->imageHeaderTableAttributes >> vihtPufHDLocationShift) & vihtPufHDLocationMask) == 3 ? PufHdLoc::PUFinBH : PufHdLoc::PUFinEFuse;
    idCodeCheck = (iHTable->imageHeaderTableAttributes >> vihtIdCodeCheckShift) & vihtIdCodeCheckMask;
    kekIvMust = false;
}

/******************************************************************************/
VersalImageHeaderTable::~VersalImageHeaderTable()
{
    if (section != NULL)
    {
    }
}

/******************************************************************************/
void VersalImageHeaderTable::Build(BootImage& bi, Binary& cache)
{
    LOG_TRACE("VersalImageHeaderTable::Build START: iht_optional_data_length = 0x%x", iht_optional_data_length);
    bool isApuOrRpuSubsystem = false;

    // CRITICAL: This is the ImageHeaderTable section itself (NOT individual image headers!)
    // This section goes directly to cache and is NOT in bi.headers
    // Individual ImageHeader sections (created later) go to bi.headers conditionally
    if (section != NULL)
    {
        cache.Sections.push_back(std::unique_ptr<Section>(section));
    }

    
    if (!prebuilt)
    {
        pdiId = bi.bifOptions->GetPdiId();
        parentId = bi.bifOptions->GetParentId();
        idCode = bi.bifOptions->GetIdCode();
        extendedIdCode = bi.bifOptions->GetExtendedIdCode();
        bypassIdCode = bi.bifOptions->GetBypassIdcodeFlag();
        bootDevice = bi.bifOptions->GetBootDevice();

        SetImageHeaderTableVersion(VERSION_v4_00_VERSAL);
        SetHeaderTablesSize();
        SetTotalMetaHdrLength(0);
        SetIdentificationString(bi.IsBootloaderFound());
        SetIds(bi.imageList.size() != 0);
        metaHdrKeySrc = bi.options.bifOptions->metaHdrAttributes.encrKeySource;
        SetMetaHdrSecureHdrIv(metaHdrSecHdrIv.get());
        dpacm = bi.bifOptions->metaHdrAttributes.dpaCM;
        pufHDLoc = bi.bifOptions->metaHdrAttributes.pufHdLoc;
    }
    else
    {
        if (bi.bifOptions->GetPdiId() != 0)
        {
            pdiId = bi.bifOptions->GetPdiId();
        }
        if (bi.bifOptions->GetParentId() != 0)
        {
            parentId = bi.bifOptions->GetParentId();
        }

        if (bi.bifOptions->GetIdCode() != 0)
        {
            idCode = bi.bifOptions->GetIdCode();
        }
        if (bi.bifOptions->GetExtendedIdCode() != 0)
        {
            extendedIdCode = bi.bifOptions->GetExtendedIdCode();
        }

        if (bi.bifOptions->GetBypassIdcodeFlag() != false)
        {
            bypassIdCode = bi.bifOptions->GetBypassIdcodeFlag();
        }
        if (bi.bifOptions->GetBootDevice() != 0)
        {
            bootDevice = bi.bifOptions->GetBootDevice();
        }

        SetIdentificationString(bi.IsBootloaderFound());
        SetIds(bi.imageList.size() != 0);

        if (bi.bifOptions->metaHdrAttributes.encrKeySource != KeySource::None)
        {
            metaHdrKeySrc = bi.options.bifOptions->metaHdrAttributes.encrKeySource;
        }
        if (bi.bifOptions->metaHdrAttributes.dpaCM != DpaCM::DpaCMDisable)
        {
            dpacm = bi.bifOptions->metaHdrAttributes.dpaCM;
        }
        if (bi.bifOptions->metaHdrAttributes.pufHdLoc != PufHdLoc::PUFinEFuse)
        {
            pufHDLoc = bi.bifOptions->metaHdrAttributes.pufHdLoc;
        }

        SetOptionalData(bi.iht_optional_data.get(), bi.iht_optional_data_length);
    }


    /* Sub system Image Header creation */
    if (bi.createSubSystemPdis == true)
    {
        for (std::list<SubSysImageHeader*>::iterator subsysimage = bi.subSysImageList.begin(); subsysimage != bi.subSysImageList.end(); subsysimage++)
        {
            (*subsysimage)->Build(bi, cache);
            isApuOrRpuSubsystem = false;
            for (std::list<ImageHeader*>::iterator img = (*subsysimage)->imgList.begin(); img != (*subsysimage)->imgList.end(); img++)
            {
                ValidateSecurityCombinations((*img)->GetAuthenticationType(), (*img)->GetEncryptContext()->Type(),
                    (*img)->GetChecksumContext()->Type());
                if ((*img)->GetDestCpu() == DestinationCPU::A53_0 || (*img)->GetDestCpu() == DestinationCPU::A53_1 || (*img)->GetDestCpu() == DestinationCPU::R5_0 || (*img)->GetDestCpu() == DestinationCPU::R5_1 || (*img)->GetDestCpu() == DestinationCPU::R5_lockstep)
                {
                    isApuOrRpuSubsystem = true;
                }    
            }
            if (isApuOrRpuSubsystem == false)
            {
               nonApuSubSysCnt++;
            }
        }
        if (nonApuSubSysCnt == 0)
        {
           idCodeCheck = 3;
        }
        else
        {
           idCodeCheck = 0; 
        }
    }
    else
    {
        for (std::list<ImageHeader*>::iterator image = bi.imageList.begin(); image != bi.imageList.end(); image++)
        {
            (*image)->Build(bi, cache);
            ValidateSecurityCombinations((*image)->GetAuthenticationType(), (*image)->GetEncryptContext()->Type(),
                (*image)->GetChecksumContext()->Type());
            if ((*image)->GetDestCpu() == DestinationCPU::A53_0 || (*image)->GetDestCpu() == DestinationCPU::A53_1 || (*image)->GetDestCpu() == DestinationCPU::R5_0 || (*image)->GetDestCpu() == DestinationCPU::R5_1 || (*image)->GetDestCpu() == DestinationCPU::R5_lockstep)
            {
               idCodeCheck = 3;
            }
            imageHeaderList.push_back(*image);
        }
    }

    bi.options.SetPadHeaderTable(false);
    if (bi.options.DoPadHeaderTable())
    {
        if (bi.imageList.size() == 0)
        {
            std::list<std::string> outFileNames = bi.options.GetOutputFileNames();
            if (outFileNames.size() > 0)
            {
                std::cerr << "WARNING: -padimageheader was requested, but no images were specified." << std::endl;
            }
        }
        else
        {
            int paddingsize = GetMaxNumOfPartitions() - bi.imageList.size();
            if (paddingsize > 0)
            {
                bi.imageList.back()->section->Reserve = (1 + paddingsize) * 64;
            }
        }
    }
   /* Detect if PSM partition exists for SetUserOptionalData */
    isCorePsmFwPresent = false;
    for (std::list<PartitionHeader*>::iterator partHdr = bi.partitionHeaderList.begin(); partHdr != bi.partitionHeaderList.end(); partHdr++)
    {
        if((*partHdr)->imageHeader->GetDestCpu() == DestinationCPU::PMU)
            isCorePsmFwPresent = true;
    }

    LOG_TRACE("IsAuthOptimizationEnabled = %d, numHashTableEntries = %d", bi.options.IsAuthOptimizationEnabled(), bi.numHashTableEntries);
    if (bi.options.IsAuthOptimizationEnabled())
    {
         /* Adding an entry for header in hashtable */
        if(bi.options.bifOptions->metaHdrAttributes.authenticate == Authentication::None)
        {
            LOG_ERROR("MetaHeader must be authenticated if authentication optimization is enabled, using -enable_auth_opt");
        }
        else
        {
            bi.numHashTableEntries++;
            LOG_TRACE("Incremented numHashTableEntries to %d for header", bi.numHashTableEntries);
        }

        std::string metaHdrSsk = bi.options.bifOptions->metaHdrAttributes.ssk;
        if(metaHdrSsk.size() == 0)
        {
            metaHdrSsk = bi.options.bifOptions->GetSSKFileName();
        }

        std::string metaHdrPsk = bi.options.bifOptions->metaHdrAttributes.psk;
        if(metaHdrPsk.size() == 0)
        {
            metaHdrPsk = bi.options.bifOptions->GetPSKFileName();
        }

        uint32_t i = 0;
        for (std::list<PartitionHeader*>::iterator partHdr = bi.partitionHeaderList.begin(); partHdr != bi.partitionHeaderList.end(); partHdr++, i++)
        {
            if (!(*partHdr)->IsBootloader())
            {
                (*partHdr)->partitionNum = i;
                (*partHdr)->section->partitionNum = i;
                if ((*partHdr)->imageHeader->GetAuthenticationType() != Authentication::None)
                {
                    std::string partHdrSsk = (*partHdr)->imageHeader->GetAuthContext()->sskFile;
                    std::string partHdrPsk = (*partHdr)->imageHeader->GetAuthContext()->pskFile;
                    if(metaHdrSsk.compare(partHdrSsk))
                    {
                        LOG_ERROR("MetaHeader and partition must use same SSK if authentication optimization is enabled, using -enable_auth_opt");
                    }
                    if(metaHdrPsk.compare(partHdrPsk))
                    {
                        LOG_ERROR("MetaHeader and partition must use same PSK if authentication optimization is enabled, using -enable_auth_opt");
                    }
                    bi.numHashTableEntries++;
                }
            }
        }

        //For HSM with auth opt we can increment numHashTableEntries here as all the partition hashes will be stored in optional data
        if(bi.bifOptions->metaHdrAttributes.spkSignature != "")
        {
            if(bi.IsBootloaderFound())
            {
                bi.numHashTableEntries += bi.partitionHeaderList.size()-1;
            }
            else
            {
                bi.numHashTableEntries += bi.partitionHeaderList.size();
            }
        }

    }
   /* Detect if PSM partition exists and store it for SetUserOptionalData */
    isCorePsmFwPresent = false;
    for (std::list<PartitionHeader*>::iterator partHdr = bi.partitionHeaderList.begin(); partHdr != bi.partitionHeaderList.end(); partHdr++)
    {
        if((*partHdr)->imageHeader->GetDestCpu() == DestinationCPU::PMU)
            isCorePsmFwPresent = true;
    }

    bool isCorePsm = false;
    for (std::list<PartitionHeader*>::iterator partHdr = bi.partitionHeaderList.begin(); partHdr != bi.partitionHeaderList.end(); partHdr++)
    {
        if((*partHdr)->imageHeader->GetDestCpu() == DestinationCPU::PMU)
            isCorePsm = true;
    }

    bi.imageHeaderTable->SetUserOptionalData(bi.bifOptions->metaHdrAttributes.ihtOptionalDataInfo, bi.numHashTableEntries, isCorePsm);
    bi.iht_optional_data_length = iht_optional_data_length;
    bi.copied_iht_optional_data_length = copied_iht_optional_data_length;
    LOG_TRACE("After SetUserOptionalData: iht_optional_data_length = 0x%x, copied = 0x%x", iht_optional_data_length, copied_iht_optional_data_length);
    bi.iht_optional_data = std::make_unique<uint32_t[]>(bi.iht_optional_data_length / sizeof(uint32_t));
    memcpy(bi.iht_optional_data.get(), iht_optional_data.get(), iht_optional_data_length);
    
    // Section already added to cache at start of Build() with .get()
    // Cache stores non-owning reference, ImageHeaderTable keeps ownership
}

/******************************************************************************/
void VersalImageHeaderTable::Link(BootImage &bi)
{
    /* Realigning the data ptr is necessary because Section->Data ptr gets changed in the
    RSAAuthenticationContext::ResizeIfNecessary() function call */
    RealignSectionDataPtr();

    slaveBootSplitMode = (bi.bifOptions->GetSplitMode() == SplitMode::SlaveMode) ? true : false;
    fullBhSize = bi.options.bootheaderSize;
    allHdrSize = bi.options.allHeaderSize;

    if (bi.partitionHeaderTable->firstSection != NULL)
    {
        SetFirstPartitionHeaderOffset((uint32_t)bi.partitionHeaderTable->firstSection->WordAddress());
    }

    if (bi.options.bifOptions->GetHeaderEncyption())
    {
        SetFirstImageHeaderOffset(bi.encryptedHeaders->WordAddress());
        SetImageCount((uint32_t)bi.imageList.size());
    }
    else
    {
        if (bi.imageList.size() != 0)
        {
            if (bi.imageList.front()->section != NULL)
            {
                uint32_t size = (bi.imageList.size() > 0) ? (uint32_t)bi.imageList.front()->section->WordAddress() : 0;
                SetFirstImageHeaderOffset(size);
            }
            SetImageCount((uint32_t)bi.imageList.size());
        }
    }

    if (bi.createSubSystemPdis == true)
    {
        if (bi.options.bifOptions->GetHeaderEncyption())
        {
            SetFirstImageHeaderOffset(bi.encryptedHeaders->WordAddress());
            SetImageCount((uint32_t)bi.subSysImageList.size());
        }
        else if (bi.subSysImageList.size() != 0)
        {
            if (bi.subSysImageList.front()->section != NULL)
            {
                uint32_t size = (bi.subSysImageList.size() > 0) ? (uint32_t)bi.subSysImageList.front()->section->WordAddress() : 0;
                SetFirstImageHeaderOffset(size);
            }
            SetImageCount((uint32_t)bi.subSysImageList.size());
        }
    }
    SetPartitionCount((uint32_t)bi.partitionHeaderList.size());

    uint32_t acOffset = (bi.headerAC != 0) ? (bi.headerAC->section->WordAddress()) : 0;
    SetHeaderAuthCertificateOffset(acOffset);

    // Go through image list, to set the next image's offset parameters
    if (bi.createSubSystemPdis == true)
    {
        /* Sub system Image Header creation */
        for (std::list<SubSysImageHeader*>::iterator subsysimage = bi.subSysImageList.begin(); subsysimage != bi.subSysImageList.end();)
        {
            std::list<SubSysImageHeader*>::iterator prevSubSysImage = subsysimage;
            subsysimage++;
            if (subsysimage == bi.subSysImageList.end())
            {
                (*prevSubSysImage)->Link(bi, 0);
            }
            else
            {
                (*prevSubSysImage)->Link(bi, (*subsysimage));
            }
        }
    }
    else
    {
        for (std::list<ImageHeader*>::iterator currentImage = imageHeaderList.begin(); currentImage != imageHeaderList.end(); )
        {
            std::list<ImageHeader*>::iterator prevImage = currentImage;
            currentImage++;

            if (currentImage == imageHeaderList.end())
            {
                (*prevImage)->Link(bi, (*prevImage)->GetPartitionHeaderList().front(), 0);
            }
            else
            {
                (*prevImage)->Link(bi, (*prevImage)->GetPartitionHeaderList().front(), (*currentImage));
            }
        }
    }
    if (prebuilt)
    {
        SetTotalMetaHdrLength(bi.imageHeaderTable->metaHeaderLength);
    }
    SetBootDevice(bi.bifOptions->GetBootDevice());
    SetBootDeviceAddress(bi.bifOptions->GetBootDeviceAddress());
    SetMetaHdrKeySrc(bi.imageHeaderTable->metaHdrKeySrc, bi.bifOptions);
    SetMetaHdrGreyOrBlackIv(kekIvFile);
    SetImageHeaderTableAttributes();
    SetReservedFields();
    SetOptionalDataSize();
    SetChecksum();
}

/******************************************************************************/
void VersalImageHeaderTable::SetImageHeaderTableVersion(uint32_t version)
{
    iHTable->version = version;
}

/******************************************************************************/
void VersalImageHeaderTable::SetHeaderTablesSize()
{
    iHTable->headerSizes = (sizeof(VersalImageHeaderTableStructure)/4) | (sizeof(VersalImageHeaderStructure)/4 << 8) | (sizeof(VersalPartitionHeaderTableStructure)/4 << 16);
}

/******************************************************************************/
void VersalImageHeaderTable::SetTotalMetaHdrLength(uint32_t size)
{
    iHTable->totalMetaHdrLength = size / sizeof(uint32_t);
}

/******************************************************************************/
void VersalImageHeaderTable::SetPartitionCount(uint32_t count)
{
    iHTable->partitionTotalCount = count;
}

/******************************************************************************/
void VersalImageHeaderTable::SetImageCount(uint32_t count)
{
    iHTable->imageTotalCount = count;
}

/******************************************************************************/
void VersalImageHeaderTable::SetFirstImageHeaderOffset(uint32_t offset)
{
    iHTable->firstImageHeaderWordOffset = offset;
    if (slaveBootSplitMode && (iHTable->firstImageHeaderWordOffset != 0))
    {
        iHTable->firstImageHeaderWordOffset -= fullBhSize / sizeof(uint32_t);
    }
}

/******************************************************************************/
void VersalImageHeaderTable::SetFirstPartitionHeaderOffset(uint32_t offset)
{
    iHTable->firstPartitionHeaderWordOffset = offset;
    if (slaveBootSplitMode && (iHTable->firstPartitionHeaderWordOffset != 0))
    {
        iHTable->firstPartitionHeaderWordOffset -= fullBhSize / sizeof(uint32_t);
    }
}

/******************************************************************************/
void VersalImageHeaderTable::SetBootDeviceAddress(uint32_t address)
{
    iHTable->secondaryBootDeviceAddress = address;
}

/******************************************************************************/
void VersalImageHeaderTable::SetIds(bool warnIdCode)
{
    if (idCode == 0 && warnIdCode)
    {
        LOG_WARNING("id_code is not specified in BIF, default id code is 0x04ca8093)");
        idCode = DEFAULT_ID_CODE_S80;
    }
    iHTable->idCode = idCode;
    iHTable->extendedIdCode = extendedIdCode;
    iHTable->parentId = parentId;
    iHTable->pdiId = pdiId;
}

/******************************************************************************/
void VersalImageHeaderTable::SetImageHeaderTableAttributes()
{
    iHTable->imageHeaderTableAttributes = bypassIdCode;
    iHTable->imageHeaderTableAttributes |= ((creatorId & 0x0F) << 1);
    iHTable->imageHeaderTableAttributes |= ((bootDevice & vihtSecBootDeviceMask) << vihtSecBootDeviceShift);
    iHTable->imageHeaderTableAttributes |= ((dpacm & vihtDpacmEnableMask) << vihtDpacmEnableShift);
    iHTable->imageHeaderTableAttributes |= ((pufHDLoc & vihtPufHDLocationMask) << vihtPufHDLocationShift);
    iHTable->imageHeaderTableAttributes |= ((idCodeCheck & vihtIdCodeCheckMask) << vihtIdCodeCheckShift);
}

/******************************************************************************/
void VersalImageHeaderTable::SetMetaHdrSecureHdrIv(uint8_t* iv)
{
    if (iv == NULL)
    {
        memset(iHTable->metaHdrSecureHdrIv, 0, IV_LENGTH * WORD_SIZE_IN_BYTES);
    }
    else
    {
        memcpy(iHTable->metaHdrSecureHdrIv, iv, IV_LENGTH * WORD_SIZE_IN_BYTES);
    }
}

/******************************************************************************/
void VersalImageHeaderTable::SetMetaHdrKeySrc(KeySource::Type keyType, BifOptions* bifOptions)
{
    //Encryption key source, only key source used for PLM is valid for meta header
    kekIvMust = false;
    switch (keyType)
    {
    case KeySource::EfuseRedKey:
        iHTable->metaHdrKeySource = EFUSE_RED_KEY;
        break;

    case KeySource::BbramRedKey:
        iHTable->metaHdrKeySource = BBRAM_RED_KEY;
        break;

    case KeySource::EfuseBlkKey:
        iHTable->metaHdrKeySource = EFUSE_BLK_KEY;
        kekIvFile = bifOptions->GetEfuseKekIVFile();
        if (kekIvFile == "")
        {
            LOG_ERROR("'efuse_kek_iv' is mandatory with 'keysrc=efuse_blk_key'");
        }
        kekIvMust = true;
        break;

    case KeySource::BbramBlkKey:
        iHTable->metaHdrKeySource = BBRAM_BLK_KEY;
        kekIvFile = bifOptions->GetBbramKekIVFile();
        if (kekIvFile == "")
        {
            LOG_ERROR("'bbram_kek_iv' is mandatory with 'keysrc=bbram_blk_key'");
        }
        kekIvMust = true;
        break;

    case KeySource::BhBlkKey:
        iHTable->metaHdrKeySource = BH_BLACK_KEY;
        kekIvFile = bifOptions->GetBHKekIVFile();
        if (kekIvFile == "")
        {
            LOG_ERROR("'bh_kek_iv' is mandatory with 'keysrc=bh_blk_key'");
        }
        kekIvMust = true;
        break;

    case KeySource::EfuseGryKey:
        iHTable->metaHdrKeySource = EFUSE_GRY_KEY;
        kekIvFile = bifOptions->GetEfuseKekIVFile();
        if (kekIvFile == "")
        {
            LOG_ERROR("'efuse_kek_iv' is mandatory with 'keysrc=efuse_gry_key'");
        }
        kekIvMust = true;
        break;

    case KeySource::BbramGryKey:
        iHTable->metaHdrKeySource = BBRAM_GRY_KEY;
        kekIvFile = bifOptions->GetBbramKekIVFile();
        if (kekIvFile == "")
        {
            LOG_ERROR("'bbram_kek_iv' is mandatory with 'keysrc=bbram_gry_key'");
        }
        kekIvMust = true;
        break;

    case KeySource::BhGryKey:
        iHTable->metaHdrKeySource = BH_GRY_KEY;
        kekIvFile = bifOptions->GetBHKekIVFile();
        if (kekIvFile == "")
        {
            LOG_ERROR("'bh_kek_iv' is mandatory with 'keysrc=bh_gry_key'");
        }
        kekIvMust = true;
        break;

    case KeySource::None:
        iHTable->metaHdrKeySource = 0;
        break;

    default:
        LOG_ERROR("BIF attribute error !!!\n\t\tInvalid 'keysrc' mentioned for MetaHeader.");
        break;
    }
}

/******************************************************************************/
void VersalImageHeaderTable::SetMetaHdrGreyOrBlackIv(std::string ivFile)
{
    auto ivData = std::make_unique<uint8_t[]>(IV_LENGTH * 4);
    memset(ivData.get(), 0, IV_LENGTH * 4);

    if (ivFile != "")
    {
        FileImport fileReader;
        if (!fileReader.LoadHexData(ivFile, ivData.get(), IV_LENGTH * 4))
        {
            LOG_ERROR("Invalid no. of data bytes for Black/Grey Key IV.\n           Expected length for Grey/Black IV is 12 bytes");
        }
    }
    else
    {
        if (kekIvMust)
        {
            LOG_ERROR("Black/Grey IV is mandatory in case of Black/Grey key sources\n           Please use 'bh_kek_iv' to specify the IV in BIF file");
        }
    }

    memcpy(&iHTable->metaHdrGreyOrBlackIV, ivData.get(), IV_LENGTH * 4);
}

/******************************************************************************/
void VersalImageHeaderTable::SetHeaderAuthCertificateOffset(uint32_t address)
{
    iHTable->headerAuthCertificateWordOffset = address;
}

/******************************************************************************/
void VersalImageHeaderTable::SetPdiId(uint32_t id)
{
    pdiId = id;
}

/******************************************************************************/
void VersalImageHeaderTable::SetParentId(uint32_t id)
{
    parentId = id;
}

/******************************************************************************/
void VersalImageHeaderTable::SetIdentificationString(bool bootloaderFound)
{
    if (bootloaderFound)
    {
        iHTable->identificationString = FPDI;
    }
    else
    {
        iHTable->identificationString = PPDI;
    }
}

/******************************************************************************/
void VersalImageHeaderTable::SetCreatorId(uint8_t id)
{
    creatorId = id;
}

/******************************************************************************/
void VersalImageHeaderTable::SetReservedFields(void)
{
    memset(&iHTable->reserved, 0x00, MAX_IHT_RESERVED_VERSAL * sizeof(uint32_t));
}

/******************************************************************************/
void VersalImageHeaderTable::SetChecksum(void)
{
    iHTable->ihtChecksum = ComputeWordChecksum(iHTable, sizeof(VersalImageHeaderTableStructure) - sizeof(iHTable->ihtChecksum) /* bytes*/);
}

/******************************************************************************/
void VersalImageHeaderTable::SetOptionalDataSize()
{
    iHTable->optionalDataSize = iht_optional_data_length / 4;
}

/******************************************************************************/
void VersalImageHeaderTable::SetXplmModulesData(BootImage& bi, uint32_t * data, uint32_t size)
{
    if (size != 0)
    {
        bi.xplm_modules_data_length = size;
        bi.xplm_modules_data = std::make_unique<uint32_t[]>(bi.xplm_modules_data_length / sizeof(uint32_t));
        memcpy(bi.xplm_modules_data.get(), data, bi.xplm_modules_data_length);
    }
}

/******************************************************************************/
void VersalImageHeaderTable::SetOptionalData(uint32_t * data, uint32_t size)
{
    iht_optional_data_length = size;
    if (size != 0)
    {
        iht_optional_data = std::make_unique<uint32_t[]>(iht_optional_data_length / sizeof(uint32_t));
        memcpy(iht_optional_data.get(), data, iht_optional_data_length);
    }
}

/******************************************************************************/
void VersalImageHeaderTable::SetUserOptionalData(std::vector<std::pair<std::string, uint32_t>> optionalDataInfo, uint32_t numHashTableEntries, uint32_t isCorePsm)
{
    LOG_TRACE("SetUserOptionalData START: iht_optional_data_length = 0x%x, numHashTableEntries = %d, isCorePsm = %d", iht_optional_data_length, numHashTableEntries, isCorePsm);
    bool foundCosePsm = false;
    
    /* Fetch optional data, if available then exclude padding.
       For both Versal and Versal NET: Filter out PSM data to regenerate it fresh.
       This ensures PSM optional data is always consistent across HSM and non-HSM flows. */
    if(iht_optional_data_length > 0)
    {
        uint32_t actual_opData_length = 0;
        std::vector<uint8_t> filtered_data;  // Changed from raw pointer to vector for automatic memory management
        uint32_t filtered_length = 0;
        
        
        while(actual_opData_length < iht_optional_data_length)
        {
            uint32_t* buffer = iht_optional_data.get() + (actual_opData_length / 4);
            uint32_t opData_id = buffer[0] & 0x0000FFFF;
            LOG_TRACE("opdata_id %x", opData_id);
            
            /* Check if PSM optional data exists - always filter it out for regeneration */
            if(opData_id == 0x04)
            {
                /* For both Versal and Versal NET: Don't set foundCosePsm 
                   PSM data will always be regenerated fresh to ensure consistency */
                LOG_TRACE("Found PSM optional data (ID=0x04) in imported PDI - will filter and regenerate");
            }
                
            if(opData_id == 0x0000FFFF)
            {
                break;
            }

            uint32_t section_length = (((buffer[0] & 0xFFFF0000) >> 16) * 4);
            
            /* Safety check to prevent infinite loop */
            if (section_length == 0)
            {
                LOG_TRACE("Optional data section has zero length - unable to parse further");
                break;
            }
            
            /* Filter out PSM data and hash tables for regeneration.
               - PSM data (ID=0x04): Always regenerate for consistency
               - Hash table (ID=0x01): Always regenerate to avoid stale data from imports
               This ensures consistent optional data across all build flows. */
            if(opData_id == 0x04)
            {
                /* Skip PSM data - will regenerate */
                LOG_TRACE("Filtering out PSM optional data (ID=0x04) from imported PDI");
            }
            else if(opData_id == 0x01)
            {
                /* Skip hash table - will regenerate */
                LOG_TRACE("Filtering out hash table (ID=0x01) from imported PDI");
            }
            else
            {
                /* Copy this optional data section */
                filtered_data.resize(filtered_length + section_length);
                memcpy(filtered_data.data() + filtered_length, (uint8_t*)buffer, section_length);
                filtered_length += section_length;
            }
            
            actual_opData_length += section_length;
        }


        /* Replace imported optional data with filtered data */
        if(filtered_length > 0)
        {
            iht_optional_data = std::make_unique<uint32_t[]>(filtered_length / sizeof(uint32_t));
            memcpy(iht_optional_data.get(), filtered_data.data(), filtered_length);
            iht_optional_data_length = filtered_length;
        }
        else
        {
            /* No data after filtering, clear everything */
            if(iht_optional_data != nullptr)
            {
                iht_optional_data.reset();
            }
            iht_optional_data_length = 0;
        }
    }

   /*
       For both Versal and Versal NET:
       - PSM optional data (Section ID=0x04) is ALWAYS regenerated when PSM firmware exists
       - ALL system-reserved optional data (IDs 0x01-0x20) is filtered and regenerated
       - Only user-defined optional data (IDs > 0x20) is preserved from imports
       
       This ensures consistent behavior across all flows:
       - HSM Stage 1: System optional data generated fresh
       - HSM Stage 2+: System optional data regenerated (filtered from imports)
       - Non-HSM: System optional data generated fresh
       - Result: All flows produce identical system optional data
    */

    
    if(isCorePsmFwPresent && !foundCosePsm)
    {
        LOG_TRACE("Adding PSM optional data, iht_optional_data_length before = 0x%x", iht_optional_data_length);
        /*  Section id = 4 is reserved for core = psm
            Storing only Optional Data Header and Checksum  */
        uint32_t sectn_size_id = 0;
        /* Optional Data Header + Checksum */
        uint16_t sectn_length = sizeof(uint32_t) + sizeof(uint32_t);
        sectn_size_id = (uint32_t)((sectn_length / 4) << 16) | 0x04;

        auto new_data = std::make_unique<uint32_t[]>((iht_optional_data_length + sectn_length) / sizeof(uint32_t));
        if (iht_optional_data) {
            memcpy(new_data.get(), iht_optional_data.get(), iht_optional_data_length);
        }
        memcpy((uint8_t*)new_data.get() + iht_optional_data_length, &sectn_size_id, sizeof(uint32_t));
        uint32_t checksum = ComputeWordChecksum((uint8_t*)new_data.get() + iht_optional_data_length, sectn_length - sizeof(uint32_t));
        memcpy((uint8_t*)new_data.get() + iht_optional_data_length + sectn_length - sizeof(uint32_t), &checksum, sizeof(uint32_t));
        iht_optional_data = std::move(new_data);
        iht_optional_data_length += sectn_length;
        LOG_TRACE("After PSM: iht_optional_data_length = 0x%x", iht_optional_data_length);
    }

    for (size_t i = 0; i < optionalDataInfo.size(); i++)
    {
        if ((0x00 <= optionalDataInfo[i].second) && (optionalDataInfo[i].second <= 0x20))
        {
            LOG_WARNING("The Data IDs form 0x0 to 0x20 for Optional Data are reserved for internal use. Please use any ID > 0x20 as User Optional Data ID");
        }

        size_t size = 0;
        uint8_t* tempData = (uint8_t*)file_to_buf((char *)optionalDataInfo[i].first.c_str(), &size);
        if (tempData == NULL)
        {
            LOG_ERROR("Cannot read optional data file: %s", optionalDataInfo[i].first.c_str());
        }
        if(size > 0x20000){
            LOG_ERROR("Maximum allowed size for optional data is 0x20000 bytes, including optional data header of 8 bytes. Refer UG1283 for more details");
        }

        uint32_t optional_data_padLength = (size % 64 != 0) ? 64 - (size % 64) : 0;
        auto data = std::make_unique<uint8_t[]>(size + optional_data_padLength);
        memset(data.get(), 0, size + optional_data_padLength);
        memcpy(data.get(), tempData, size);
        free(tempData);
        size += optional_data_padLength;

        if (size != 0)
        {
            uint32_t sectn_size_id = 0;
            /* Optional Data Header + Optional Data Actual size + Checksum */
            uint16_t sectn_length = sizeof(uint32_t) + size + sizeof(uint32_t);
            sectn_size_id = (uint32_t)((sectn_length / 4) << 16) | (optionalDataInfo[i].second);

            auto new_data = std::make_unique<uint32_t[]>((iht_optional_data_length + sectn_length) / sizeof(uint32_t));
            if (iht_optional_data) {
                memcpy(new_data.get(), iht_optional_data.get(), iht_optional_data_length);
            }
            memcpy((uint8_t*)new_data.get() + iht_optional_data_length, &sectn_size_id, sizeof(uint32_t));
            memcpy((uint8_t*)new_data.get() + iht_optional_data_length + sizeof(uint32_t), data.get(), size);

            uint32_t checksum = ComputeWordChecksum((uint8_t*)new_data.get() + iht_optional_data_length, sectn_length - sizeof(uint32_t));
            memcpy((uint8_t*)new_data.get() + iht_optional_data_length + sectn_length - sizeof(uint32_t), &checksum, sizeof(uint32_t));
            iht_optional_data = std::move(new_data);
            iht_optional_data_length += sectn_length;
        }
    }

    copied_iht_optional_data_length = iht_optional_data_length;
    LOG_TRACE("SetUserOptionalData: numHashTableEntries = %d, iht_optional_data_length before hash = 0x%x", numHashTableEntries, iht_optional_data_length);

    if (numHashTableEntries != 0)
    {
        /* Optional Data Header + Optional Data Actual size (32 bit(4Bytes) partition Number + Hash Length in bytes) + Checksum */
        uint16_t sectn_length = sizeof(uint32_t) + (numHashTableEntries * (sizeof(uint32_t) + SHA3_LENGTH_BYTES)) + sizeof(uint32_t);
        LOG_TRACE("Allocating hash table space: sectn_length = 0x%x (0x%x entries)", sectn_length, numHashTableEntries);
        auto new_data = std::make_unique<uint32_t[]>((iht_optional_data_length + sectn_length) / sizeof(uint32_t));
        if (iht_optional_data) {
            memcpy(new_data.get(), iht_optional_data.get(), iht_optional_data_length);
        }
        memset((uint8_t*)new_data.get() + iht_optional_data_length, 0, sectn_length);
        iht_optional_data = std::move(new_data);
        iht_optional_data_length += sectn_length;
        LOG_TRACE("After hash table allocation: iht_optional_data_length = 0x%x", iht_optional_data_length);
    }

    
    if (iht_optional_data_length != 0)
    {
        uint32_t padLength = (iht_optional_data_length % 64 != 0) ? 64 - (iht_optional_data_length % 64) : 0;
        auto new_data = std::make_unique<uint32_t[]>((iht_optional_data_length + padLength) / sizeof(uint32_t));
        if (iht_optional_data) {
            memcpy(new_data.get(), iht_optional_data.get(), iht_optional_data_length);
        }
        memset((uint8_t*)new_data.get() + (iht_optional_data_length), 0xFF, padLength);
        iht_optional_data = std::move(new_data);
        iht_optional_data_length += padLength;

        GetSectionPtr()->IncreaseLengthAndPadTo(sizeof(VersalImageHeaderTableStructure) + iht_optional_data_length, 0);
        memcpy(GetSectionPtr()->Data.get() + sizeof(VersalImageHeaderTableStructure), iht_optional_data.get(), iht_optional_data_length);

        iHTable = (VersalImageHeaderTableStructure*)section->Data.get();
        iHTable->optionalDataSize = iht_optional_data_length / 4;
        LOG_TRACE("FINAL: iHTable->optionalDataSize = 0x%x (bytes = 0x%x)", iHTable->optionalDataSize, iht_optional_data_length);
    }
    else
    {
        LOG_TRACE("No optional data to process");
    }
    LOG_TRACE("User optional data is processed");
}

/******************************************************************************/
void VersalImageHeaderTable::RealignSectionDataPtr(void)
{
    iHTable = (VersalImageHeaderTableStructure*)section->Data.get();
}

/******************************************************************************/
uint32_t VersalImageHeaderTable::GetImageHeaderTableVersion(void)
{
    return iHTable->version;
}

/******************************************************************************/
uint32_t VersalImageHeaderTable::GetPartitionCount(void)
{
    return iHTable->partitionTotalCount;
}

/******************************************************************************/
uint32_t VersalImageHeaderTable::GetImageCount(void)
{
    return iHTable->imageTotalCount;
}

/******************************************************************************/
uint32_t VersalImageHeaderTable::GetFirstImageHeaderOffset(void)
{
    return iHTable->firstImageHeaderWordOffset;
}

/******************************************************************************/
uint32_t VersalImageHeaderTable::GetTotalMetaHdrLength()
{
    return (iHTable->totalMetaHdrLength) * sizeof(uint32_t);
}

/******************************************************************************/
void VersalImageHeaderTable::ValidateSecurityCombinations(Authentication::Type authType, Encryption::Type encryptType,
                                Checksum::Type cksumType)
{
    if (cksumType != Checksum::None)
    {
        if (authType != Authentication::None)
        {
            LOG_ERROR("Checksum is not supported with Authentication");
        }
        if (encryptType != Encryption::None)
        {
            LOG_ERROR("Checksum is not supported with Encryption");
        }
    }
}

/******************************************************************************/
VersalImageHeader::VersalImageHeader(std::string& filename)
    : ImageHeader(filename)
    , imageHeader(NULL)
    , cdoHeader(nullptr)
    , aie_array_base_address(AIE_BASE_ADDR)
    , coreBaseAddr(0)
    , southBankBaseAddr(0)
    , westBankBaseAddr(0)
    , northBankBaseAddr(0)
    , eastBankBaseAddr(0)
    , num_of_slrs(0)
{
    Name = StringUtils::BaseName(filename);
    uint32_t size = sizeof(VersalImageHeaderStructure);

    auto temp_section = std::make_unique<Section>("ImageHeader " + Name, size);
    section = temp_section.release();  // Transfer ownership to raw pointer member
    memset(section->Data.get(), 0, size);
}

/******************************************************************************/
VersalImageHeader::VersalImageHeader(uint8_t* data, uint64_t len)
    : ImageHeader(data, len)
    , imageHeader(NULL)
    , cdoHeader(nullptr)
    , aie_array_base_address(AIE_BASE_ADDR)
    , coreBaseAddr(0)
    , southBankBaseAddr(0)
    , westBankBaseAddr(0)
    , northBankBaseAddr(0)
    , eastBankBaseAddr(0)
    , num_of_slrs(0)
{
    Name = "Buffer" + StringUtils::Format(".%d", bufferIndex++);
    uint32_t size = sizeof(VersalImageHeaderStructure);

    auto temp_section = std::make_unique<Section>("ImageHeader " + Name, size);
    section = temp_section.release();  // Transfer ownership to raw pointer member
    memset(section->Data.get(), 0, size);
}

/******************************************************************************/
VersalImageHeader::VersalImageHeader(std::ifstream& ifs, bool IsBootloader)
    : ImageHeader(ifs)
    , imageHeader(NULL)
    , cdoHeader(nullptr)
    , aie_array_base_address(AIE_BASE_ADDR)
    , coreBaseAddr(0)
    , southBankBaseAddr(0)
    , westBankBaseAddr(0)
    , northBankBaseAddr(0)
    , eastBankBaseAddr(0)
    , num_of_slrs(0)
{
    bool firstValidHdr = false;
    VersalImageHeaderStructure importedIH;

    long pos = ifs.tellg();
    ifs.read((char*)&importedIH, 4 * sizeof(uint32_t));

    char buffer[4];
    char name[256];
    uint32_t i = 0;

    do
    {
        ifs.read(buffer, 4);
        name[i + 0] = buffer[0];
        name[i + 1] = buffer[1];
        name[i + 2] = buffer[2];
        name[i + 3] = buffer[3];
        i += 4;
    } while (buffer[0] && buffer[1] && buffer[2] && buffer[3] && i<sizeof(name));

    if (i >= sizeof(name))
    {
        LOG_DEBUG(DEBUG_STAMP, "Image header name too long");
        LOG_ERROR("Failure parsing imported bootimage");
    }

    Name = name;
    uint32_t size = sizeof(VersalImageHeaderStructure);

    ifs.seekg(pos);
    auto temp_section = std::make_unique<Section>("ImageHeader " + Name, size);
    section = temp_section.release();  // Transfer ownership to raw pointer member
    imageHeader = (VersalImageHeaderStructure*)section->Data.get();
    ifs.read((char*)imageHeader, size);

    uint32_t count = 0;
    if (imageHeader->dataSectionCount < 32)
    {
        count = imageHeader->dataSectionCount;
    }

    uint32_t offset = imageHeader->partitionHeaderWordOffset * sizeof(uint32_t);

    for (uint8_t index = 0; index < count; index++)
    {
        Bootloader = IsBootloader;

        auto hdr = std::make_unique<VersalPartitionHeader>(this, index);
        if (!firstValidHdr)
        {
            hdr->firstValidIndex = true;
            firstValidHdr = true;
        }
        ifs.seekg(offset);
        hdr->ReadHeader(ifs);
        hdr->ReadData(ifs);
        if (hdr->GetPartitionKeySource())
        {
            hdr->preencrypted = true;
        }
        
        // CRITICAL: Save raw pointer before release() to avoid use-after-release
        VersalPartitionHeader* rawHdr = hdr.get();
        partitionHeaderList.push_back(hdr.release());

        Alignment = 0;
        Offset = 0;
        Reserve = 0;

        destCpu = (DestinationCPU::Type)rawHdr->GetDestinationCpu();
        exceptionLevel = (ExceptionLevel::Type)rawHdr->GetExceptionLevel();
        trustzone = (TrustZone::Type)rawHdr->GetTrustZone();
        early_handoff = rawHdr->GetEarlyHandoff();
        hivec = rawHdr->GetHivec();
        partitionType = rawHdr->GetPartitionType();
        PartOwner = (PartitionOwner::Type)rawHdr->GetOwnerType();
        dpacm = rawHdr->GetDpaCMFlag();
        pufHdLoc = rawHdr->GetPufHdLocation();
        offset += rawHdr->GetPartitionHeaderSize();
    }
}

/******************************************************************************/
VersalImageHeader::VersalImageHeader(std::ifstream& ifs, VersalImageHeaderStructure* importedIH, bool IsBootloader, uint32_t img_index)
    : ImageHeader(ifs)
    , imageHeader(NULL)
    , coreBaseAddr(0)
    , southBankBaseAddr(0)
    , westBankBaseAddr(0)
    , northBankBaseAddr(0)
    , eastBankBaseAddr(0)
    , num_of_slrs(0)
{
    bool firstValidHdr = false;

    Name = importedIH->imageName;
    uint32_t size = sizeof(VersalImageHeaderStructure);

    auto temp_section = std::make_unique<Section>("ImageHeader " + Name, size);
    section = temp_section.release();  // Transfer ownership to raw pointer member
    imageHeader = (VersalImageHeaderStructure*)section->Data.get();
    memcpy(imageHeader, importedIH, size);
    
    uint32_t offset =  (imageHeader->partitionHeaderWordOffset * sizeof(uint32_t)) + (img_index * sizeof(VersalPartitionHeaderTableStructure));

    auto tempPHT = std::make_unique<VersalPartitionHeaderTableStructure>();
    ifs.seekg(offset);
    ifs.read((char*)tempPHT.get(), sizeof(VersalPartitionHeaderTableStructure));

    uint32_t count = 0;
    if (imageHeader->dataSectionCount < 32)
    {
        count = tempPHT->dataSectionCount;
    }

    // Cleanup handled by unique_ptr

    for (uint8_t index = 0; index < count; index++)
    {
        Bootloader = IsBootloader;

        auto hdr = std::make_unique<VersalPartitionHeader>(this, index);
        if (!firstValidHdr)
        {
            hdr->firstValidIndex = true;
            firstValidHdr = true;
        }
        ifs.seekg(offset);
        hdr->ReadHeader(ifs);
        hdr->ReadData(ifs);
        if (hdr->GetPartitionKeySource())
        {
            hdr->preencrypted = true;
        }
        
        // CRITICAL: Save raw pointer before release() to avoid use-after-release
        VersalPartitionHeader* rawHdr = hdr.get();
        partitionHeaderList.push_back(hdr.release());

        Alignment = 0;
        Offset = 0;
        Reserve = 0;

        destCpu = (DestinationCPU::Type)rawHdr->GetDestinationCpu();
        exceptionLevel = (ExceptionLevel::Type)rawHdr->GetExceptionLevel();
        trustzone = (TrustZone::Type)rawHdr->GetTrustZone();
        early_handoff = rawHdr->GetEarlyHandoff();
        hivec = rawHdr->GetHivec();
        partitionType = rawHdr->GetPartitionType();
        PartOwner = (PartitionOwner::Type)rawHdr->GetOwnerType();
        dpacm = rawHdr->GetDpaCMFlag();
        pufHdLoc = rawHdr->GetPufHdLocation();
        offset += rawHdr->GetPartitionHeaderSize();
        cluster = rawHdr->GetDestinationCluster();
        lockstep = rawHdr->GetLockStepFlag();
    }
}
/******************************************************************************/
VersalImageHeader::~VersalImageHeader()
{
    if (section != NULL)
    {
    }
}

/******************************************************************************/
uint32_t VersalImageHeader::GetImageHeaderAttributes(void)
{
    return imageHeader->imageAttributes;
}

/******************************************************************************/
void VersalImageHeader::SetAuthBlock(size_t blockSize, bool flag)
{
    if (flag)
    {
        authBlock = 0;
    }
    else
    {
        if (blockSize == 0)
        {
            authBlock = 8;
        }
        else
        {
            authBlock = blockSize;
        }
    }
}

/******************************************************************************/
void VersalImageHeader::SetPartitionUid(uint32_t id)
{
    partitionUid = id;
}

/******************************************************************************/
void VersalImageHeader::SetEncryptionKeySrc(KeySource::Type type)
{
    keySrc = type;
    if (type != KeySource::None)
    {
        LOG_TRACE("Setting Encryption Key Source as %d", type);
    }
}

/******************************************************************************/
void VersalImageHeader::SetImageId(void)
{
    /* This is for the non-subsystem flow. BOOTGEN_SUBSYSTEM_PDI = 0
    Place image IDs as per the new subsystem guidelines i.e. 0x1c000001 for PMC subsystem and
    0x1c000000 for default subsystems */
    if (IsBootloader())
    {
        imageHeader->imageId = 0x1c000001;
    }
    else
    {
        imageHeader->imageId = 0x1c000000;
    }
}

/******************************************************************************/
void VersalImageHeader::SetPartitionRevocationId(uint32_t id)
{
    partitionRevokeId = id;
}

/******************************************************************************/
void VersalImageHeader::SetSpkRevocationId(uint32_t id)
{
    spkRevokeId = id;
}

/******************************************************************************/
void VersalImageHeader::SetMemCopyAddr()
{
    if (ihMemCpyAddr == 0xFFFFFFFFFFFFFFFF)
    {
        ihMemCpyAddr = 0;
    }
    imageHeader->memcpyAddressLo = ihMemCpyAddr;
    imageHeader->memcpyAddressHi = ihMemCpyAddr >> 32;
}

/******************************************************************************/
uint32_t VersalImageHeader::GetPartitionUid(void)
{
    return partitionUid;
}

/******************************************************************************/
KeySource::Type VersalImageHeader::GetEncryptionKeySrc(void)
{
    return keySrc;
}

/******************************************************************************/
uint32_t VersalImageHeader::GetPartitionRevocationId()
{
    return partitionRevokeId;
}

/******************************************************************************/
uint32_t VersalImageHeader::GetSpkRevocationId()
{
    return spkRevokeId;
}

/******************************************************************************/
DpaCM::Type VersalImageHeader::GetDpacm(void)
{
    return dpacm;
}

/******************************************************************************/
PufHdLoc::Type VersalImageHeader::GetPufHdLocation(void)
{
    return pufHdLoc;
}

/******************************************************************************/
std::string VersalImageHeader::GetKekIV(void)
{
    return kekIvFile;
}

/******************************************************************************/
void VersalImageHeader::Build(BootImage& bi, Binary& cache)
{
    if ((bi.subSysImageList.size() == 0) || (bi.createSubSystemPdis == false))
    {
        if (section != NULL)
        {
            bi.headers.push_back(section);
            bi.imageHeaderTable->metaHeaderLength += section->Length;
        }
    }

    uint32_t defaultAlignment = bi.options.GetDefaultAlignment();

    if (imageHeader == NULL)
    {
        imageHeader = (VersalImageHeaderStructure*)section->Data.get();
        SetImageName();
        SetImageHeaderAttributes();
        SetDataSectionCount(0);
        SetPartitionHeaderOffset(0);
        SetMetaHdrRevokeId(bi.bifOptions->metaHdrAttributes.partitionRevokeId);
        SetImageId();
        SetMemCopyAddr();

        if (Alignment.IsSet() && Offset.IsSet())
        {
            LOG_ERROR("[alignment] and [offset] attributes are mutually exclusive");
        }
        if (Alignment.IsSet() && (Alignment.Value() & (defaultAlignment - 1)))
        {
            LOG_ERROR("Alignment (0x%X) is not a multiple of %d", Alignment.Value(), defaultAlignment);
        }
        if (Reserve.IsSet() && (Reserve.Value()  & (defaultAlignment - 1)))
        {
            LOG_ERROR("Reserve (0x%X) is not a multiple of %d", Reserve.Value(), defaultAlignment);
        }
        if (Offset.IsSet() && (Offset.Value() & (defaultAlignment - 1)))
        {
            LOG_ERROR("Offset (0x%X) is not a multiple of %d", Offset.Value(), defaultAlignment);
        }

        partitionHeaderList.clear();

        ParseFileToImport(bi);

        SetDataSectionCount(partitionHeaderList.size());
    }
    bi.partitionHeaderList.insert(bi.partitionHeaderList.end(), partitionHeaderList.begin(), partitionHeaderList.end());
}

/******************************************************************************/
void VersalImageHeader::ImportBin(BootImage& bi)
{
    SetDomain(Domain::PS);
    Binary::Address_t load_addr, exec_addr;

    ByteFile data(Filename);

    if (Load.IsSet())
    {
        load_addr = Load.Value();
    }
    else
    {
        load_addr = 0;
    }

    if (Startup.IsSet())
    {
        exec_addr = Startup.Value();
    }
    else
    {
        exec_addr = 0;
    }

    uint32_t dataValue;
    uint32_t alignlen = data.len + ((4 - (data.len & 3)) & 3);
    auto tempBuffer = std::make_unique<uint8_t[]>(alignlen);

    if (GetPartitionType() == PartitionType::CONFIG_DATA_OBJ)
    {
        dataValue = ReadBigEndian32(data.bytes + 4);
        bool change_endianness = false;
        if ((dataValue != 0x584c4e58) && (dataValue != 0x584e4c58) && (dataValue != 0x004f4443) && (dataValue != 0x43444f00))
        {
            LOG_ERROR("Invalid CDO format - incorrect identification word (XLNX/CDO) - 0x%x", dataValue);
        }
        /* Read ID value (XNLX/CDO) from CDO Header to identify endianness */
        if (dataValue == 0x584c4e58 || dataValue == 0x004f4443)
        {
            /* Convert BE CDOs to LE */
            change_endianness = true;
            LOG_WARNING("CDO is in Big Endian format. Big Endian CDOs are deprecated. Please use little endian CDOs.");
        }

        for (uint32_t index = 0; index < data.len; index += 4)
        {
            dataValue = ReadBigEndian32(data.bytes + index);
            if (change_endianness)
            {
                WriteLittleEndian32(tempBuffer.get() + index, dataValue);
            }
            else
            {
                WriteBigEndian32(tempBuffer.get() + index, dataValue);
            }
        }
        if (PostProcessCdo(tempBuffer.get(), data.len)) return;
        load_addr = 0xFFFFFFFF;
        exec_addr = 0;
    }
    else if (GetPartitionType() == PartitionType::CFI)
    {
        uint32_t value[4] = { 0 };
        /* Rearrange data in CFI files as follows
        CFI File: D6 00 00 52 BE BA FE C0 BE BA FE C0 BE BA FE C0
        PDI File: C0 FE BA BE C0 FE BA BE C0 FE BA BE 52 00 00 D6
        */

        for (uint32_t index = 0; index < data.len; index += 16)
        {
            /* Read 32-bit word, change endianness
            Swap positions of 4 32-bit words */
            for (uint8_t i = 0; i < 4; i++)
            {
                value[i] = ReadBigEndian32(data.bytes + index + (4 * i));
                WriteLittleEndian32(tempBuffer.get() + index + 4 * (3 - i), value[i]);
            }
        }
        
        exec_addr = 0;
    }
    else
    {
        memcpy(tempBuffer.get(), data.bytes, alignlen);
    }
    auto hdr = std::make_unique<VersalPartitionHeader>(this, 0);
    hdr->firstValidIndex = true;
    hdr->loadAddress = load_addr;
    hdr->execAddress = exec_addr;
    if(updateReserveInPh == true)
    {
        if(Reserve.IsSet())
        {
            if(alignlen > Reserve.Value())
            {
                LOG_WARNING("Total Partition length is more than Reserve Length. Hence reserve attribute is ignored.");
            }
            else
            {
                alignlen = Reserve.Value();
            }
        }
    }
    hdr.get()->partition = std::make_unique<VersalPartition>(hdr.get(), tempBuffer.get(), alignlen);
    hdr->partitionSize = alignlen;
    SetLoadAndExecAddress(hdr.get());
    partitionHeaderList.push_back(hdr.release());
}

/******************************************************************************/
void VersalImageHeader::ImportBuffer(BootImage& bi)
{
    SetDomain(Domain::PS);

    auto hdr = std::make_unique<VersalPartitionHeader>(this, 0);
    hdr->firstValidIndex = true;

    hdr->execAddress = Startup.ValueOrDefault(0);
    if (Load.IsSet())
    {
        hdr->loadAddress = Load.Value();
    }
    else
    {
        hdr->loadAddress = 0;
        if ((GetPartitionType() == PartitionType::CFI) || (GetPartitionType() == PartitionType::NPI) || (GetPartitionType() == PartitionType::CONFIG_DATA_OBJ))
        {
            SetDomain(Domain::PL);
        }
    }
    if(updateReserveInPh)
    {
        if(Reserve.IsSet())
        {
            if(bufferSize > Reserve.Value())
            {
                LOG_WARNING("Total Partition length is more than Reserve Length. Hence reserve attribute is ignored.");
            }
            else
            {
                bufferSize = Reserve.Value();
            }
        }
    }
    hdr.get()->partition = std::make_unique<VersalPartition>(hdr.get(), buffer, bufferSize);
    hdr->partitionSize = bufferSize;
    partitionHeaderList.push_back(hdr.release());
}

/******************************************************************************/
void VersalImageHeader::ImportNpi(BootImage& bi)
{
    SetPartitionType(PartitionType::CONFIG_DATA_OBJ);
    if (Bootloader)
    {
        LOG_ERROR("NPI can not be marked [bootloader]");
    }

    /* Parse the bitstream, and set the bit file name for usage by other features
    such as '-process_bitstream' */
#ifdef ENABLE_WDI
    isl::iostreams::istream stream(Filename.c_str(), std::ios_base::binary);
#else
    std::ifstream stream(Filename.c_str(), std::ios_base::binary);
#endif
    if (!stream)
    {
        LOG_ERROR("Cannot read NPI file - %s ", Filename.c_str());
    }
    auto bit = std::make_unique<VersalBitFile>(stream);
    bit->ParseBit(bi);
    bi.bitFilename = Filename.c_str();

    /* The endianess is different (Big Endian) in case of Zynq MP/FPGA encryption cases. All other cases the
    the bitstream is copied as Little Endian */
    auto os = bit->GetOutputStreamType();
    bit->CopyNpi(os.get());

    /* Bitstream sizes should be word aligned. Otherwise bitstream is invalid */
    if (os->Size() % 4)
    {
        LOG_DEBUG(DEBUG_STAMP, "BIT stream %s does not have a word aligned number of bytes (0xX)", Name.c_str(), (uint32_t)os->Size());
        LOG_ERROR("BIT file parsing error !!!");
    }

    /* As bitstreams are configured in the PL section
    There should be no load / start-up (execution) addresses in case of bitstreams. */
    if (this->Startup.IsSet())
    {
        LOG_ERROR("[startup=...] attribute not supported for BIT partition - %s", this->Name.c_str());
    }

    if (PostProcessCdo(os->Start(), os->Size())) return;

    auto hdr = std::make_unique<VersalPartitionHeader>(this, 0);
    hdr->firstValidIndex = true;
    hdr->execAddress = 0;

    /* Padding of the partition, based on encryption */
    hdr->partitionSize = os->Size();

    // Larger size, if needed.
    hdr->transferSize = os->Size();
    hdr->preservedBitstreamHdr = os->pHdr;

    hdr.get()->partition = std::make_unique<VersalPartition>(hdr.get(), os->Start(), os->Size());

    partitionHeaderList.push_back(hdr.release());
}

/******************************************************************************/
void VersalImageHeader::ImportBit(BootImage& bi)
{
    /* Bitstream can never be marked as a bootloader */
    if (Bootloader)
    {
        LOG_ERROR("Bit stream can not be marked [bootloader]");
    }

    static bool cfi_gsc_mask = false;
    if (GetPartitionType() == PartitionType::CFI_GSC)
    {
        if (!cfi_gsc_mask)
        {
            SetPartitionType(PartitionType::CFI_GSC_UNMASK);
            cfi_gsc_mask = true;
        }
        else if (cfi_gsc_mask)
        {
            SetPartitionType(PartitionType::CFI_GSC);
            cfi_gsc_mask = false;
        }
    }
    else
    {
        SetPartitionType(PartitionType::CFI);
    }

    /* Parse the bitstream, and set the bit file name for usage by other features
    such as '-process_bitstream' */
#ifdef ENABLE_WDI
    isl::iostreams::istream stream(Filename.c_str(), std::ios_base::binary);
#else
    std::ifstream stream(Filename.c_str(), std::ios_base::binary);
#endif
    if (!stream)
    {
        LOG_ERROR("Cannot read BIT file - %s ", Filename.c_str());
    }
    auto bit = std::make_unique<VersalBitFile>(stream);
    bit->ParseBit(bi);
    bi.bitFilename = Filename.c_str();

    /* The endianess is different (Big Endian) in case of Zynq MP/FPGA encryption cases. All other cases the
    the bitstream is copied as Little Endian */
    auto os = bit->GetOutputStreamType();
    bit->Copy(os.get());

    /* Bitstream sizes should be word aligned. Otherwise bitstream is invalid */
    if (os->Size() % 4)
    {
        LOG_DEBUG(DEBUG_STAMP, "BIT stream %s does not have a word aligned number of bytes (0xX)", Name.c_str(), (uint32_t)os->Size());
        LOG_ERROR("BIT file parsing error !!!");
    }


    /* As bitstreams are configured in the PL section
       There should be no load / start-up (execution) addresses in case of bitstreams. */
    if (this->Startup.IsSet())
    {
        LOG_ERROR("[startup=...] attribute not supported for BIT partition - %s", this->Name.c_str());
    }

    

    auto hdr = std::make_unique<VersalPartitionHeader>(this, 0);
    hdr->firstValidIndex = true;
    hdr->execAddress = 0;

    /* Padding of the partition, based on encryption */
    hdr->partitionSize = os->Size();

    // Larger size, if needed.
    hdr->transferSize = os->Size();
    hdr->preservedBitstreamHdr = os->pHdr;
    SetPartitionType(PartitionType::CFI);
    hdr.get()->partition = std::make_unique<VersalPartition>(hdr.get(), os->Start(), os->Size());

    partitionHeaderList.push_back(hdr.release());
}

/******************************************************************************/
void VersalImageHeader::ParseFileToImport(BootImage& bi)
{
    if (destCpu == DestinationCPU::AIE)
    {
        
        if (bi.convertAieElfToCdo == true)
        {
            CreateAieEnginePartition(bi);
            LOG_INFO("AIE partitions are created by converting AIE elfs to CDOs");
        }
        else
        {
            ImportAieEngineElf(bi);
            LOG_INFO("AIE partitions are created using AIE elfs");
        }
    }
    else if (partitionType == PartitionType::SLR_BOOT)
    {
        CreateSlrBootPartition(bi);
    }
    else if (partitionType == PartitionType::SLR_CONFIG)
    {
        CreateSlrConfigPartition(bi);
    }
    else if (partitionType == PartitionType::IMAGE_STORE_PDI)
    {
        CreateWriteImageStorePartition();
    }
    else
    {
        if (Filename != "")
        {
            if (FileUtils::GetFileSize(Filename) == 0)
            {
                LOG_ERROR("Cannot read empty file - %s ", Filename.c_str());
            }
            if ((filelist.size() > 1) && (GetPartitionType() != PartitionType::CONFIG_DATA_OBJ))
            {
                LOG_ERROR("File for merging is not in CDO format - %s", Filename.c_str());
            }
            LOG_INFO("Parsing file - %s", filelist.at(0).c_str());
#ifdef ENABLE_WDI
            isl::iostreams::istream stream(Filename.c_str(), std::ios_base::binary);
#else
            std::ifstream stream(Filename.c_str(), std::ios_base::binary);
#endif
            if (!stream)
            {
                LOG_ERROR("Cannot read file - %s ", Filename.c_str());
            }
            std::string line;
            while(line == "")
            {
                getline(stream, line);
            }

            if (IsElf(line) && (partitionType != PartitionType::RAW))
            {
                ImportElf(bi);
            }
            else if (!IsElf(line) && (partitionType == PartitionType::RAW))
            {
                ImportBin(bi);
            }
            else if ((line.find("Xilinx ASCII Bitstream") != std::string::npos) || (line.find("Xilinx ASCII CFI Deviceimage") != std::string::npos))
            {
                ImportBit(bi);
            }
            else if ((line.find("Xilinx ASCII NPI Deviceimage") != std::string::npos) || (line.find("Xilinx ASCII PSAXIMM Deviceimage") != std::string::npos)
                || (line.find("Xilinx ASCII CDO") != std::string::npos) || (line.find("version") != std::string::npos) || (GetPartitionType() == PartitionType::CONFIG_DATA_OBJ))
            {
                ImportCdo(bi);
            }
            else
            {
                if (IsElf(line))
                {
                    SetPartitionType(PartitionType::RAW_ELF);
                }
                else if (GetPartitionType() != PartitionType::RESERVED)
                {
                    SetPartitionType(GetPartitionType());
                }
                else
                {
                    SetPartitionType(PartitionType::RAW);
                }
                ImportBin(bi);
            }
#ifdef ENABLE_WDI
            stream.auto_close();
#else
            stream.close();
#endif
        }
        else
        {
            ImportBuffer(bi);
        }
    }
}

/******************************************************************************/
void VersalImageHeader::ImportCdoSource(BootImage& bi)
{
    uint8_t* buffer = NULL;
    size_t size = 0;
    buffer = DecodeCdo(Filename, &size);
    if (PostProcessCdo(buffer, size)) return;
    auto buffer_owner = std::unique_ptr<uint8_t[]>(buffer);
    auto hdr = std::make_unique<VersalPartitionHeader>(this, 0);
    hdr->firstValidIndex = true;
    hdr->loadAddress = 0xFFFFFFFFFFFFFFFF;
    hdr->execAddress = 0;

    hdr.get()->partition = std::make_unique<VersalPartition>(hdr.get(), buffer_owner.get(), size);
    hdr->partitionSize = size;
    SetLoadAndExecAddress(hdr.get());
    partitionHeaderList.push_back(hdr.release());
}

/******************************************************************************/
void VersalImageHeader::ImportCdo(BootImage& bi)
{
    uint8_t* buffer = NULL;
    size_t size = 0;

    if (filelist.size() > 0)
    {
        if ((bi.bifOptions->pdiType == PartitionType::SLR_SLAVE_CONFIG) && (getenv("BOOTGEN_PROCESS_NOC_MARKERS") != NULL))
        {
            ParseSlaveSlrConfigCdos(bi, filelist, &buffer, &size, false);
        }
        else
        {
            ParseCdos(bi, filelist, &buffer, &size, false);
        }
    }
    SetPartitionType(PartitionType::CONFIG_DATA_OBJ);
    auto hdr = std::make_unique<VersalPartitionHeader>(this, 0);
    hdr->firstValidIndex = true;
    hdr->loadAddress = 0xFFFFFFFFFFFFFFFF;
    hdr->execAddress = 0;
    if(updateReserveInPh == true)
    {
        if(Reserve.IsSet())
        {
            if(size > Reserve.Value())
            {
                LOG_WARNING("Total Partition length is more than Reserve Length. Hence reserve attribute is ignored.");
            }
            else
            {
                size = Reserve.Value();
            }
        }
    }
    hdr.get()->partition = std::make_unique<VersalPartition>(hdr.get(), buffer, size);
    hdr->partitionSize = hdr->transferSize = size;
    free(buffer);
    SetLoadAndExecAddress(hdr.get());
    partitionHeaderList.push_back(hdr.release());
}

/******************************************************************************/
uint8_t* VersalImageHeader::DecodeCdo(std::string file, size_t* size)
{
    uint8_t* buffer = NULL;
    FILE* fp = fopen(file.c_str(), "r");
    CdoSequence* seq = cdoseq_from_source(fp);
    fclose(fp);
    if (seq == NULL)
    {
        LOG_ERROR("Error decoding CDO file %s", file.c_str());
    }
    buffer = (uint8_t*)cdoseq_to_binary(seq, size, 0);
    return buffer;
}

/******************************************************************************/
void VersalImageHeader::Link(BootImage &bi, PartitionHeader* partitionHeader, ImageHeader* nextImageHeader)
{
    slaveBootSplitMode = (bi.bifOptions->GetSplitMode() == SplitMode::SlaveMode) ? true : false;
    fullBhSize = bi.options.bootheaderSize;
    allHdrSize = bi.options.allHeaderSize;

    imageHeader = (VersalImageHeaderStructure*)section->Data.get();
    if (partitionHeader->section != NULL)
    {
        SetPartitionHeaderOffset((uint32_t)partitionHeader->section->Address);
    }

    SetImageId();
    SetChecksum();
}

/******************************************************************************/
void VersalImageHeader::SetMetaHdrRevokeId(uint32_t revokeId)
{
    imageHeader->metaHdrRevokeId = revokeId;
}

/******************************************************************************/
void VersalImageHeader::SetChecksum(void)
{
    imageHeader->ihChecksum = ComputeWordChecksum(imageHeader, sizeof(VersalImageHeaderStructure) - sizeof(imageHeader->ihChecksum) /* bytes*/);
}

/******************************************************************************/
void VersalImageHeader::SetPartitionHeaderOffset(uint32_t addr)
{
    imageHeader->partitionHeaderWordOffset = addr / sizeof(uint32_t);
    if (slaveBootSplitMode && (imageHeader->partitionHeaderWordOffset != 0))
    {
        imageHeader->partitionHeaderWordOffset -= fullBhSize / sizeof(uint32_t);
    }
}

/******************************************************************************/
void VersalImageHeader::SetDataSectionCount(uint32_t cnt)
{
    imageHeader->dataSectionCount = cnt;
}

/******************************************************************************/
void VersalImageHeader::SetImageHeaderAttributes()
{
    if (GetPartOwner() != PartitionOwner::FSBL)
    {
        imageHeader->imageAttributes = 1 << 3;
    }
    else
    {
        imageHeader->imageAttributes = 0;
    }
    if (ihDelayLoad)
    {
        imageHeader->imageAttributes |= (1 << vihDelayLoadShift);
    }
    if (ihDelayHandoff)
    {
        imageHeader->imageAttributes |= (1 << vihDelayHandoffShift);
    }
    if (ihMemCpyAddr != 0xFFFFFFFFFFFFFFFF)
    {
        imageHeader->imageAttributes |= (1 << vihCopyToMemoryShift);
    }
}

/******************************************************************************/
void VersalImageHeader::SetImageName(void)
{
    /* Only 16-bytes available to store the name
    name length <= 16 bytes - complete name
    name length > 16 bytes - some part of name + file extension
    */
    std::string ext = StringUtils::GetExtension(Name);
    uint32_t i = 0;
    if (Name.size() <= 16)
    {
        for (i = 0; i < Name.size(); i++)
        {
            imageHeader->imageName[i] = Name[i];
        }
    }
    else
    {
        for (i = 0; i < (16 - ext.size()); i++)
        {
            imageHeader->imageName[i] = Name[i];
        }
        uint8_t j = 0;
        for (; i < 16; i++)
        {
            imageHeader->imageName[i] = ext[j++];
        }
    }
}

/******************************************************************************/
uint32_t VersalImageHeader::GetPartitionHeaderOffset(void)
{
    return imageHeader->partitionHeaderWordOffset * sizeof(uint32_t);
}

/******************************************************************************/
uint32_t VersalImageHeader::GetDataSectionCount(void)
{
    return imageHeader->dataSectionCount;
}

/******************************************************************************/
void VersalImageHeader::ImportElf(BootImage& bi)
{
    uint8_t proc_state = 0;

    domain = Domain::PS;
    SetPartitionType(PartitionType::ELF);

    /* Convert the file into array of bytes */
    ByteFile data(Filename);

    /* Get the ELF Class format - 32-bit elf vs 64-bit elf */
    elfClass = GetElfClass(data.bytes);
    auto elf = ElfFormat::GetElfFormat(elfClass, data.bytes, &proc_state, data.len);

    /* Check for no. of executable sections & non-zero size LOAD sections */
    uint8_t exec_count = 0;
    uint8_t non_zero_elf_sec_count = 0;

    for (uint8_t iprog = 0; iprog < elf->programHdrEntryCount; iprog++)
    {
        if (elf->GetProgramHeaderFlags(iprog) & PF_X)
        {
            exec_count++;
        }
        if ((elf->GetProgramHeaderFileSize(iprog) > 0) && (elf->GetProgramHeaderType(iprog) == xPT_LOAD))
        {
            non_zero_elf_sec_count++;
        }
    }

    if (non_zero_elf_sec_count == 0)
    {
        LOG_ERROR("No non-empty program sections in %s", Filename.c_str());
    }

    // Variables for combining sections
    uint32_t num_hdrs = 0;
    uint32_t hdr_index = 0;
    Binary::Length_t size = 0;
    Binary::Length_t total_size = 0;
    Binary::Address_t addr = 0;
    Binary::Address_t prev_end = 0;
    Binary::Address_t offset = 0;

    Binary::Address_t load_addr = 0;
    Binary::Address_t exec_addr = 0;
    std::vector<uint8_t> partition_data;  // Changed from raw pointer to vector for automatic memory management

    /* Loop through all the program headers and populate the fields exec, load address etc. */
    for (uint8_t iprog = 0; iprog < elf->programHdrEntryCount; iprog++)
    {
        bool first_index = false;
        size = elf->GetProgramHeaderFileSize(iprog);
        addr = elf->GetPhysicalAddress(iprog);
        exec_addr = elf->GetStartUpAddress();

        /* For bootloader, all the sections are combined by padding the gaps to create only partition */
        if (Bootloader)
        {
            bi.imageHeaderTable->SetOptionalData(elf->iht_optional_data, elf->iht_optional_data_size);
            bi.imageHeaderTable->SetXplmModulesData(bi, elf->xplm_modules_data, elf->xplm_modules_data_size);

            /* Only loadable sections with non-size are considered */
            if ((elf->GetProgramHeaderType(iprog) == xPT_LOAD) && (size > 0))
            {
                num_hdrs++;

                /* Consider load address of first section */
                if (num_hdrs == 1)
                {
                    load_addr = addr;
                }
                else
                {
                    /* Calculate filler bytes and pad to combine different sections */
                    Binary::Length_t filler_bytes = addr - prev_end;
                    if (filler_bytes != 0)
                    {
                        total_size += filler_bytes;
                        partition_data.resize(total_size);
                        memset(partition_data.data() + offset, 0, filler_bytes);
                        offset = total_size;
                    }
                }
                total_size += size;
                partition_data.resize(total_size);
                memcpy(partition_data.data() + offset, elf->GetProgramHeaderData(iprog), size);
                prev_end = addr + size;
                offset = total_size;
            }
            /* Loop through till last entry */
            if (iprog < elf->programHdrEntryCount - 1)
            {
                continue;
            }

            /* Calculate pad bytes for aligning PMC FW for 16 byte-alignment to add to lengths populated in boot header */
            uint32_t pmc_fw_pad_bytes = 0;
            uint64_t pmc_fw_size = total_size;
            if(updateReserveInPh == true)
            {
                if(Reserve.IsSet())
                {
                    if(pmc_fw_size > Reserve.Value())
                    {
                        LOG_WARNING("Total Partition length is more than Reserve Length. Hence reserve attribute is ignored.");
                    }
                    else
                    {
                        pmc_fw_size = Reserve.Value();
                    }
                }
            }
            if (pmc_fw_size % 16 != 0)
            {
                pmc_fw_pad_bytes = 16 - (pmc_fw_size % 16);
            }
            SetFsblFwSizeIh(pmc_fw_size + pmc_fw_pad_bytes);
            SetTotalFsblFwSizeIh(pmc_fw_size + pmc_fw_pad_bytes);

            if ((bi.bifOptions->GetPmcCdoFileList().size() > 0) || (bi.bifOptions->GetPmcDataBuffer() != NULL))
            {
                uint8_t* cdo_partition = NULL;
                size_t cdo_length = 0;
                if(bi.bifOptions->GetPmcCdoFileList().size() != 0)
                {
                    LOG_INFO("File for merging - %s ", bi.bifOptions->GetPmcCdoFileList().at(0).c_str());
                }

                bool add_ssit_sync_master_cmd = (bi.bifOptions->pdiType == PartitionType::SLR_SLAVE_BOOT);
                ParseCdos(bi, bi.bifOptions->GetPmcCdoFileList(), &cdo_partition, &cdo_length, add_ssit_sync_master_cmd);
                /* Calculate pad bytes for aligning PMC CDO for 16 byte-alignment */
                uint32_t total_cdo_pad_bytes = 0;
                if (cdo_length % 16 != 0)
                {
                    total_cdo_pad_bytes = 16 - (cdo_length % 16);
                }

                /* Append PMC CDO to PMC FW to create a single partition */
                pmcdataSize = totalpmcdataSize = cdo_length + total_cdo_pad_bytes;
                total_size = pmc_fw_size + pmc_fw_pad_bytes + pmcdataSize;
                partition_data.resize(total_size);
                memset(partition_data.data() + pmc_fw_size, 0, pmc_fw_pad_bytes);
                memcpy(partition_data.data() + pmc_fw_size + pmc_fw_pad_bytes, cdo_partition, cdo_length);
                memset(partition_data.data() + pmc_fw_size + pmc_fw_pad_bytes + cdo_length, 0, total_cdo_pad_bytes);

                free(cdo_partition);
            }
        }
        /* For all other partitions add each loadable section as a different partition */
        else
        {
            /* Only loadable sections with non-size are considered */
            if ((elf->GetProgramHeaderType(iprog) == xPT_LOAD) && (size > 0))
            {
                load_addr = addr;
                total_size = size;
                if(updateReserveInPh == true)
                {
                    if(Reserve.IsSet() && non_zero_elf_sec_count > 1)
                    {
                        LOG_WARNING("Multiple sections in elf. Hence reserve attribute is ignored.");
                    }
                    if(Reserve.IsSet() && non_zero_elf_sec_count == 1)
                    {
                        if(total_size > Reserve.Value())
                        {
                            LOG_WARNING("Total Partition length is more than Reserve Length. Hence reserve attribute is ignored.");
                        }
                        else
                        {
                            total_size = Reserve.Value();
                        }
                    }
                }
                partition_data.resize(total_size);
                memcpy(partition_data.data(), elf->GetProgramHeaderData(iprog), total_size);
            }
        }

        if (!partition_data.empty())
        {
            if (hdr_index == 0)
            {
                first_index = true;
            }

            /* Override the exec and load address, if startup & load are set through BIF attributes */
            if (Startup.IsSet() && iprog == 0)
            {
                exec_addr = Startup.Value();
            }
            if (Load.IsSet() && iprog == 0)
            {
                load_addr = Load.Value();
            }
            auto partHdr = std::make_unique<VersalPartitionHeader>(this, hdr_index);
            partHdr->firstValidIndex = first_index;
            partHdr->elfEndianess = elf->endian;
            partHdr->execAddress = exec_addr;
            partHdr->loadAddress = load_addr;
            partHdr->execState = proc_state;
            if (elf->atf_handoff_params_prg_hdr_count == iprog && elf->atf_handoff_params_found)
            {
                partHdr->atf_handoff_params_offset = elf->atf_handoff_params_offset;
                partHdr->update_atf_handoff_params = true;
            }

            partHdr.get()->partition = std::make_unique<VersalPartition>(partHdr.get(), partition_data.data(), total_size);

            // This length also includes padding size necessary for 16-byte alignment
            partHdr->partitionSize = partHdr->partition->section->Length;

            partitionHeaderList.push_back(partHdr.release());
            hdr_index++;
            
            // Clear the vector after partition is created (partition constructor makes a copy)
            partition_data.clear();
        }
    }
}

/******************************************************************************/
void VersalImageHeader::ParseSlaveSlrConfigCdos(BootImage& bi, std::vector<std::string> filelist, uint8_t** cdo_data, size_t* cdo_size, bool add_ssit_sync_master)
{
    std::vector<uint8_t> total_cdo_data;  // Changed from raw pointer to vector for automatic memory management
    std::vector<uint8_t> cdo_padded_buffer;  // Changed from raw pointer to vector for automatic memory management
    size_t cdo_padded_buffer_length = 0;
    uint64_t total_cdo_length = 0;
    //void *cdo_data_pp = NULL;
    //size_t cdo_data_pp_length = 0;

    std::unique_ptr<uint32_t[]> start_marker_offsets;  // Smart pointer for auto cleanup
    uint8_t num_start_markers = 0;
    std::unique_ptr<uint32_t[]> end_marker_offsets;    // Smart pointer for auto cleanup
    uint8_t num_end_markers = 0;


    if (filelist.size() > 0)
    {
        /* Offset and length are set to CDO header size, to take into account the addition of
        merged CDO header which will be added at the end */
        uint64_t offset = sizeof(VersalCdoHeader);
        total_cdo_length = sizeof(VersalCdoHeader);

        for (uint8_t idx = 0; idx != filelist.size(); idx++)
        {
            void* cdo_data = NULL;
            size_t cdo_length = 0;
            uint64_t actual_cdo_size = 0;
            const char* cdo_filename = filelist.at(idx).c_str();
            CdoSequence * cdo_seq;
            CdoSequence * cdo_seq1;
            if (add_ssit_sync_master)
            {
                /* Add SSIT Sync Master command */
                #ifdef ENABLE_WDI
                if(isl::iostreams::xp::is_xp_format(cdo_filename))
                {
                    cdo_seq1 = Decompressing_File(cdo_filename);
                }
                else
                {
                    cdo_seq1 = cdoseq_load_cdo(cdo_filename);
                }
                #else
                cdo_seq1 = cdoseq_load_cdo(cdo_filename);
                #endif
                if (cdo_seq1 == NULL)
                {
                    LOG_ERROR("Error parsing CDO file");
                }
                cdo_seq = cdocmd_create_sequence();
                cdocmd_add_ssit_sync_master(cdo_seq);
                cdocmd_concat_seq(cdo_seq, cdo_seq1);
                cdocmd_delete_sequence(cdo_seq1);  // Free temporary sequence
                add_ssit_sync_master = false;
            }
            else
            {
                #ifdef ENABLE_WDI
                if(isl::iostreams::xp::is_xp_format(cdo_filename))
                {
                    cdo_seq = Decompressing_File(cdo_filename);
                }
                else
                {
                    cdo_seq = cdoseq_load_cdo(cdo_filename);
                }
                #else
                cdo_seq = cdoseq_load_cdo(cdo_filename);
                #endif
                if (cdo_seq == NULL)
                {
                    LOG_ERROR("Error parsing CDO file");
                }
            }

            if (bi.overlayCDO && cdooverlay_apply(cdo_seq, (CdoOverlayInfo *)(bi.overlayCDO)))
            {
                LOG_ERROR("Error applying overlay CDO file");
            }

            /* Enable the search for sync points - only needs to be done for SSIT devices */
            search_for_sync_points();
            cdo_data = cdoseq_to_binary(cdo_seq, &cdo_length, 0);
            CheckIdsInCdo(cdo_seq, bi.options.IsVersalNetSeries(), cdo_filename);
            cdocmd_delete_sequence(cdo_seq);  // Free CDO sequence memory

            
            if (cdo_length != 0)
            {
                actual_cdo_size = cdo_length - sizeof(VersalCdoHeader);
            }
            else
            {
                LOG_WARNING("Incorrect CDO length read from : %s", cdo_filename);
            }

            total_cdo_length += (actual_cdo_size);
            total_cdo_data.resize(total_cdo_length);
            memcpy(total_cdo_data.data() + offset, (uint8_t*)cdo_data + sizeof(VersalCdoHeader), actual_cdo_size);
            offset += actual_cdo_size;
            //delete cdo_data;
        }

        num_start_markers = get_num_start_markers();
        start_marker_offsets.reset(get_slr_start_marker_offsets());  // Wrap C pointer in unique_ptr
        num_end_markers = get_num_end_markers();
        end_marker_offsets.reset(get_slr_end_marker_offsets());      // Wrap C pointer in unique_ptr

        if (num_start_markers != num_end_markers)
        {
            LOG_ERROR("Number of start and end markers for NoC startup sequence is not same.");
        }

        if (num_start_markers > 0)
        {
            size_t size = 0, prev_start = 0;
            size_t start = 0, end = 0;
            uint32_t p_offset = 0;
            size_t copied_offset = 0;
            size_t pad_bytes = 0;
            LOG_TRACE("       markers found at offsets -");
            for (int i = 0; i < num_start_markers; i++)
            {
                start = start_marker_offsets[i] * 4;  // Use array indexing with unique_ptr
                end = end_marker_offsets[i] * 4;      // Use array indexing with unique_ptr
                /* Create a new chunk at start only if start and end are not in same 32K data */
                if (start  / SSIT_CHUNK_SIZE != end / SSIT_CHUNK_SIZE)
                {
                    size = start - prev_start;

                    pad_bytes = SSIT_CHUNK_SIZE - (size % SSIT_CHUNK_SIZE);
                    cdo_padded_buffer_length += (size + pad_bytes);

                    cdo_padded_buffer.resize(cdo_padded_buffer_length);
                    memcpy(cdo_padded_buffer.data() + p_offset, total_cdo_data.data() + copied_offset, size);
                    p_offset += size;
                    copied_offset += size;

                    if (pad_bytes != 0)
                    {
                        auto cdoCmd = CdoCmdNoOperation2(pad_bytes);
                        memcpy(cdo_padded_buffer.data() + p_offset, cdoCmd, CDO_CMD_NOP_SIZE + 4);
                        p_offset += (CDO_CMD_NOP_SIZE + 4);
                        if (cdoCmd->length > 0)
                        {
                            memset(cdo_padded_buffer.data() + p_offset, 0, cdoCmd->length * sizeof(uint32_t));
                            p_offset += (cdoCmd->length * sizeof(uint32_t));
                        }
                    }

                    prev_start = start;
                    LOG_TRACE("           offset = 0x%lx, end = 0x%lx", start, end);
                }
                else
                {
                    LOG_TRACE("           skipping marker at 0x%lx since end marker (0x%lx) is in same chunk", start, end);
                }
            }
            /* Remaining data after last marker */
            size = total_cdo_length - prev_start;
            cdo_padded_buffer_length += size;
            cdo_padded_buffer.resize(cdo_padded_buffer_length);
            memcpy(cdo_padded_buffer.data() + p_offset, total_cdo_data.data() + copied_offset, size);

            // Smart pointers auto-deleted at end of scope

            total_cdo_length = (cdo_padded_buffer_length);
            total_cdo_data = std::move(cdo_padded_buffer);  // Move instead of realloc+memcpy+free
        }

        auto cdo_header = std::make_unique<VersalCdoHeader>();
        cdo_header->remaining_words = 0x04;
        cdo_header->id_word = 0x004f4443; /* CDO */
        cdo_header->version = 0x00000200; /* Version - 2.0 */
        cdo_header->length = (total_cdo_length - sizeof(VersalCdoHeader)) / 4;
        cdo_header->checksum = ~(cdo_header->remaining_words + cdo_header->id_word + cdo_header->version + cdo_header->length);
        memcpy(buffer, cdo_header.get(), sizeof(VersalCdoHeader));
        // Cleanup handled by unique_ptr
    }

    *cdo_size = total_cdo_length;
    *cdo_data = (uint8_t*)malloc(total_cdo_length);
    memcpy(*cdo_data, total_cdo_data.data(), total_cdo_length);
}

/******************************************************************************/
void VersalImageHeader::ParseCdos(BootImage& bi, std::vector<std::string> filelist, uint8_t** cdo_data, size_t* cdo_size, bool add_ssit_sync_master)
{
    std::vector<uint8_t> total_cdo_data;  // Changed from raw pointer to vector for automatic memory management
    uint64_t total_cdo_length = 0;
    void *cdo_data_pp = NULL;
    size_t cdo_data_pp_length = 0;
    char slrid_from_source = 0;
    char slrid_from_binary = 0;
    char input_ch_souce = 0;
    char input_ch_binary = 0;
    bool is_source_cdo = false;
    bool is_binary_cdo = false;
    uint32_t idcode_source = 0;
    uint32_t source_cdo_idcode = 0;
    uint32_t idcode_binary = 0;
    uint32_t binary_cdo_idcode = 0;

    if (filelist.size() > 0)
    {
        /* Offset and length are set to CDO header size, to take into account the addition of
        merged CDO header which will be added at the end */
        uint64_t offset = sizeof(VersalCdoHeader);
        total_cdo_length = sizeof(VersalCdoHeader);
        for (uint8_t idx = 0; idx != filelist.size(); idx++)
        {
            void* cdo_data = NULL;
            size_t cdo_length = 0;
            uint64_t actual_cdo_size = 0;
            const char* cdo_filename = filelist.at(idx).c_str();
            CdoSequence * cdo_seq;
            CdoSequence * cdo_seq1;

            if (add_ssit_sync_master)
            {
                /* Add SSIT Sync Master command */
                #ifdef ENABLE_WDI
                if(isl::iostreams::xp::is_xp_format(cdo_filename))
                {
                    cdo_seq1 = Decompressing_File(cdo_filename);
                }
                else
                {
                    cdo_seq1 = cdoseq_load_cdo(cdo_filename);
                }
                #else
                cdo_seq1 = cdoseq_load_cdo(cdo_filename);
                #endif
                if (cdo_seq1 == NULL)
                {
                    LOG_ERROR("Error parsing CDO file");
                }
                cdo_seq = cdocmd_create_sequence();
                cdocmd_add_ssit_sync_master(cdo_seq);
                cdocmd_concat_seq(cdo_seq, cdo_seq1);
                cdocmd_delete_sequence(cdo_seq1);  // Free temporary sequence
                add_ssit_sync_master = false;
            }
            else
            {
                #ifdef ENABLE_WDI
                if(isl::iostreams::xp::is_xp_format(cdo_filename))
                {
                    cdo_seq = Decompressing_File(cdo_filename);
                }
                else
                {
                    cdo_seq = cdoseq_load_cdo(cdo_filename);
                }
                #else
                cdo_seq = cdoseq_load_cdo(cdo_filename);
                #endif
                if (cdo_seq == NULL)
                {
                    LOG_ERROR("Error parsing CDO file");
                }
            }

            slrid_from_source = slr_id_from_source(input_ch_souce);
            if (slrid_from_source != 0)
            {
                if (bi.globalSlrId != 0)
                {
                    is_source_cdo = (bi.globalSlrId == slrid_from_source);
                    if (is_source_cdo == false)
                    {
                        LOG_WARNING("Mismatch between SLR ID of %s and rest of the CDOs. This may cause runtime issues. \n           Please ensure that all the CDOs used for creating the pdi are for the same SLR/device", cdo_filename);
                    }
                    bi.globalSlrId = 0;
                }
                bi.globalSlrId = slrid_from_source;
            }
            else
            {
                slrid_from_binary = slr_id_from_binary(input_ch_binary);
                if (slrid_from_binary != 0)
                {
                   if (bi.globalSlrId != 0)
                   {
                       is_binary_cdo = (bi.globalSlrId == slrid_from_binary);
                       if (is_binary_cdo == false)
                       {
                          LOG_WARNING("Mismatch between SLR ID of %s and rest of the CDOs. This may cause runtime issues. \n           Please ensure that all the CDOs used for creating the pdi are for the same SLR/device", cdo_filename);
                       }
                       bi.globalSlrId = 0;
                   }
                   else
                   {
                       bi.globalSlrId = slrid_from_binary;
                   }
                }
            }
            if (bi.overlayCDO && cdooverlay_apply(cdo_seq, (CdoOverlayInfo *)(bi.overlayCDO)))
            {
                LOG_ERROR("Error applying overlay CDO file");
            }
            cdo_data = cdoseq_to_binary(cdo_seq, &cdo_length, 0);
            CheckIdsInCdo(cdo_seq, bi.options.IsVersalNetSeries(), cdo_filename);
            // TODO: Call this only for v2
            const char * env = getenv("BOOTGEN_CHECK_CDO_COMMANDS");
            if (env && *env != '\0') {
                if (check_cdo_commands(cdo_data, cdo_length, bi.xplm_modules_data.get(), bi.xplm_modules_data_length) != 0) {
                    LOG_WARNING("Invalid PLM cdo command is found in input cdo file");
                }
            }
            cdocmd_delete_sequence(cdo_seq);  // Free CDO sequence memory
            
            if (cdocmd_post_process_cdo(cdo_data, cdo_length, &cdo_data_pp, &cdo_data_pp_length))
            {
                LOG_ERROR("PMC CDO post process error");
            }
            
            source_cdo_idcode = idcode_from_source(idcode_source);
            binary_cdo_idcode = idcode_from_binary(idcode_binary);
            if (source_cdo_idcode != 0)
            {
               if (bi.bifOptions->idCode != source_cdo_idcode)
               {
                   //LOG_WARNING("CDO %s is of different device than cdos present in pdi", cdo_filename);
               }
            } 
            else if (binary_cdo_idcode != 0)
            {
               if (bi.bifOptions->idCode != binary_cdo_idcode)
               {
                   //LOG_WARNING("CDO %s is of different device than cdos present in pdi", cdo_filename);
               }
            }
            if (cdo_data_pp != NULL)
            {
                free(cdo_data);  // Free original cdo_data before replacing it
                cdo_data = (uint8_t*)cdo_data_pp;
                cdo_length = cdo_data_pp_length;
                cdo_data_pp = NULL;
            }

            if (cdo_length > sizeof(VersalCdoHeader))
            {
                actual_cdo_size = cdo_length - sizeof(VersalCdoHeader);
            }
            else
            {
                LOG_WARNING("Incorrect cdo length read from : %s", cdo_filename);
            }

            total_cdo_length += (actual_cdo_size);
            total_cdo_data.resize(total_cdo_length);
            memcpy(total_cdo_data.data() + offset, (uint8_t*)cdo_data + sizeof(VersalCdoHeader), actual_cdo_size);
            offset += actual_cdo_size;
            free(cdo_data);  // Free cdo_data (from cdoseq_to_binary or cdocmd_post_process_cdo)
            cdo_data = NULL;
        }
        auto cdo_header = std::make_unique<VersalCdoHeader>();
        cdo_header->remaining_words = 0x04;
        cdo_header->id_word = 0x004f4443; /* CDO */
        cdo_header->version = 0x00000200; /* Version - 2.0 */
        cdo_header->length = (total_cdo_length - sizeof(VersalCdoHeader)) / 4;
        cdo_header->checksum = ~(cdo_header->remaining_words + cdo_header->id_word + cdo_header->version + cdo_header->length);
        memcpy(total_cdo_data.data(), cdo_header.get(), sizeof(VersalCdoHeader));
        // Cleanup handled by unique_ptr
    }
    else
    {
        /* If PMC CDO is passed as a buffer 
           or in case PDI is passed as input in BIF, PMC data is read into a buffer from the PDI */
        total_cdo_length = bi.bifOptions->GetTotalpmcdataSize();
        total_cdo_data.resize(total_cdo_length);
        memcpy(total_cdo_data.data(), bi.bifOptions->GetPmcDataBuffer(), total_cdo_length);
    }

    // Allocate output buffer and copy vector data (C-style interface requires malloc'd pointer)
    *cdo_size = total_cdo_length;
    *cdo_data = (uint8_t*)malloc(total_cdo_length);
    memcpy(*cdo_data, total_cdo_data.data(), total_cdo_length);
    // Vector automatically cleaned up at end of scope
}

/******************************************************************************/
void VersalImageHeader::CreateAieEnginePartition(BootImage& bi)
{
    bool is_dir = false;
    std::ifstream aie_file(Filename);
    uint64_t size = 0;
    struct stat buf;
    std::list<std::string> aie_elf_list;

    if (stat(Filename.c_str(), &buf) == 0)
    {
        is_dir = S_IFDIR & buf.st_mode;
    }
    if (!is_dir)
    {
        aie_elf_list = bi.bifOptions->aie_elfs;
    }
    else
    {
        aie_elf_list = GetAieFilesPath(Filename);
    }
    for (std::list<std::string>::iterator aie_file = aie_elf_list.begin(); aie_file != aie_elf_list.end(); aie_file++)
    {
        size += ImportAieEngineElfCdo(*aie_file);
    }

    cdoHeader = std::make_unique<VersalCdoHeader>();
    cdoHeader->remaining_words = 0x04;
    cdoHeader->id_word = 0x004f4443; /* CDO */
    cdoHeader->version = 0x00000200; /* Version - 2.0 */
    size += sizeof(VersalCdoHeader);
    auto partHdr = std::make_unique<VersalPartitionHeader>(this, 0);
    partHdr->execState = 0;
    partHdr->elfEndianess = 0;
    partHdr->firstValidIndex = true;

    auto pBuffer = std::make_unique<uint8_t[]>(size);
    uint32_t p_offset = 0;
    cdoHeader->length = (size - sizeof(VersalCdoHeader)) / 4;
    cdoHeader->checksum = ~(cdoHeader->remaining_words + cdoHeader->id_word + cdoHeader->version + cdoHeader->length);
    memcpy(pBuffer.get(), cdoHeader.get(), sizeof(VersalCdoHeader));
    p_offset += sizeof(VersalCdoHeader);
    for (auto& cdoPtr : cdoSections)  // Use range-based loop with smart pointers
    {
        memcpy(pBuffer.get() + p_offset, cdoPtr.get(), CDO_COMMAND_SIZE);
        p_offset += CDO_COMMAND_SIZE;
        memcpy(pBuffer.get() + p_offset, cdoPtr->data, (cdoPtr->length - 2) * 4);
        p_offset += ((cdoPtr->length - 2) * 4);
        // Smart pointer auto-deletes
    }

    partHdr->partitionSize = size;
    partHdr.get()->partition = std::make_unique<VersalPartition>(partHdr.get(), pBuffer.get(), size);
    partitionHeaderList.push_back(partHdr.release());
    cdoSections.clear();
}

/******************************************************************************/
std::list<std::string> VersalImageHeader::GetAieFilesPath(std::string filename)
{
    std::list <std::string> aie_elf_list;
    std::list <std::string> core_list;
    std::string json_file;
    std::string file_path;
    file_path = filename + "//" + "aie" + "//";
    json_file = file_path + "active_cores.json";
    std::ifstream aie_json(json_file);

    if (!aie_json.good())
    {
        LOG_ERROR("AIE Work \"%s\" directory doesn't have AIE folder", filename.c_str());
    }

    core_list = ParseAieJson(json_file.c_str());
    for (std::list<std::string>::iterator aie_file = core_list.begin(); aie_file != core_list.end(); aie_file++)
    {
        std::string aie_elf = file_path + (*aie_file) + "//" + "/Release/" + "//" + (*aie_file);
        aie_elf_list.push_back(aie_elf);
    }
    return aie_elf_list;
}

/******************************************************************************/
uint64_t VersalImageHeader::ImportAieEngineElfCdo(std::string aie_file)
{
    uint32_t progHdrCnt = 0;
    std::vector<uint8_t> newData;  // Changed from raw pointer to vector for automatic memory management
    uint32_t rowNum = 0;
    uint32_t colNum = 0;

    SetPartitionType(PartitionType::CONFIG_DATA_OBJ);

    /* Convert the file into array of bytes */
    ByteFile data(aie_file);

    /* Get the ELF Class format - 32-bit elf vs 64-bit elf */
    ElfFormat32 elf(data.bytes);

    /* Check for no. of executable sections
    & non-zero size LOAD sections */
    uint8_t execCount = 0;
    uint8_t nonZeroCount = 0;
    for (uint32_t iprog = 0; iprog < elf.programHdrEntryCount; iprog++)
    {
        if (elf.GetProgramHeaderFlags(iprog) & PF_X)
        {
            execCount++;
        }
        if ((elf.GetProgramHeaderFileSize(iprog) > 0) && (elf.GetProgramHeaderType(iprog) == xPT_LOAD))
        {
            nonZeroCount++;
        }
    }

    if (nonZeroCount == 0)
    {
        LOG_ERROR("No non-empty program sections in %s", Filename.c_str());
    }

    // Extract column and row numbers from elf file name (elfname_<colnum>_<rownum>.elf)
    // Example: test_2_4.elf -> AIE core with column-2 and row-4
    std::string temp = StringUtils::RemoveExtension(StringUtils::BaseName(aie_file));
    size_t pos = temp.find_last_of(".");
    if (pos != std::string::npos)
    {
        temp = temp.substr(pos+1);
    }
    std::vector<std::string> coreName = SplitString(temp, '_');
    if (coreName.size() != 2)
    {
        LOG_ERROR("Invalid core name in %s", aie_file.c_str());
    }
    colNum = std::stoi(coreName.at(0));
    rowNum = std::stoi(coreName.at(1));

    /* AIE Tile details in the form of [ColumnNum (16-bits) | RowNum(16-bits)]
    to be put into Exec Address Lo field in case of AIE partition */
    LOG_INFO("Parsing AIE elfs - %s", Filename.c_str());
    CalculateAieEngineBaseAddress(colNum, rowNum);
    /* Loop through all the program headers and populate the fields like, startup address,
    load address etc., which can be overrided by the BIF file options */

    uint32_t numHdrs = 0;
    std::size_t size = 0;
    std::size_t prevSize = 0;
    std::size_t totalSize = 0;
    uint64_t addr = 0;
    uint64_t prevAddr = 0;
    uint64_t offset = 0;
    Binary::Address_t textSecAddr = 0;

    Elf32ProgramHeader* elfPrgHeader = NULL;

    uint64_t total_psize = 0;

    for (uint32_t iprog = 0; iprog < elf.programHdrEntryCount; iprog++)
    {
        elfPrgHeader = elf.programHeaders[iprog];
        // For executable sections (combine all text sections) (addr < 0x20000 corresponds to AIE program memory)
        if ((elfPrgHeader->p_type == xPT_LOAD) && (elfPrgHeader->p_paddr < 0x20000) && (elfPrgHeader->p_filesz > 0) && (elfPrgHeader->p_flags & PF_X))
        {
            size = elfPrgHeader->p_filesz;
            addr = elfPrgHeader->p_paddr;
            // Consider 1st text section for load address in PHT
            if (numHdrs == 0)
            {
                textSecAddr = elf.GetPhysicalAddress(iprog);
            }

            numHdrs++;

            if (numHdrs > 1)
            {
                std::size_t fillerBytes = (std::size_t) (addr - (prevAddr + prevSize));
                totalSize += fillerBytes;
                newData.resize(totalSize);
                memset(newData.data() + offset, 0, fillerBytes);
                offset = totalSize;
            }
            /* Populate the section data */
            totalSize += size;
            newData.resize(totalSize);
            memcpy(newData.data() + offset, elfPrgHeader->data, size);
            prevAddr = addr;
            prevSize = size;
            offset = totalSize;
        }
    }

    total_psize += CdoCmdDmaWrite(totalSize, GetAieEngineGlobalAddress(textSecAddr), newData.data());
    // No need to free - vector automatically cleans up

    for (uint8_t iprog = 0; iprog < elf.programHdrEntryCount; iprog++)
    {
        elfPrgHeader = elf.programHeaders[iprog];
        // For data sections (addr >= 0x20000 corresponds to AIE data memory)
        if ((elfPrgHeader->p_type == xPT_LOAD) && !(elfPrgHeader->p_flags & PF_X) && (elfPrgHeader->p_paddr >= 0x20000))
        {
            if (elfPrgHeader->p_filesz > 0)
            {
                Binary::Length_t previousPartitionSize = 0, spill, pSize;
                Binary::Address_t pAddr;
                do
                {
                    spill = CheckAieEngineDataMemoryBoundary((Binary::Address_t)elf.GetPhysicalAddress(iprog) + previousPartitionSize,
                        elfPrgHeader->p_filesz - previousPartitionSize);

                    pSize = elfPrgHeader->p_filesz - spill - previousPartitionSize;
                    pAddr = GetAieEngineGlobalAddress((Binary::Address_t)elf.GetPhysicalAddress(iprog) + previousPartitionSize);
                    total_psize += CdoCmdDmaWrite(pSize, pAddr, elfPrgHeader->data + previousPartitionSize);
                    previousPartitionSize += pSize;
                    progHdrCnt++;
                } while (spill);
            }
            else if (elfPrgHeader->p_memsz > 0)
            {
                Binary::Length_t previousPartitionSize = 0, spill;
                do
                {
                    spill = CheckAieEngineDataMemoryBoundary((Binary::Address_t)elf.GetPhysicalAddress(iprog) + previousPartitionSize,
                        elfPrgHeader->p_memsz - previousPartitionSize);

                    uint32_t pSize = elfPrgHeader->p_memsz - spill - previousPartitionSize;
                    uint64_t pAddr = GetAieEngineGlobalAddress((Binary::Address_t)elf.GetPhysicalAddress(iprog) + previousPartitionSize);
                    uint32_t p_size_pad = pSize + ((4 - (pSize & 3)) & 3);
                    auto databuffer = std::make_unique<uint8_t[]>(p_size_pad);
                    memset(databuffer.get(), 0, p_size_pad);
                    previousPartitionSize += pSize;
                    total_psize += CdoCmdDmaWrite(pSize, pAddr, databuffer.get());
                    progHdrCnt++;
                } while (spill);
            }
        }
    }
    return total_psize;
}

/* In physical address space, PM is mapped to 0x20000 and DM is mapped to 0x0,
* whereas in logical address space, PM is mapped to 0x0 and DM is mapped to 0x20000.
* Also, the DM is spread across banks in 4 directions, so adjust the row/tile number,
* based on the bank in which the DM address falls. Below are the details about how to
* adjust the row/col number based on the DM address.
* +----------------------------+---------------+---------------+
* | Bank                       |   Odd Row     |    Even Row   |
* |----------------------------|---------------|---------------|
* | South (0 � 0x7fff)         |   row - 1     |    row  - 1   |
* | West (0x8000 � 0xffff)     |   same tile   |    col - 1    |
* | North (0x10000 � 0x17fff)  |   row + 1     |    row + 1    |
* | East (0x18000 � 0x1ffff)   |   col + 1     |    same tile  |
* +----------------------------+---------------+---------------+
* Row 0 is now assigned to shim, so we should be using row - 1 to check if the row
* is odd or even
*/

/******************************************************************************/
void VersalImageHeader::ImportAieEngineElf(BootImage& bi)
{
    uint32_t progHdrCnt = 0;
    uint8_t procState = 0;
    std::vector<uint8_t> newData;  // Changed from raw pointer to vector for automatic memory management
    uint32_t rowNum = 0;
    uint32_t colNum = 0;

    SetPartitionType(PartitionType::ELF);

    /* Convert the file into array of bytes */
    ByteFile data(Filename);

    /* Get the ELF Class format - 32-bit elf vs 64-bit elf */
    ElfFormat32 elf(data.bytes);

    /* Check for no. of executable sections
    & non-zero size LOAD sections */
    uint8_t execCount = 0;
    uint8_t nonZeroCount = 0;
    for (uint32_t iprog = 0; iprog < elf.programHdrEntryCount; iprog++)
    {
        if (elf.GetProgramHeaderFlags(iprog) & PF_X)
        {
            execCount++;
        }
        if ((elf.GetProgramHeaderFileSize(iprog) > 0) && (elf.GetProgramHeaderType(iprog) == xPT_LOAD))
        {
            nonZeroCount++;
        }
    }

    if (nonZeroCount == 0)
    {
        LOG_ERROR("No non-empty program sections in %s", Filename.c_str());
    }

    // Extract column and row numbers from elf file name (elfname_<colnum>_<rownum>.elf)
    // Example: test_2_4.elf -> AIE core with column-2 and row-4
    std::string temp = StringUtils::RemoveExtension(StringUtils::BaseName(Filename));
    std::vector<std::string> coreName = SplitString(temp, '_');
    if (coreName.size() != 2)
    {
        LOG_ERROR("Invalid core name in %s", Filename.c_str());
    }
    colNum = std::stoi(coreName.at(0));
    rowNum = std::stoi(coreName.at(1));

    //colNum = std::stoi(temp.substr((temp.length() - 3), 1));
    //rowNum = std::stoi(temp.substr((temp.length() - 1), 1));

    /* AIE Tile details in the form of [ColumnNum (16-bits) | RowNum(16-bits)]
    to be put into Exec Address Lo field in case of AIE partition */
    uint64_t aieTileDetails = (colNum << 16) | (rowNum);

    CalculateAieEngineBaseAddress(colNum, rowNum);
    /* Loop through all the program headers and populate the fields like, startup address,
    load address etc., which can be overrided by the BIF file options */

    uint32_t numHdrs = 0;
    std::size_t size = 0;
    std::size_t prevSize = 0;
    std::size_t totalSize = 0;
    uint64_t addr = 0;
    uint64_t prevAddr = 0;
    uint64_t offset = 0;
    uint8_t padding = 0;
    Binary::Address_t textSecAddr = 0;

    Elf32ProgramHeader* elfPrgHeader = NULL;
    auto partHdr = std::make_unique<VersalPartitionHeader>(this, progHdrCnt);
    partHdr->execState = procState;
    partHdr->elfEndianess = elf.endian;

    for (uint32_t iprog = 0; iprog < elf.programHdrEntryCount; iprog++)
    {
        elfPrgHeader = elf.programHeaders[iprog];
        // For executable sections (combine all text sections) (addr < 0x20000 corresponds to AIE program memory)
        if ((elfPrgHeader->p_type == xPT_LOAD) && (elfPrgHeader->p_paddr < 0x20000) && (elfPrgHeader->p_filesz > 0) && (elfPrgHeader->p_flags & PF_X))
        {
            size = elfPrgHeader->p_filesz;
            addr = elfPrgHeader->p_paddr;
            // Consider 1st text section for load address in PHT
            if (numHdrs == 0)
            {
                textSecAddr = elf.GetPhysicalAddress(iprog);
            }

            numHdrs++;

            if (numHdrs > 1)
            {
                std::size_t fillerBytes = (std::size_t) (addr - (prevAddr + prevSize));
                totalSize += fillerBytes;
                newData.resize(totalSize);
                memset(newData.data() + offset, 0, fillerBytes);
                offset = totalSize;
            }
            /* Populate the section data */
            totalSize += size;
            newData.resize(totalSize);
            memcpy(newData.data() + offset, elfPrgHeader->data, size);
            prevAddr = addr;
            prevSize = size;
            offset = totalSize;
            partHdr->firstValidIndex = true;
        }
    }
    //padding = (4 - (totalSize & 3) & 3);
    partHdr->partitionSize = totalSize + padding;

    /* AIE Tile details in Exec address in case of AIE partitions
    Load address & execution address will be same for AIE elfs, so no need of separate exec addr field*/
    //partHdr->execAddress = GetAieEngineGlobalAddress((Binary::Address_t)elf.GetStartUpAddress());
    partHdr->execAddress = aieTileDetails;

    // consider addr of first header
    partHdr->loadAddress = GetAieEngineGlobalAddress(textSecAddr);
    /* Create a new partition out of each valid program header */
    partHdr.get()->partition = std::make_unique<VersalPartition>(partHdr.get(), newData.data(), partHdr->partitionSize);
    progHdrCnt++;
    partitionHeaderList.push_back(partHdr.release());

    for (uint8_t iprog = 0; iprog < elf.programHdrEntryCount; iprog++)
    {
        elfPrgHeader = elf.programHeaders[iprog];
        // For data sections (addr >= 0x20000 corresponds to AIE data memory)
        if ((elfPrgHeader->p_type == xPT_LOAD) && !(elfPrgHeader->p_flags & PF_X) && (elfPrgHeader->p_paddr >= 0x20000))
        {
            if (elfPrgHeader->p_filesz > 0)
            {
                Binary::Length_t previousPartitionSize = 0, spill;
                do
                {
                    spill = CheckAieEngineDataMemoryBoundary((Binary::Address_t)elf.GetPhysicalAddress(iprog) + previousPartitionSize,
                        elfPrgHeader->p_filesz - previousPartitionSize);

                    auto partHdr = std::make_unique<VersalPartitionHeader>(this, progHdrCnt);
                    partHdr->partitionType = PartitionType::ELF;
                    partHdr->partitionSize = elfPrgHeader->p_filesz - spill - previousPartitionSize;
                    partHdr->loadAddress = GetAieEngineGlobalAddress((Binary::Address_t)elf.GetPhysicalAddress(iprog) + previousPartitionSize);
                    partHdr.get()->partition = std::make_unique<VersalPartition>(partHdr.get(), elfPrgHeader->data + previousPartitionSize, partHdr->partitionSize);
                    previousPartitionSize += partHdr->partitionSize;
                    partitionHeaderList.push_back(partHdr.release());
                    progHdrCnt++;
                } while (spill);
            }
            else if (elfPrgHeader->p_memsz > 0)
            {
                Binary::Length_t previousPartitionSize = 0, spill;
                do
                {
                    spill = CheckAieEngineDataMemoryBoundary((Binary::Address_t)elf.GetPhysicalAddress(iprog) + previousPartitionSize,
                        elfPrgHeader->p_memsz - previousPartitionSize);

                    auto partHdr = std::make_unique<VersalPartitionHeader>(this, progHdrCnt);
                    partHdr->partitionType = PartitionType::ELF;
                    partHdr->partitionSize = elfPrgHeader->p_memsz - spill - previousPartitionSize;
                    partHdr->loadAddress = GetAieEngineGlobalAddress((Binary::Address_t)elf.GetPhysicalAddress(iprog) + previousPartitionSize);
                    previousPartitionSize += partHdr->partitionSize;
                    auto databuffer = std::make_unique<uint8_t[]>(partHdr->partitionSize);
                    memset(databuffer.get(), 0, partHdr->partitionSize);
                    partHdr.get()->partition = std::make_unique<VersalPartition>(partHdr.get(), databuffer.get(), partHdr->partitionSize);
                    partitionHeaderList.push_back(partHdr.release());
                    progHdrCnt++;
                } while (spill);
            }
        }
    }
    // No need to free - vector automatically cleans up
}

/******************************************************************************/
uint32_t VersalImageHeader::CdoCmdDmaWrite(uint32_t pSize, uint64_t pAddr, uint8_t *databuffer)
{
    uint32_t total_size;
    auto cdoDataSec = std::make_unique<CdoCommandDmaWrite>();
    uint32_t p_size_pad = pSize + ((4 - (pSize & 3)) & 3);
    cdoDataSec->header = 0x00ff0105;
    cdoDataSec->length = (p_size_pad / 4) + 2;
    cdoDataSec->hi_address = ((pAddr) >> 32) & 0xFFFFFFFF;
    cdoDataSec->lo_address = (pAddr) & 0xFFFFFFFF;
    auto cdoDataSec_data = std::make_unique<uint8_t[]>(p_size_pad);
    memset(cdoDataSec_data.get(), 0, p_size_pad);
    memcpy(cdoDataSec_data.get(), databuffer, pSize);
    cdoDataSec->data = cdoDataSec_data.release();  // Transfer ownership to struct
    cdoSections.push_back(std::move(cdoDataSec));  // Move unique_ptr into list
    total_size = (CDO_COMMAND_SIZE + p_size_pad);
    LOG_TRACE("AIE ELF CDO DMA Write Command: Address-0x%x%08x, Size-%x", cdoDataSec->hi_address, cdoDataSec->lo_address, p_size_pad);
    return total_size;
}

/******************************************************************************/
uint32_t VersalImageHeader::CdoCmdWriteImageStore(uint32_t pSize, uint64_t pdi_id, uint8_t *databuffer)
{
    uint32_t total_size;
    auto cdoDataSec = std::make_unique<CdoCommandWriteImageStore>();
    uint32_t p_size_pad = pSize + ((4 - (pSize & 3)) & 3);
    cdoDataSec->header = 0x00ff070D;
    cdoDataSec->length = (p_size_pad / 4) + 2;
    cdoDataSec->id = pdi_id;
    auto cdoDataSec_data = std::make_unique<uint8_t[]>(p_size_pad);
    memset(cdoDataSec_data.get(), 0, p_size_pad);
    memcpy(cdoDataSec_data.get(), databuffer, pSize);
    cdoDataSec->data = cdoDataSec_data.release();  // Transfer ownership to struct
    total_size = (CDO_COMMAND_SIZE + p_size_pad);
    return total_size;
}

/******************************************************************************/
void VersalImageHeader::CalculateAieEngineBaseAddress(uint32_t colNum, uint32_t rowNum)
{

#define AIE_ARRAY_TILE_STRIDE 0x40000
#define AIE_ARRAY_ROW_WIDTH 32

    // Row-0 is always used for SHIM, so actual rows start from 1
    rowNum++;
    uint32_t coreIndex = AIE_ARRAY_ROW_WIDTH * colNum + rowNum;

    coreBaseAddr = aie_array_base_address + (coreIndex * AIE_ARRAY_TILE_STRIDE);

    // South Tile
    uint32_t southCoreIndex = ((AIE_ARRAY_ROW_WIDTH * colNum) + (rowNum - 1));
    southBankBaseAddr = aie_array_base_address + (southCoreIndex * AIE_ARRAY_TILE_STRIDE);

    // West Tile
    uint32_t westCoreIndex = ((rowNum - 1) % 2) ? coreIndex : ((AIE_ARRAY_ROW_WIDTH * (colNum - 1)) + rowNum);
    westBankBaseAddr = aie_array_base_address + (westCoreIndex * AIE_ARRAY_TILE_STRIDE);

    // North Tile
    uint32_t northCoreIndex = ((AIE_ARRAY_ROW_WIDTH * colNum) + (rowNum + 1));
    northBankBaseAddr = aie_array_base_address + (northCoreIndex * AIE_ARRAY_TILE_STRIDE);

    // East Tile
    uint32_t eastCoreIndex = ((rowNum - 1) % 2) ? ((AIE_ARRAY_ROW_WIDTH * (colNum + 1)) + rowNum) : coreIndex;
    eastBankBaseAddr = aie_array_base_address + (eastCoreIndex * AIE_ARRAY_TILE_STRIDE);
}

/******************************************************************************/
Binary::Address_t VersalImageHeader::GetAieEngineGlobalAddress(Binary::Address_t elfAddr)
{
    Binary::Address_t globalAddr = elfAddr;
    if (globalAddr < 0x20000)
    {
        globalAddr += coreBaseAddr + 0x20000;
    }
    else if (globalAddr < 0x40000)
    {
        globalAddr -= 0x20000;
        if (globalAddr < 0x8000)
        {
            globalAddr += southBankBaseAddr;
        }
        else if (globalAddr < 0x10000)
        {
            globalAddr += westBankBaseAddr - 0x8000;
        }
        else if (globalAddr < 0x18000)
        {
            globalAddr += northBankBaseAddr - 0x10000;
        }
        else
        {
            globalAddr += eastBankBaseAddr - 0x18000;
        }
    }
    return globalAddr;
}

/******************************************************************************/
uint32_t VersalImageHeader::CheckAieEngineDataMemoryBoundary(Binary::Address_t globalAddr, Binary::Length_t pSize)
{
    Binary::Address_t startAddr = globalAddr;
    Binary::Address_t endAddr;
    uint64_t spillSize = 0;

    if (startAddr < 0x40000)
    {
        startAddr -= 0x20000;
        endAddr = startAddr + pSize - 1;

        if (((startAddr >= 0 && startAddr < 0x8000) && (endAddr >= 0 && endAddr < 0x8000)) ||
            ((startAddr >= 0x8000 && startAddr < 0x10000) && (endAddr >= 0x8000 && endAddr < 0x10000)) ||
            ((startAddr >= 0x10000 && startAddr < 0x18000) && (endAddr >= 0x10000 && endAddr < 0x18000)) ||
            ((startAddr >= 0x18000 && startAddr < 0x20000) && (endAddr >= 0x18000 && endAddr < 0x20000)))
        {
            // do nothing
        }
        else
        {
            if (startAddr < 0x8000)
            {
                spillSize = endAddr + 1 - 0x8000;
            }
            else if (startAddr < 0x10000)
            {
                spillSize = endAddr + 1 - 0x10000;
            }
            else if (startAddr < 0x18000)
            {
                spillSize = endAddr + 1 - 0x18000;
            }
            else
            {
                spillSize = endAddr + 1 - 0x20000;
            }
        }
    }
    return spillSize;
}

/******************************************************************************/
void VersalImageHeader::SetLoadAndExecAddress(PartitionHeader* partHdr)
{
    if (destCpu == DestinationCPU::AIE)
    {
        // Extract column and row numbers from elf file name (elfname_<colnum>_<rownum>.elf)
        // Example: test_2_4.elf -> AIE core with column-2 and row-4
        std::string temp = StringUtils::RemoveExtension(StringUtils::BaseName(Filename));
        uint32_t colNum = std::stoi(temp.substr((temp.length() - 3), 1));
        uint32_t rowNum = std::stoi(temp.substr((temp.length() - 1), 1));

        /* AIE Tile details in the form of [ColumnNum (16-bits) | RowNum(16-bits)]
        to be put into Exec Address Lo field in case of AIE partition */
        uint64_t aieTileDetails = (colNum << 16) | (rowNum);

        /* AIE Tile details in Exec address in case of AIE partitions
        Load address & execution address will be same for AIE elfs, so no need of separate exec addr field*/
        //partHdr->execAddress = GetAieEngineGlobalAddress((Binary::Address_t)elf.GetStartUpAddress());
        partHdr->execAddress = aieTileDetails;

        CalculateAieEngineBaseAddress(colNum, rowNum);
        partHdr->loadAddress = GetAieEngineGlobalAddress(0);
    }
}

/******************************************************************************/
std::list<std::string> VersalImageHeader::ParseAieJson(const char * filename)
{
    std::list<std::string> core_list;
    cJSON *Jfile = parse_file(filename);
    cJSON *JsonStr = cJSON_GetObjectItem(Jfile, "ActiveCores");
    int active_cores = cJSON_GetArraySize(JsonStr);
    for (int core_no = 0; core_no < active_cores; core_no++) {
        cJSON *core_array = cJSON_GetArrayItem(JsonStr, core_no);
        auto arrayStr = StringUtils::Split_jsonString((std::string) cJSON_Print(core_array));
        auto key = StringUtils::Trim_quotes(arrayStr.first);
        auto value = StringUtils::Trim_quotes(arrayStr.second);
        //auto path = "/Release/" + key;
        //path_full.push_back(value + path);
        core_list.push_back(key);
    }
    return core_list;
}

/******************************************************************************/
/* 
Total SLR boot partition:
+-------------------+
|    CDO Header     |              CDO header size
+-------------------+                     +
|                   |              WRITE KEYHOLE CDO command size
|     SLR1 PDI      |                     +
|                   |              SLR1 PDI size
+-------------------+                     +
|       SYNC        |              SYNC CDO command size
+-------------------+                     +
|                   |              WRITE KEYHOLE CDO command size
|     SLR2 PDI      |                     +
|                   |              SLR2 PDI size
+-------------------+                     +
|       SYNC        |              SYNC CDO command size
+-------------------+                     +
|                   |              WRITE KEYHOLE CDO command size
|     SLR3 PDI      |                     +
|                   |              SLR3 PDI size
+-------------------+                     +
|       SYNC        |              SYNC CDO command size
+-------------------+                     +
|    NoC Freq &     |
|     reconfig      |              NoC CDO size
|       CDO         |
+-------------------+                     +
|       SYNC        |              SYNC CDO command size
+-------------------+
*/

void VersalImageHeader::CreateSlrBootPartition(BootImage& bi)
{
    LOG_INFO("Creating SLR Boot CDO partition");

    if ((slrBootPdiInfo.size() == 1) && GetSlrType(slrBootPdiInfo.front()) == MASTER_CDO)
    {
        SetPartitionType(PartitionType::CONFIG_DATA_OBJ);
        auto partHdr = std::make_unique<VersalPartitionHeader>(this, 0);
        partHdr->execState = 0;
        partHdr->elfEndianess = 0;
        partHdr->firstValidIndex = true;
        uint8_t* cdo_buffer = NULL;
        size_t size = 0;
        const char* cdo_filename = slrBootPdiInfo.front()->file.c_str();
        CdoSequence * cdo_seq;
        #ifdef ENABLE_WDI
        if(isl::iostreams::xp::is_xp_format(cdo_filename))
        {
            cdo_seq = Decompressing_File(cdo_filename);
        }
        else
        {
            cdo_seq = cdoseq_load_cdo(cdo_filename);
        }
        #else
        cdo_seq = cdoseq_load_cdo(cdo_filename);
        #endif
        if (cdo_seq == NULL)
        {
            LOG_ERROR("Error parsing CDO file");
        }
        if (bi.overlayCDO && cdooverlay_apply(cdo_seq, (CdoOverlayInfo *)(bi.overlayCDO)))
        {
            LOG_ERROR("Error applying overlay CDO file : %s", cdo_filename);
        }
        cdo_buffer = (uint8_t*)cdoseq_to_binary(cdo_seq, &size, 0);
        partHdr->partitionSize = size;
        partHdr.get()->partition = std::make_unique<VersalPartition>(partHdr.get(), cdo_buffer, size);
        partitionHeaderList.push_back(partHdr.release());
        free(cdo_buffer);
        // Cleanup handled by unique_ptr
        cdoSections.clear();
    }
    else
    {
        uint32_t p_offset = 0;
        uint64_t size = 0;
        uint8_t slr_cnt = 0;

        /* Add CDO Header */
        cdoHeader = std::make_unique<VersalCdoHeader>();
        cdoHeader->remaining_words = CDO_REMAINING_WORDS;
        cdoHeader->id_word = CDO_IDENTIFICATION; /* CDO */
        cdoHeader->version = CDO_VERSION; /* Version - 2.0 */
        cdoHeader->length = 0;
        cdoHeader->checksum = 0;

        size += sizeof(VersalCdoHeader);
        std::vector<uint8_t> p_buffer(size);
        memcpy(p_buffer.data(), cdoHeader.get(), sizeof(VersalCdoHeader));
        p_offset += sizeof(VersalCdoHeader);


        slrBootPdiInfo.sort(SortByIndex);
        num_of_slrs = GetTotalSlrCount(slrBootPdiInfo);
        char * do_ssit_check = getenv("BOOTGEN_DO_SSIT_NOC_CHECK");
        bool master_cdo_verified = 0;
        std::string device_name = "";

        if (do_ssit_check != NULL)
        {
            switch (bi.bifOptions->idCode) {
            case 0x04d24093: /* h10 */
            case 0x14d24093:
                //device_name = "xcvp1202";
                break;
            case 0x04d08093: /* h20 */
            case 0x14d08093:
                device_name = "xcvp1502";
                break;
            case 0x04d2c093: /* h30 */
            case 0x14d2c093:
                device_name = "xcvh1782";
                break;
            case 0x04d10093: /* h40 */
            case 0x14d10093:
                device_name = "xcvp1702";
                break;
            case 0x04d14093: /* h50 */
            case 0x14d14093:
                device_name = "xcvp1802";
                break;
            case 0x14d2a093:
                device_name = "xcvh1522";
                break;
            case 0x14d29093:
                device_name = "xcvh1542";
                break;
            case 0x14d28093:
                device_name = "xcvh1582";
                break;
            case 0x14d2d093:
                device_name = "xcvh1742";
                break;
            case 0x14d34093:
                device_name = "xcvp1552";
                break;
            case 0x04c40093:
                device_name = "xcvp1902";
                break;
            case 0x04d1c093:
                device_name = "xcvp2502";
                break;
            case 0x14d20093:
                device_name = "xcvp2802";
                break;
            default:
                LOG_WARNING("Skipping NoC configuration verification for the SSIT device. Device IDCODE 0x%08x ", bi.bifOptions->idCode);
            }
        }

        /* Add CDO Write Keyhole commands */
        for (std::list<SlrPdiInfo*>::iterator slr_id = slrBootPdiInfo.begin(); slr_id != slrBootPdiInfo.end(); slr_id++)
        {
            uint32_t file_size = 0;
            uint32_t pad_size = 0;
            std::ifstream stream((*slr_id)->file.c_str(), std::ios_base::binary);
            if (!stream)
            {
                LOG_ERROR("Cannot read file - %s ", (*slr_id)->file.c_str());
            }
            
            if (GetSlrType(*slr_id) == SlrPdiType::BOOT)
            {
                slr_cnt++;
                ByteFile slr_boot_data((*slr_id)->file);
                file_size = slr_boot_data.len;
                pad_size = file_size + ((4 - (file_size & 3)) & 3);
                uint32_t bh_offset = 0;
                uint32_t ih_offset = ((VersalBootHeaderStructure *)(slr_boot_data.bytes))->imageHeaderByteOffset;
                
                if (ih_offset & 3) {
                    ih_offset = (ih_offset + 3) & ~3; 
                }
                
                /* Remove the 16-bytes of SMAP bus width from start of PDI */
                uint32_t smap_data = (slr_boot_data.bytes[0]) + (slr_boot_data.bytes[1] << 8) + (slr_boot_data.bytes[2] << 16) + (slr_boot_data.bytes[3] << 24);
                if ((smap_data == 0xDD000000) || (smap_data == 0x00DD0000) || (smap_data == 0x000000DD))
                {
                    bh_offset = (SMAP_BUS_WIDTH * 4);
                    file_size -= (SMAP_BUS_WIDTH * 4);
                    pad_size -= (SMAP_BUS_WIDTH * 4);
                    ih_offset -= (SMAP_BUS_WIDTH * 4);
                }

                uint32_t p1_size = ih_offset, p2_size = file_size - p1_size;
                //if (getenv("BOOTGEN_SPLIT_SSIT_SLAVE_BOOT_PDI") != NULL)
                {
                    pad_size = p1_size + ((4 - (p1_size & 3)) & 3);
                }

                if ((device_name != "") && (do_ssit_check != NULL))
                {
                    /* Get the partition offsets from the PDI */
                    uint32_t partition_offset = 0;
                    uint32_t partition_size = 0;
                    //VersalBootHeaderStructure* bH = (VersalBootHeaderStructure*)(slr_boot_data.bytes);
                    uint32_t *id_offset = (uint32_t *)(slr_boot_data.bytes + bh_offset + 0x4);
                    if (*id_offset == 0x584c4e58)
                    {
                        uint32_t *ih_byte_offset = (uint32_t *)(slr_boot_data.bytes + bh_offset + 0xb4);
                        VersalImageHeaderTableStructure* iHT = (VersalImageHeaderTableStructure*)(slr_boot_data.bytes + *ih_byte_offset);
                        size_t offset = iHT->firstPartitionHeaderWordOffset * 4;
                        offset += sizeof(VersalPartitionHeaderTableStructure);
                        for (uint8_t index = 1; index < iHT->partitionTotalCount; index++)
                        {
                            VersalPartitionHeaderTableStructure* pHT = (VersalPartitionHeaderTableStructure*)(slr_boot_data.bytes + offset);
                            if (((pHT->partitionAttributes >> vphtPartitionTypeShift) & vphtPartitionTypeMask) == PartitionType::CONFIG_DATA_OBJ)
                            {
                                partition_size = pHT->totalPartitionLength * 4;
                                partition_offset = pHT->partitionWordOffset * 4;
                                break;
                            }
                            offset += sizeof(VersalPartitionHeaderTableStructure);
                        }

                        CdoSequence* cdo_seq = decode_cdo_binary(slr_boot_data.bytes + partition_offset, partition_size);
                        if (cdo_seq == NULL) {
                            LOG_WARNING("Cannot decode binary cdo data for slr %d, skipping verification of NoC configuration", (*slr_id)->index);
                        }
                        else
                        {
                            CompareCDOSequences(cdo_seq, device_name, (*slr_id)->file, (*slr_id)->index);
                            //cdocmd_delete_sequence(cdo_seq);  // Free CDO sequence memory
                        }
                    }
                }

                /* For DMA alignment - add nop commands to align it to 128-bit (16-byte) */
                size_t pad_bytes = ((16 - ((p_offset + CDO_CMD_WRITE_KEYHOLE_SIZE) & 15)) & 15);
                size += (CDO_CMD_WRITE_KEYHOLE_SIZE + pad_size + pad_bytes);
                p_buffer.resize(size);

                if (pad_bytes != 0)
                {
                    auto cdoCmd = CdoCmdNoOperation(pad_bytes);
                    LOG_TRACE("NOP - 0x%x", p_offset);
                    memcpy(p_buffer.data() + p_offset, cdoCmd, CDO_CMD_NOP_SIZE);
                    p_offset += CDO_CMD_NOP_SIZE;
                    if (cdoCmd->header.length > 0)
                    {
                        memset(p_buffer.data() + p_offset, 0, cdoCmd->header.length * sizeof(uint32_t));
                        p_offset += (cdoCmd->header.length * sizeof(uint32_t));
                    }
                }

                /* Add Write Key Hole command with SLR Boot PDI data */
                auto cdoCmd = CdoCmdWriteKeyHole(pad_size, (*slr_id)->index);
                memcpy(p_buffer.data() + p_offset, cdoCmd, CDO_CMD_WRITE_KEYHOLE_SIZE);
                p_offset += CDO_CMD_WRITE_KEYHOLE_SIZE;

                //if (getenv("BOOTGEN_SPLIT_SSIT_SLAVE_BOOT_PDI") != NULL)
                {
                    memcpy(p_buffer.data() + p_offset, slr_boot_data.bytes + bh_offset, p1_size);
                    p_offset += p1_size;
                    if ((pad_size - p1_size) != 0)
                    {
                        memset(p_buffer.data() + p_offset, 0, pad_size - p1_size);
                        p_offset += pad_size - p1_size;
                    }
                
                    /* Add SSIT Sync Slave command */
                    size += sizeof(CdoSsitSlaves);
                    p_buffer.resize(size);
                    auto ssit_sync_slaves_cmd = CdoCmdWriteSsitSyncSlaves(1 << ((*slr_id)->index - 1) & 0xFF);
                    memcpy(p_buffer.data() + p_offset, ssit_sync_slaves_cmd, sizeof(CdoSsitSlaves));
                    p_offset += sizeof(CdoSsitSlaves);
                    /* Add part 2 write keyhole*/
                    pad_size = p2_size + ((4 - (p2_size & 3)) & 3);
                    pad_bytes = ((16 - ((p_offset + CDO_CMD_WRITE_KEYHOLE_SIZE) & 15)) & 15);
                    size += (CDO_CMD_WRITE_KEYHOLE_SIZE + pad_size + pad_bytes);
                    p_buffer.resize(size);
                    if (pad_bytes != 0)
                    {
                        auto cdoCmd = CdoCmdNoOperation(pad_bytes);
                        LOG_TRACE("NOP - 0x%x", p_offset);
                        memcpy(p_buffer.data() + p_offset, cdoCmd, CDO_CMD_NOP_SIZE);
                        p_offset += CDO_CMD_NOP_SIZE;
                        if (cdoCmd->header.length > 0)
                        {
                            memset(p_buffer.data() + p_offset, 0, cdoCmd->header.length * sizeof(uint32_t));
                            p_offset += (cdoCmd->header.length * sizeof(uint32_t));
                        }
                    }
                    auto cdoCmd2 = CdoCmdWriteKeyHole(pad_size, (*slr_id)->index);
                    memcpy(p_buffer.data() + p_offset, cdoCmd2, CDO_CMD_WRITE_KEYHOLE_SIZE);
                    p_offset += CDO_CMD_WRITE_KEYHOLE_SIZE;

                    if( (slr_boot_data.len - ih_offset - (SMAP_BUS_WIDTH * 4)) < p2_size)
                    {
                        memcpy(p_buffer.data() + p_offset, slr_boot_data.bytes + ih_offset, p2_size);
                    }
                    else
                    {
                        memcpy(p_buffer.data() + p_offset, slr_boot_data.bytes + ih_offset + (SMAP_BUS_WIDTH)*4, p2_size);
                    }
                    // memcpy(p_buffer.data() + p_offset, slr_boot_data.bytes + ih_offset + (SMAP_BUS_WIDTH * 4), p2_size);

                    p_offset += p2_size;
                    if ((pad_size - p2_size) != 0)
                    {
                        memset(p_buffer.data() + p_offset, 0, pad_size - p2_size);
                        p_offset += pad_size - p2_size;
                    }
                }
                /*else
                {
                    memcpy(p_buffer + p_offset, slr_boot_data.bytes + bh_offset, file_size);
                    p_offset += file_size;
                    if ((pad_size - file_size) != 0)
                    {
                        memset(p_buffer + p_offset, 0, pad_size - file_size);
                        p_offset += pad_size - file_size;
                    }
                }*/

                /* Add SSIT Wait Slave command */
                size += sizeof(CdoSsitSlaves);
                p_buffer.resize(size);
                auto ssit_wait_slaves_cmd = CdoCmdWriteSsitWaitSlaves(1 << ((*slr_id)->index - 1));
                memcpy(p_buffer.data() + p_offset, ssit_wait_slaves_cmd, sizeof(CdoSsitSlaves));
                p_offset += sizeof(CdoSsitSlaves);

                
                if (slr_cnt == num_of_slrs)
                {
                    /* Add SSIT Sync Slave command */
                    size += sizeof(CdoSsitSlaves);
                    p_buffer.resize(size);
                    auto ssit_sync_slaves_cmd = CdoCmdWriteSsitSyncSlaves(((1 << num_of_slrs) - 1) & 0xFF);
                    memcpy(p_buffer.data() + p_offset, ssit_sync_slaves_cmd, sizeof(CdoSsitSlaves));
                    p_offset += sizeof(CdoSsitSlaves);
                }
                if (bi.options.GetDumpOption() != DumpOption::SLAVE_PDIS)
                {
                    remove ((*slr_id)->file.c_str());
                }
            }
            else if (GetSlrType(*slr_id) == SlrPdiType::MASTER_CDO)
            {
                /* Add Master Boot NPI and NoC freq CDO commands by parsing the CDO file */
                const char* cdo_filename = (*slr_id)->file.c_str();
                CdoSequence* cdo_seq;
                #ifdef ENABLE_WDI
                if(isl::iostreams::xp::is_xp_format(cdo_filename))
                {
                    cdo_seq = Decompressing_File(cdo_filename);
                }
                else
                {
                    cdo_seq = cdoseq_load_cdo(cdo_filename);
                }
                #else
                cdo_seq = cdoseq_load_cdo(cdo_filename);
                #endif
                if (cdo_seq == NULL)
                {
                    LOG_ERROR("Error parsing CDO file : %s", cdo_filename);
                }

                if (bi.overlayCDO && cdooverlay_apply(cdo_seq, (CdoOverlayInfo *)(bi.overlayCDO)))
                {
                    LOG_ERROR("Error applying overlay CDO file : %s", cdo_filename);
                }
                if ((master_cdo_verified == 0) && (device_name != "") && (do_ssit_check != NULL))
                {
                    master_cdo_verified = 1;
                    CompareCDOSequences(cdo_seq, device_name, (*slr_id)->file.c_str(), (*slr_id)->index);
                    //cdocmd_delete_sequence(cdo_seq);  // Free CDO sequence memory
                    #ifdef ENABLE_WDI
                    if(isl::iostreams::xp::is_xp_format(cdo_filename))
                    {
                        cdo_seq = Decompressing_File(cdo_filename);
                    }
                    else
                    {
                        cdo_seq = cdoseq_load_cdo(cdo_filename);
                    }
                    #else
                    cdo_seq = cdoseq_load_cdo(cdo_filename);
                    #endif
                    if (cdo_seq == NULL)
                    {
                        LOG_ERROR("Error parsing CDO file : %s", cdo_filename);
                    }
                }
                size_t cdo_size = 0;
                uint8_t* cdo_buffer = (uint8_t*)cdoseq_to_binary(cdo_seq, &cdo_size, 0);
                cdocmd_delete_sequence(cdo_seq);  // Free CDO sequence memory
                file_size = cdo_size;

                if (file_size > sizeof(VersalCdoHeader))
                {
                    pad_size = file_size + ((4 - (file_size & 3)) & 3);
                    file_size -= sizeof(VersalCdoHeader);
                    pad_size -= sizeof(VersalCdoHeader);
                }
                else
                {
                    LOG_WARNING("Incorrect CDO length read from : %s", cdo_filename);
                }

                if (IsCdoCmdEndFound(cdo_buffer, file_size))
                {
                    file_size -= +sizeof(CdoCommandHeader);
                    pad_size -= +sizeof(CdoCommandHeader);
                }
                size += pad_size;
                p_buffer.resize(size);

                memcpy(p_buffer.data() + p_offset, cdo_buffer + sizeof(VersalCdoHeader), file_size);
                if ((pad_size - file_size) != 0)
                {
                    memset(p_buffer.data() + p_offset + file_size, 0, pad_size - file_size);
                }
                free(cdo_buffer);
                p_offset += pad_size;
                if ((*slr_id)->index == slrBootPdiInfo.size())
                {
                    /* Add SSIT Sync Slave command */
                    size += sizeof(CdoSsitSlaves);
                    p_buffer.resize(size);

                    /* Add SSIT Sync Slave command */
                    auto ssit_sync_slaves_cmd = CdoCmdWriteSsitSyncSlaves(((1 << num_of_slrs) - 1) & 0xFF);
                    memcpy(p_buffer.data() + p_offset, ssit_sync_slaves_cmd, sizeof(CdoSsitSlaves));
                    p_offset += sizeof(CdoSsitSlaves);
                }
            }
        }

        size += sizeof(CdoCommandHeader);
        p_buffer.resize(size);
        auto cmd_end = CdoCmdCdoEnd();
        memcpy(p_buffer.data() + p_offset, cmd_end, sizeof(CdoCommandHeader));

        /* Update CDO header lengths and checksum */
        cdoHeader->length = (size - sizeof(VersalCdoHeader)) / 4;
        cdoHeader->checksum = ~(cdoHeader->remaining_words + cdoHeader->id_word + cdoHeader->version + cdoHeader->length);
        memcpy(p_buffer.data(), cdoHeader.get(), sizeof(VersalCdoHeader));

        SetPartitionType(PartitionType::CONFIG_DATA_OBJ);
        auto partHdr = std::make_unique<VersalPartitionHeader>(this, 0);
        partHdr->execState = 0;
        partHdr->elfEndianess = 0;
        partHdr->firstValidIndex = true;
        partHdr->partitionSize = size;
        partHdr.get()->partition = std::make_unique<VersalPartition>(partHdr.get(), p_buffer.data(), size);
        partitionHeaderList.push_back(partHdr.release());
        // Cleanup handled by unique_ptr
        cdoSections.clear();
    }
}

/******************************************************************************/
size_t GetActualChunkSize(SsitConfigSlrInfo* slr_info, BootImage* bi = nullptr)
{
    size_t num_bytes = 0;
    static bool chunk_size_info_printed = false;
    /* Default chunk size - 32KB */
    uint64_t chunk_size = SSIT_CHUNK_SIZE;
    char * ssit_chunk_size = getenv("BOOTGEN_SSIT_CHUNK_SIZE");
    if (ssit_chunk_size != NULL)
    {
        chunk_size = strtoull(ssit_chunk_size, NULL, 16);
    } 
    if (bi != NULL)
    {
        // Check for key rolling configuration (not just encryption)
        for (std::list<ImageHeader*>::iterator imageItr = bi->imageList.begin(); imageItr != bi->imageList.end(); imageItr++)
        {
            // Check if this ImageHeader has both encryption AND key rolling enabled
            if ((*imageItr)->GetEncryptContext() != NULL && (*imageItr)->GetEncryptContext()->Type() == Encryption::AES)
            {
                std::vector<uint32_t>& encrBlocks = (*imageItr)->GetEncrBlocksList();
                uint32_t defEncrBlockSize = (*imageItr)->GetDefaultEncrBlockSize();
                
                // Only apply if key rolling is actually configured (not just encryption enabled)
                if (defEncrBlockSize > 0)
                {
                    chunk_size = defEncrBlockSize * 1024; // Convert KB to bytes
                    LOG_TRACE("Key rolling detected: Using configured block size %d KB for SSIT chunking", defEncrBlockSize);
                    break;
                }
                else if (encrBlocks.size() > 0 && encrBlocks[0] > 0 && (encrBlocks[0] % 16) == 0)
                {
                    chunk_size = encrBlocks[0] * 1024; // Convert KB to bytes
                    LOG_TRACE("Key rolling detected: Using explicit block size %d KB for SSIT chunking", encrBlocks[0]);
                    break;
                }
                // If encryption is enabled but no key rolling config found, keep default chunk size
            }
        }
    }
    
    if (!chunk_size_info_printed)
    {
        LOG_INFO("SSIT chunk size = 0x%x", chunk_size);
        chunk_size_info_printed = true;
    }
	
 	for (std::vector<SsitConfigPartitionSecurityInfo*>::iterator slr_security_info = slr_info->security_info.begin(); slr_security_info != slr_info->security_info.end(); slr_security_info++)
	{
		if ((*slr_security_info)->partition_index == slr_info->partition_index && (*slr_security_info)->top_chunk_processed == false)
		{
			if ((*slr_security_info)->checksum != Checksum::None)
			{
				num_bytes = SHA3_LENGTH_BYTES;
				slr_info->partition_offset += num_bytes;
				(*slr_security_info)->top_chunk_processed = true;
				return num_bytes;
			}
			else if (((*slr_security_info)->encryption != Encryption::None) && ((*slr_security_info)->authentication != Authentication::None))
			{
				num_bytes = sizeof(AuthCertificate4096Sha3PaddingStructure); /* Auth Certificate size is the same for all Algorithms in case of Versal */
				num_bytes += (SECURE_HDR_SZ + AES_GCM_TAG_SZ);
				slr_info->partition_offset += num_bytes;
				(*slr_security_info)->top_chunk_processed = true;
				return num_bytes;
			}
			else if ((*slr_security_info)->authentication != Authentication::None)
			{
				num_bytes = sizeof(AuthCertificate4096Sha3PaddingStructure); /* Auth Certificate size is the same for all Algorithms in case of Versal */
				slr_info->partition_offset += num_bytes;
				(*slr_security_info)->top_chunk_processed = true;
				return num_bytes;
			}
			else if ((*slr_security_info)->encryption != Encryption::None)
			{
				num_bytes = (SECURE_HDR_SZ + AES_GCM_TAG_SZ);
				slr_info->partition_offset += num_bytes;
				(*slr_security_info)->top_chunk_processed = true;
				return num_bytes;
			}
		}
	}

    /* Create chunks such that each partition within a SLR PDI starts as a new chunk */
    size_t partition_size = slr_info->partition_sizes.at(slr_info->partition_index);

    if((partition_size - slr_info->partition_offset) <= chunk_size)
    {
        /* If remaining bytes in partition are less than chunk size, create a chunk with remaining bytes */
        num_bytes = partition_size - slr_info->partition_offset;
        if (slr_info->partition_index < (slr_info->partition_sizes.size() - 1))
        {
            slr_info->partition_index++;
            slr_info->partition_offset = 0;
        }
        else
        {
            slr_info->partition_offset += num_bytes;
        }
    }
    else
    {
        /* If partition size is greater than chunk size, create a chunk with chunk size */
        num_bytes = chunk_size;
        slr_info->partition_offset += num_bytes;
    }

    return num_bytes;
}

/******************************************************************************/
void VersalImageHeader::LogConfigSlrDetails(size_t chunk_num, uint8_t slr_num, size_t offset, size_t chunk_size, size_t sync_points)
{
    auto log_details = std::make_unique<SsitConfigSlrLog>();
    log_details->slr_num = slr_num;
    log_details->offset = offset;
    log_details->size = chunk_size;
    log_details->sync_points = sync_points;
    configSlrLog.push_back(log_details.release());
}

/******************************************************************************/
void VersalImageHeader::PrintConfigSlrSummary(void)
{
    LOG_TRACE("Note: Offset in below table is from the start of the config CDO partition and not start of PDI");
    LOG_TRACE("---------------------------------------------------------------");
    LOG_TRACE("                        SSIT Summary                           ");
    LOG_TRACE("---------------------------------------------------------------");
    LOG_TRACE(" Chunks   SLR     Offset        Size    Cumulative Sync Points");
    LOG_TRACE("---------------------------------------------------------------");
    uint32_t index = 1;
    for (std::vector<SsitConfigSlrLog*>::iterator slr_info = configSlrLog.begin(); slr_info != configSlrLog.end(); slr_info++)
    {
        std::string slr = ((*slr_info)->slr_num == 4) ? "master" : ("slr-" + std::to_string((*slr_info)->slr_num));
        LOG_TRACE("%4d  %8s    0x%-8x    0x%-8x    %3d", index++, slr.c_str(), (*slr_info)->offset, (*slr_info)->size, (*slr_info)->sync_points);
    }
    LOG_TRACE("---------------------------------------------------------------");
}

/******************************************************************************/
/* SLR Slave config partition CDO with round robin chunking
        +--------------+--------------+--------------+--------------+
        |     SLR1     |     SLR2     |     SLR3     |  Master SLR  |
        +--------------+--------------+--------------+--------------+
        |       1      |     2 (S)    |       3      |              |
        +--------------+--------------+--------------+--------------+
        |   4 (S) (S)  |              |       5      |              |
        +--------------+--------------+--------------+--------------+
        |              |              |       6      |              |
        +--------------+--------------+--------------+--------------+
        |              |              |       7      |              |
        +--------------+--------------+--------------+--------------+
        |              |              |     8 (S)    |     9 (S)    |
        +--------------+--------------+--------------+--------------+
        |              |  10 (S) (S)  |      11      |              |
        +--------------+--------------+--------------+--------------+
        |              |      -       |      12      |              |
        +--------------+--------------+--------------+--------------+
        |              |      -       |      13      |              |
        +--------------+--------------+--------------+--------------+
        |              |      -       |     14 (S)   |     15 (S)   |
        +--------------+--------------+--------------+--------------+
*/

void VersalImageHeader::CreateSlrConfigPartition(BootImage& bi)
{
    size_t size = 0;
    size_t p_offset = 0;
    size_t common_sync_point = 0, current_sync_point = 0, last_sync_point = 0;
    size_t total_slr_chunk_size = 0;
    size_t master_file_size = 0;
    size_t slr_total_file_size = 0;
    uint32_t chunk_num = 1;
    bool master_slr_available = false;
    LOG_INFO("Creating SLR Config CDO partition");

    /* CDO Header */
    cdoHeader = std::make_unique<VersalCdoHeader>();
    cdoHeader->remaining_words = CDO_REMAINING_WORDS;
    cdoHeader->id_word = CDO_IDENTIFICATION; /* CDO */
    cdoHeader->version = CDO_VERSION; /* Version - 2.0 */

    /* Populate length and checksum later, as we can't estimate the exact size of CDO */
    cdoHeader->length = 0;
    cdoHeader->checksum = 0;
    size += sizeof(VersalCdoHeader);
    std::vector<uint8_t> p_buffer(size);
    memcpy(p_buffer.data(), cdoHeader.get(), sizeof(VersalCdoHeader));
    p_offset += sizeof(VersalCdoHeader);

    /* Initialize the individual SLR config structures */
    slrConfigPdiInfo.sort(SortByIndex);
    for (std::list<SlrPdiInfo*>::iterator slr_id = slrConfigPdiInfo.begin(); slr_id != slrConfigPdiInfo.end(); slr_id++)
    {
        auto configSlr = std::make_unique<SsitConfigSlrInfo>();
        configSlr->file = (*slr_id)->file;
        configSlr->index = (*slr_id)->index;
        configSlr->offset = 0;
        configSlr->size = 0;
        configSlr->sync_points = 0;
        configSlr->data = NULL;
        configSlr->partition_index = 0;
        configSlr->partition_offset = 0;
        configSlr->num_chunks = 0;
        configSlr->eof = false;
        
        // Check BEFORE release to avoid use-after-move
        if (configSlr->index == SlrId::MASTER)
        {
            master_slr_available = true;
        }
        
        configSlrsInfo.push_back(configSlr.release());
    }

    /* Parse slave SLR config files to get SLR data, identify sync points, file sizes and get total file size */
    ParseSlrConfigFiles(&slr_total_file_size, &bi);

    /* 1. Create DMA write keyhole command for chunk from each slave SLR in a round robin fashion.
       2. Continue the above process till a sync point is found in the current chunk.
       3. Once a sync point is reached for a slave SLRx, don't include this slave SLRx chunks in the round robin.
       4. Continue the above steps for remaining slave SLRs.
       5. Once all the slave SLRs reach a common sync point, create a master SLR chunk.
       6. Master SLR chunk is data between two sync points (CDO_SSIT_SYNC_SLAVES_CMD).
       7. Repeat the above steps, till all the sync points are processed or EOF for each slave SLR PDI.
    */
    uint8_t master_index = 0;
    while (total_slr_chunk_size < slr_total_file_size)
    {
        for (std::vector<SsitConfigSlrInfo*>::iterator slr_info = configSlrsInfo.begin(); slr_info != configSlrsInfo.end(); slr_info++)
        {
            /* Process slave SLR data only if it has not yet reached the common sync point */
            if (((*slr_info)->sync_points == common_sync_point) && ((*slr_info)->index != SlrId::MASTER) && (last_sync_point == current_sync_point))
            {
                /* Slave SLRs processing */
                size_t chunk_size = GetActualChunkSize(*slr_info, &bi);

                if(chunk_size != 0)
                {
                    (*slr_info)->num_chunks++;
                    /* For DMA alignment - add nop commands to align it to 128-bit (16-byte) */
                    size_t pad_bytes = ((16 - ((p_offset + CDO_CMD_WRITE_KEYHOLE_SIZE) & 15)) & 15);
                    size += (chunk_size + CDO_CMD_WRITE_KEYHOLE_SIZE + pad_bytes);
                    p_buffer.resize(size);

                    if (pad_bytes != 0)
                    {
                        auto cdoCmd = CdoCmdNoOperation(pad_bytes);
                        memcpy(p_buffer.data() + p_offset, cdoCmd, CDO_CMD_NOP_SIZE);
                        p_offset += CDO_CMD_NOP_SIZE;
                        if (cdoCmd->header.length > 0)
                        {
                            memset(p_buffer.data() + p_offset, 0, cdoCmd->header.length * sizeof(uint32_t));
                            p_offset += (cdoCmd->header.length * sizeof(uint32_t));
                        }
                    }

                    LOG_TRACE("Chunk-%d | SLR-%d", chunk_num, (*slr_info)->index);
                    /* Check for sync points in the current chunk */
                    CheckSyncPointInChunk(*slr_info, chunk_size);
                    LogConfigSlrDetails(chunk_num++, (((*slr_info)->index == 4) ? 0 : (*slr_info)->index), p_offset, chunk_size, (*slr_info)->sync_points);

                    /* Add write keyhole command for slave SLRs and master config */
                    auto cdoCmd = CdoCmdWriteKeyHole(chunk_size, (*slr_info)->index);
                    memcpy(p_buffer.data() + p_offset, cdoCmd, CDO_CMD_WRITE_KEYHOLE_SIZE);
                    p_offset += CDO_CMD_WRITE_KEYHOLE_SIZE;
                    memcpy(p_buffer.data() + p_offset, (*slr_info)->data + (*slr_info)->offset, chunk_size);

                    p_offset += chunk_size;
                    total_slr_chunk_size += chunk_size;
                    (*slr_info)->offset += chunk_size;
                }
                else
                {
                    /* If EOF reached, don't consider this SLR for finding common sync points */
                    (*slr_info)->eof = true;
                }
            }

            /* Get the common sync point - sync point which all the slave SLRs have serviced */
            current_sync_point = FindCurrentSyncPoint();
            
            if ((last_sync_point != current_sync_point))
            {
                if (master_slr_available)
                {
                    /* Do not process any other SLR data, until the master data is processed */
                    if (((*slr_info)->index == SlrId::MASTER) && (master_file_size < (*slr_info)->size))
                    {
                        size_t master_chunk_size = 0;

                        /* Chunk size in master is different from the chunk size of slave SLR chunk sizes
                           Master SLRs chunk size is data size between two sync points (CDO_SSIT_SYNC_SLAVES_CMD )in Master SLR CDO */
                        (*slr_info)->num_chunks++;

                        const char* cdo_filename = (*slr_info)->file.c_str();
                        CdoSequence * master_cdo_seq;
                        #ifdef ENABLE_WDI
                        if(isl::iostreams::xp::is_xp_format(cdo_filename))
                        {
                            master_cdo_seq = Decompressing_File(cdo_filename);
                        }
                        else
                        {
                            master_cdo_seq = cdoseq_load_cdo(cdo_filename);
                        }
                        #else
                        master_cdo_seq = cdoseq_load_cdo(cdo_filename);
                        #endif
                        CheckIdsInCdo(master_cdo_seq, bi.options.IsVersalNetSeries(), cdo_filename);
                        if (master_cdo_seq == NULL)
                        {
                            LOG_ERROR("Error parsing CDO file");
                        }
                        if (bi.overlayCDO && cdooverlay_apply(master_cdo_seq, (CdoOverlayInfo *)(bi.overlayCDO)))
                        {
                            LOG_ERROR("Error applying overlay CDO file : %s", cdo_filename);
                        }
                        size_t part_size = 0;

                        /* Pass the complete CDO sequence and SYNC point index */
                        /* If SYNC point 1 is passed, it will extract the data between start & 1st SSIT_SYNC_SLAVES command in RCDO
                           If SYNC point 2 is passed, it will extract the data between 1st & 2nd SSIT_SYNC_SLAVES command in RCDO */
                        CdoSequence* part_seq = cdoseq_extract_cdo_till_ssit_sync_slaves(master_cdo_seq, master_index + 1);
                        uint8_t* part_data = (uint8_t*)cdoseq_to_binary(part_seq, &part_size, 0);

                        size_t pad_bytes = 0;
                        master_chunk_size = part_size;
                        pad_bytes = ((16 - (p_offset & 15)) & 15);

                        size += (master_chunk_size+ pad_bytes);
                        p_buffer.resize(size);

                        /* For master SLR, just copy the CDO contents to chunk. No need to create seperate DMA commands like slave SLRs */
                        if (pad_bytes != 0)
                        {
                            auto cdoCmd = CdoCmdNoOperation(pad_bytes);
                            memcpy(p_buffer.data() + p_offset, cdoCmd, CDO_CMD_NOP_SIZE);
                            p_offset += CDO_CMD_NOP_SIZE;
                            if (cdoCmd->header.length > 0)
                            {
                                memset(p_buffer.data() + p_offset, 0, cdoCmd->header.length * sizeof(uint32_t));
                                p_offset += (cdoCmd->header.length * sizeof(uint32_t));
                            }
                        }
                        LOG_TRACE("Chunk-%d | SLR-master", chunk_num);
                        if(master_index < (*slr_info)->sync_addresses.size())
                        {
                            master_index++;
                        }
                        LogConfigSlrDetails(chunk_num++, SlrId::MASTER, p_offset, master_chunk_size, master_index);

                        /* As we strip the CDO header, we are replacing that with NOP commands to ensure other alignments are not disturbed for only the first chunk */
                        auto cdoCmd = CdoCmdNoOperation(sizeof(VersalCdoHeader));
                        memcpy(p_buffer.data() + p_offset, cdoCmd, CDO_CMD_NOP_SIZE);
                        memset(p_buffer.data() + p_offset + CDO_CMD_NOP_SIZE, 0, sizeof(VersalCdoHeader) - CDO_CMD_NOP_SIZE);
                        if (part_size > sizeof(VersalCdoHeader))
                        {
                            memcpy(p_buffer.data() + p_offset + sizeof(VersalCdoHeader), part_data + sizeof(VersalCdoHeader), part_size - sizeof(VersalCdoHeader));
                        }
                        else
                        {
                            LOG_WARNING("Incorrect CDO length read from : %s", cdo_filename);
                        }
                        p_offset += (master_chunk_size);
                        if (bi.options.GetSyncFlag())
                        {
                            /* Add SSIT Sync Slave command */
                            size += sizeof(CdoSsitSlaves);
                            p_buffer.resize(size);
                            /* Add SSIT Sync Slave command */
                            CdoSsitSlaves *ssit_sync_slaves_cmd = CdoCmdWriteSsitSyncSlaves(((1 << num_of_slrs) - 1) & 0xFF);
                            memcpy(p_buffer.data() + p_offset, ssit_sync_slaves_cmd, sizeof(CdoSsitSlaves));
                            p_offset += sizeof(CdoSsitSlaves);
                            delete ssit_sync_slaves_cmd;
                        }

                        (*slr_info)->offset += (master_chunk_size);
                        master_file_size += master_chunk_size;
                        total_slr_chunk_size += master_chunk_size;
                        last_sync_point++;
                        if (current_sync_point == 0xFFFFFFFF)
                        {
                            break;
                        }
                    }
                }
                else
                {
                     last_sync_point++;
                }
            }
        }
        common_sync_point = current_sync_point;
        if (current_sync_point == 0xFFFFFFFF)
        {
            break;
        }
    }

    size += sizeof(CdoCommandHeader);
    p_buffer.resize(size);
    auto cmd_end = CdoCmdCdoEnd();
    memcpy(p_buffer.data() + p_offset, cmd_end, sizeof(CdoCommandHeader));

    /* Update CDO header lengths and checksum */
    cdoHeader->length = (size - sizeof(VersalCdoHeader)) / 4;
    cdoHeader->checksum = ~(cdoHeader->remaining_words + cdoHeader->id_word + cdoHeader->version + cdoHeader->length);
    memcpy(p_buffer.data(), cdoHeader.get(), sizeof(VersalCdoHeader));

    SetPartitionType(PartitionType::CONFIG_DATA_OBJ);
    auto partHdr = std::make_unique<VersalPartitionHeader>(this, 0);
    partHdr->execState = 0;
    partHdr->elfEndianess = 0;
    partHdr->firstValidIndex = true;
    partHdr->partitionSize = size;
    partHdr.get()->partition = std::make_unique<VersalPartition>(partHdr.get(), p_buffer.data(), size);
    partitionHeaderList.push_back(partHdr.release());

    for (std::vector<SsitConfigSlrInfo*>::iterator slr_info = configSlrsInfo.begin(); slr_info != configSlrsInfo.end(); slr_info++)
    {
        if ((*slr_info)->index != SlrId::MASTER)
        {
            LOG_TRACE("SSIT: total slr-%d chunks  = %d", (*slr_info)->index, (*slr_info)->num_chunks);
            if (bi.options.GetDumpOption() != DumpOption::SLAVE_PDIS)
            {
                remove ((*slr_info)->file.c_str());
            }
        }
        else
        {
            LOG_TRACE("SSIT: total master chunks = %d", (*slr_info)->num_chunks);
        }
    }

    PrintConfigSlrSummary();
}

/******************************************************************************/
void GetPartitionOffsets(SsitConfigSlrInfo* slr_info, uint8_t* data, size_t size,  BootImage* bi = nullptr)
{
    uint8_t num_of_sync_points = 0;
    bool info_display = true;
    uint8_t max_partition_offsets = 8;
    std::vector<uint32_t> partitionhdr_offsets;
    VersalImageHeaderTableStructure* iHT = (VersalImageHeaderTableStructure*)data;
    size_t offset = iHT->firstPartitionHeaderWordOffset * 4;
    slr_info->partition_sizes.push_back(sizeof(VersalImageHeaderTableStructure) + (iHT->optionalDataSize * 4) + (iHT->totalMetaHdrLength * 4));
    if (bi != nullptr && bi->options.GetSyncFlag())
    {
        LOG_TRACE("SSIT: Sync flag is set");
        iHT->imageHeaderTableAttributes |= (0x1 & vNetihtEndofPDISynckMask) << vNetihtEndofPDISyncShift;
    }
    for (uint8_t index = 0; index < iHT->partitionTotalCount; index++)
    {
        VersalPartitionHeaderTableStructure* pHT = (VersalPartitionHeaderTableStructure*)(data + offset);
        
	if (iHT->metaHdrKeySource == KeySource::None)    //check to assign the PHT data to temp variables
    {
    partitionhdr_offsets.assign(max_partition_offsets,0);
        partitionhdr_offsets[0] = pHT->checksumWordOffset;
        partitionhdr_offsets[1] = pHT->totalPartitionLength;
        partitionhdr_offsets[2] = pHT->authCertificateOffset;
        partitionhdr_offsets[3] = pHT->partitionKeySource;
        partitionhdr_offsets[4] = pHT->partitionAttributes;
        partitionhdr_offsets[5] = pHT->puid;
        partitionhdr_offsets[6] = pHT->unencryptedPartitionLength;
        partitionhdr_offsets[7] = pHT->partitionWordOffset;
    }
    if (iHT->metaHdrKeySource != KeySource::None)
    {
        std::string SlaveSLRmetaEncrypt = StringUtils::RemoveExtension(slr_info->file) + "_" + "metahdrencrypt_offsets.txt";
        std::ifstream outFile(SlaveSLRmetaEncrypt.c_str());

        if (!outFile)
        {
            LOG_ERROR("Error in opening a file %s", SlaveSLRmetaEncrypt.c_str());
        }
        while (outFile)
        {
            std::string word;
            outFile >> word;

            // If file vacant
            if (word == "")
            {
                return;
            }
            if (word == "partition" + std::to_string(index))
            {
                int wordCount = 0;
                while (outFile >> word && wordCount < max_partition_offsets)
                {
                    uint32_t offset = std::stoi(word);
                    partitionhdr_offsets.push_back(offset);
                    ++wordCount;
                }
                break;
            }
        }
        outFile.close();
    }

        /* TotalPartitionLength includes AC + partition + hashes if authenticated.*/
        if (partitionhdr_offsets[0] != 0)
        {
            slr_info->partition_sizes.push_back((partitionhdr_offsets[1] * 4) + SHA3_LENGTH_BYTES);
        }
        else
        {
            slr_info->partition_sizes.push_back(partitionhdr_offsets[1] * 4);
        }
 
        auto partition_security_info = std::make_unique<SsitConfigPartitionSecurityInfo>();
        partition_security_info->partition_index = index + 1;
        partition_security_info->checksum = Checksum::None;
        partition_security_info->authentication = Authentication::None;
        partition_security_info->encryption = Encryption::None;
        partition_security_info->top_chunk_processed = false;
        if (partitionhdr_offsets[0] != 0)
        {
            partition_security_info->checksum = Checksum::SHA3;
        }
        else if (partitionhdr_offsets[2] != 0)
        {
            partition_security_info->authentication = Authentication::RSA;
        }
        if (partitionhdr_offsets[3] != KeySource::None)
        {
            partition_security_info->encryption = Encryption::AES;
        }
        slr_info->security_info.push_back(partition_security_info.release());
        
        offset += sizeof(VersalPartitionHeaderTableStructure);

        if (((partitionhdr_offsets[4] >> vphtPartitionTypeShift) & vphtPartitionTypeMask) == PartitionType::CONFIG_DATA_OBJ)
        {
            //if (index == (iHT->partitionTotalCount - 1))
            {
                //LOG_TRACE("Reading SLR Config CDO Sync Addresses - %s", slr_info->file.c_str());

                std::string sync_addresses_filename = StringUtils::RemoveExtension(slr_info->file) + 
                     "_" + std::to_string(partitionhdr_offsets[5]) + "_sync_offsets.txt";
                std::ifstream offsetFile(sync_addresses_filename.c_str());
                std::vector<uint32_t> syncpt_offsets;
                if (!offsetFile)
                {
                    //LOG_TRACE("SLR Config CDO Sync Addresses not found for - %s", slr_info->file.c_str());
                    offsetFile.close();
                }
                else
                {
                    LOG_TRACE("Reading SLR Config CDO Sync Addresses from - %s", sync_addresses_filename.c_str());
                }

                while (offsetFile)
                {
                    std::string word;
                    offsetFile >> word;
                    // If file vacant
                    if (word == "")
                    {
                        return;
                    }
                    if (word == "sync_offsets")
                    {
                        while (offsetFile)
                        {
                            offsetFile >> word;
                            uint32_t offset = std::stoi(word);
                            syncpt_offsets.push_back(offset);
                        }
                    }
                }
                if(syncpt_offsets.size() != 0)
                    syncpt_offsets.pop_back();

            num_of_sync_points = syncpt_offsets.size();
            
            //remove(sync_addresses_filename.c_str());
            
            int num_secure_chunks = 0;
            uint64_t data_chunk = SECURE_32K_CHUNK;
            size_t offset_shift = 0;
             
                // Auto-detect encryption block size ONLY if this SLR has both encryption AND key rolling enabled
                if (bi != NULL && partitionhdr_offsets[3] != KeySource::None)
                {
                    // Check if this specific SLR has key rolling enabled by looking at its encryption configuration
                    bool key_rolling_found = false;
                    for (std::list<ImageHeader*>::iterator imageItr = bi->imageList.begin(); imageItr != bi->imageList.end(); imageItr++)
                    {
                        if ((*imageItr)->GetEncryptContext() != NULL && (*imageItr)->GetEncryptContext()->Type() == Encryption::AES)
                        {
                            std::vector<uint32_t>& encrBlocks = (*imageItr)->GetEncrBlocksList();
                            uint32_t defEncrBlockSize = (*imageItr)->GetDefaultEncrBlockSize();
                            
                            // Only change chunk size if key rolling is actually configured
                            if (defEncrBlockSize > 0)
                            {
                                data_chunk = defEncrBlockSize * 1024; // Convert KB to bytes
                                LOG_TRACE("SLR-%d: Key rolling detected, using configured block size %d KB for offset calculation", slr_info->index, defEncrBlockSize);
                                key_rolling_found = true;
                                break;
                            }
                            else if (encrBlocks.size() > 0 && encrBlocks[0] > 0 && (encrBlocks[0] % 16) == 0)
                            {
                                data_chunk = encrBlocks[0] * 1024; // Convert KB to bytes
                                LOG_TRACE("SLR-%d: Key rolling detected, using explicit block size %d KB for offset calculation", slr_info->index, encrBlocks[0]);
                                key_rolling_found = true;
                                break;
                            }
                        }
                    }
                    
                    // If encryption enabled but no key rolling configured, explicitly keep default 32KB
                    if (!key_rolling_found)
                    {
                        LOG_TRACE("SLR-%d: Encryption enabled but no key rolling configured, using default 32KB chunks", slr_info->index);
                        // data_chunk already defaults to SECURE_32K_CHUNK, so no change needed
                    }
                }
                else
                {
                    // Check SSIT chunk size environment variable
                    char * ssit_chunk_size = getenv("BOOTGEN_SSIT_CHUNK_SIZE");
                    if (ssit_chunk_size != NULL)
                    {
                        data_chunk = strtoull(ssit_chunk_size, NULL, 16);
                    }
                }
            
            uint32_t partition_length = partitionhdr_offsets[6] * 4;
            uint32_t padLength = (partition_length % 16 != 0) ? 16 - (partition_length % 16) : 0;
            partition_length += padLength;

                if (partitionhdr_offsets[2] != 0 || partitionhdr_offsets[0] != 0)
                {
                    data_chunk -= SHA3_LENGTH_BYTES;
                    offset_shift += SHA3_LENGTH_BYTES;
                }
                if (partitionhdr_offsets[3] != KeySource::None)
                {
                    data_chunk -= (SECURE_HDR_SZ + AES_GCM_TAG_SZ);
                    offset_shift += SECURE_HDR_SZ + AES_GCM_TAG_SZ;
                }
                
                if (partition_length <= data_chunk)
                {
                    // no chunking
				}
                else
                {
                    num_secure_chunks = partition_length / data_chunk;
                    if (partition_length % data_chunk != 0)
                    {
                        num_secure_chunks++;
                    }
                }
                
                if (num_of_sync_points > 0)
                {
                    if (info_display)
                    {
                        LOG_TRACE("SSIT_SYNC_MASTER command detected at following offsets:\n           Count : 0x%X", num_of_sync_points);
                        LOG_TRACE("   slr_%d:", slr_info->index);
                        info_display = false;
                    }

                    /* Closed-form mapping: plaintext sync offset -> encrypted-stream byte offset
                       within the partition, then add the partition's data offset in the PDI.

                       Encrypted layout produced by ChunkifyAndEncrypt is:
                         [InitialSecHdr (SECURE_HDR_SZ + AES_GCM_TAG_SZ) bytes]
                         [Block_0 (data_chunk plaintext) + (SECURE_HDR_SZ + AES_GCM_TAG_SZ)]
                         [Block_1 (data_chunk plaintext) + (SECURE_HDR_SZ + AES_GCM_TAG_SZ)]
                         ...
                       For SHA3-checksummed/authenticated partitions, each block additionally
                       carries SHA3_LENGTH_BYTES of per-block overhead, which is already
                       accumulated into offset_shift above.

                       For plaintext byte P with block index b = floor(P / data_chunk):
                         enc_off = P + initial_sec_hdr + b * offset_shift
                       where initial_sec_hdr is the front-loaded (SECURE_HDR_SZ + AES_GCM_TAG_SZ)
                       only when the partition is encrypted.

                       Using a closed form (instead of an accumulator loop) avoids the prior
                       off-by-64 (missing initial secure header) and the latent
                       compare-shifted-vs-original-threshold drift. */
                    size_t initial_sec_hdr = 0;
                    if (partitionhdr_offsets[3] != KeySource::None)
                    {
                        initial_sec_hdr = SECURE_HDR_SZ + AES_GCM_TAG_SZ;
                    }

                    /* Recompute the plaintext block size used by the encryption
                       pipeline for this partition. Cannot trust the upstream
                       auto-detect (lines ~4244-4267) here because it treats
                       encrBlocks[0] as KB and multiplies by 1024 — but for SLR
                       config CDO partitions encrBlocks is populated in BYTES by
                       the encryption setup, leading to an artificially huge
                       data_chunk that makes block_idx always 0 and silently
                       drops the per-block shift.

                       For non-bootloader partitions the encryption pipeline
                       uses GetSecureChunkSize() (= SECURE_32K_CHUNK by default)
                       minus the per-block overhead. offset_shift already holds
                       that overhead (SECURE_HDR_SZ+AES_GCM_TAG_SZ, plus
                       SHA3_LENGTH_BYTES if authenticated/checksummed), so the
                       correct plaintext block size is simply:
                           SECURE_32K_CHUNK - offset_shift                     */
                    size_t plaintext_block = (offset_shift < SECURE_32K_CHUNK)
                                           ? (SECURE_32K_CHUNK - offset_shift)
                                           : 0;

                    for (int i = 0; i < num_of_sync_points; i++)
                    {
                        size_t plaintext_off = syncpt_offsets[i];
                        if(plaintext_block == 0)
                        {
                            LOG_WARNING("Encrypted partition for slr_%d has offset_shift (0x%zx) >= SECURE_32K_CHUNK (0x%x). "
                                        "Sync point offset adjustment will be skipped.", slr_info->index, offset_shift, SECURE_32K_CHUNK);
                        }
                        size_t block_idx = (plaintext_block == 0) ? 0 : (plaintext_off / plaintext_block);
                        size_t enc_off = plaintext_off
                                       + initial_sec_hdr
                                       + (block_idx * offset_shift);

                        enc_off += (partitionhdr_offsets[7] * 4);
                        slr_info->sync_addresses.push_back(enc_off);
                        LOG_TRACE("       offset = 0x%zx", enc_off);
                    }
                }
        }
        if (partitionhdr_offsets.size() != 0)
        {
            partitionhdr_offsets.clear();
        }
    }
}
}

/******************************************************************************/
void VersalImageHeader::ParseSlrConfigFiles(size_t* slr_total_file_size, BootImage* bi)
{
    uint32_t slr_sync_points[4] = { 0, 0, 0, 0 };
    uint32_t* syncpt_offsets = NULL;
    uint8_t num_of_sync_points = 0;
    for (std::vector<SsitConfigSlrInfo*>::iterator slr_info = configSlrsInfo.begin(); slr_info != configSlrsInfo.end(); slr_info++)
    {
        if (IsCdoFile((*slr_info)->file))
        {
            /* For CDO files - master CDO */
            const char* cdo_filename = (*slr_info)->file.c_str();
            CdoSequence * cdo_seq;
            #ifdef ENABLE_WDI
            if(isl::iostreams::xp::is_xp_format(cdo_filename))
            {
                cdo_seq = Decompressing_File(cdo_filename);
            }
            else
            {
                cdo_seq = cdoseq_load_cdo(cdo_filename);
            }
            #else
            cdo_seq = cdoseq_load_cdo(cdo_filename);
            #endif
            if (cdo_seq == NULL)
            {
                LOG_ERROR("Error parsing CDO file - %s", (*slr_info)->file.c_str());
            }
            search_for_sync_points();
            (*slr_info)->data = (uint8_t*)cdoseq_to_binary(cdo_seq, &(*slr_info)->size, 0);
            num_of_sync_points = get_num_of_sync_points();
            syncpt_offsets = get_slr_sync_point_offsets();
            LOG_TRACE("SSIT_SYNC_SLAVES command detected at following offsets:\n           Count : 0x%X", num_of_sync_points);
            slr_sync_points[0] = num_of_sync_points;
            for (int i = 0; i < num_of_sync_points; i++)
            {
                size_t offset = (*(syncpt_offsets + i) * 4);
                (*slr_info)->sync_addresses.push_back(offset);
                LOG_TRACE("       offset = 0x%x", offset);
            }
            /* As we strip the CDO HEADER, we are replacing that with NOP commands to ensure other alignments are not disturbed */
            auto cdoCmd = CdoCmdNoOperation(sizeof(VersalCdoHeader));
            memcpy((*slr_info)->data, cdoCmd, CDO_CMD_NOP_SIZE);
            memset((*slr_info)->data + CDO_CMD_NOP_SIZE, 0, sizeof(VersalCdoHeader) - CDO_CMD_NOP_SIZE);
            free(syncpt_offsets);

            /* Add the size to accomodate the NOPs added at the start of each master chunk
               Since the master chunks are read using the cdoseq_to_binary, each chunk comes with a CDO header
               This CDO header is replaced with NOPs to not disturb the alignments of the rest of the commands in CDO
               No. of CDO headers replaced is equal to the number of master chunks which is equal to the number of sync points */
            (*slr_info)->size += ((num_of_sync_points) * sizeof(VersalCdoHeader));
        }
        else
        {
            /* For PDI files - slave PDIs */
            std::ifstream fl((*slr_info)->file.c_str(), std::ios::binary);
            fl.seekg(0, std::ios::end);
            if (fl.bad() || fl.fail())
            {
                LOG_ERROR("Cannot seek to end of file - %s", (*slr_info)->file.c_str());
            }
            (*slr_info)->size = fl.tellg();
            auto slr_data = std::make_unique<uint8_t[]>((*slr_info)->size);
            fl.seekg(0, std::ios::beg);
            fl.read((char*)slr_data.get(), (*slr_info)->size);
            fl.close();
            (*slr_info)->data = slr_data.release();  // Transfer ownership to slr_info
            GetPartitionOffsets((*slr_info), (*slr_info)->data, (*slr_info)->size, bi);
            slr_sync_points[(*slr_info)->index] = (*slr_info)->sync_addresses.size();

            num_of_slrs++;
        }

        *slr_total_file_size += (*slr_info)->size;
    }

    /* Check for mismatch in sync points between different slaves and master */
    bool sync_point_mismatch_error = false;
    for (uint8_t i = 1; i <= num_of_slrs; i++)
    {
        if (slr_sync_points[i] != slr_sync_points[0])
        {
            sync_point_mismatch_error = true;
            break;
        }
    }
    char * env_ss = getenv("BOOTGEN_SKIP_SSIT_SYNCPOINT_CHECK");
    if (env_ss != NULL)
    {
        LOG_WARNING("BOOTGEN_SKIP_SSIT_SYNCPOINT_CHECK is enabled. Skipping the check for sync point mismatch across SLRs.");
        if (sync_point_mismatch_error)
        {
            LOG_WARNING("The number of SYNC commands are not same across slave SLRs and master SLR.");
        }
    }
    else
    {
        if (sync_point_mismatch_error)
        {
            LOG_WARNING("Set env variable BOOTGEN_SKIP_SSIT_SYNCPOINT_CHECK to 1, to skip the check for sync point mismatch across SLRs.");
            LOG_ERROR("The number of SYNC commands are not same across slave SLRs and master SLR.");
        }
    }
}

/******************************************************************************/
uint32_t VersalImageHeader::FindCurrentSyncPoint(void)
{
    uint32_t temp = 0xFFFFFFFF;

    /* Check  */
    for (std::vector<SsitConfigSlrInfo*>::iterator slr_info = configSlrsInfo.begin(); slr_info != configSlrsInfo.end(); slr_info++)
    {
        /* Don't check sync points in MASTER */
        if ((*slr_info)->index != SlrId::MASTER)
        {
            if (((*slr_info)->sync_points < temp) && ((*slr_info)->eof != true))
            {
                temp = (*slr_info)->sync_points;
            }
        }
    }
    return temp;
}

/******************************************************************************/
void VersalImageHeader::CheckSyncPointInChunk(SsitConfigSlrInfo* slr_info, size_t size)
{
    /* Check if the current chunk has the identified sync point for the SLR
       If yes, increment the sync point.
       "sync_point" points to the no. of sync points processed
    */
    for (uint32_t ix = 0; ix < slr_info->sync_addresses.size(); ix++)
    {
        if ((slr_info->sync_addresses[ix] >= slr_info->offset) && (slr_info->sync_addresses[ix] < (slr_info->offset + size)))
        {
            slr_info->sync_points++;
            LOG_TRACE("        sync point - %d",slr_info->sync_points);
        }
    }
}

/******************************************************************************/
void VersalImageHeader::SetSlrBootPartitions(std::list<SlrPdiInfo*> bootPdis)
{
    slrBootPdiInfo = bootPdis;
}

/******************************************************************************/
void VersalImageHeader::SetSlrConfigPartitions(std::list<SlrPdiInfo*> configPdis)
{
    slrConfigPdiInfo = configPdis;
}

/******************************************************************************/
void VersalImageHeader::SetWriteImageStorePartitions(ImageStorePdiInfo* imageStorePDIs)
{
    imageStorePdiInfo = imageStorePDIs;
}

/******************************************************************************/
void VersalImageHeader::SetDpacm(DpaCM::Type type)
{
    dpacm = type;
}

/******************************************************************************/
void VersalImageHeader::SetPufHdLocation(PufHdLoc::Type type)
{
    pufHdLoc = type;
}

/******************************************************************************/
void VersalSubSysImageHeader::Build(BootImage& bi, Binary& cache)
{
    if (section != NULL)
    {
        bi.headers.push_back(section);
        bi.imageHeaderTable->metaHeaderLength += section->Length;
    }

    if (imgList.size() == 0)
    {
        LOG_ERROR("Partitions not specified in subsystem - %s", imageName.c_str());
    }
    bool uid_updated = false;
    for (std::list<ImageHeader*>::iterator image = imgList.begin(); image != imgList.end(); image++)
    {
        (*image)->Build(bi, cache);
        if(((*image)->GetImageId() != 0) && ((*image)->IsUidInfoFoundInCdo() == true) && (uid_updated == false))
        {
            if (uniqueId == 0xFFFFFFFF)
            {
                uniqueId = (*image)->GetUniqueId();
            }
            if (ss_parentUniqueId == 0xFFFFFFFF)
            {
                ss_parentUniqueId = (*image)->GetParentUniqueId();
            }
            if (functionId == 0xFFFFFFFF)
            {
                functionId = (*image)->GetFunctionId();
            }
            uid_updated = true;
        }

        if (!isPLPowerDomain)
        {
            isPLPowerDomain = (*image)->GetPLPowerDomainFlag();
        }
        if (!isFullPowerDomain)
        {
            isFullPowerDomain = (*image)->GetFullPowerDomainFlag();
        }
        if (!isLowPowerDomain)
        {
            isLowPowerDomain = (*image)->GetLowPowerDomainFlag();
        }
        if (!isSystemPowerDomain)
        {
            isSystemPowerDomain = (*image)->GetSystemPowerDomainFlag();
        }
    }

    if (VersalSubSysImageHeaderTable == NULL)
    {
        VersalSubSysImageHeaderTable = (VersalImageHeaderStructure*)section->Data.get();
        SetImageName();
        SetImageHeaderAttributes();
        SetPartitionHeaderOffset(0);
        SetMetaHdrRevokeId(bi.bifOptions->metaHdrAttributes.partitionRevokeId);
        SetMemCopyAddress();
        SetImageHeaderIds();
    }
}

/******************************************************************************/
void VersalSubSysImageHeader::Link(BootImage &bi, SubSysImageHeader* nextHeader)
{
    VersalSubSysImageHeaderTable = (VersalImageHeaderStructure*)section->Data.get();
    if (imgList.front()->GetPartitionHeaderList().front()->section != NULL)
    {
        SetPartitionHeaderOffset((uint32_t)imgList.front()->GetPartitionHeaderList().front()->section->Address);
    }

    SetDataSectionCount();
    SetImageHeaderIds();
    SetPCRMeasurementIndex(bi.options.IsVersalNetSeries());
    SetPCRNumber(bi.options.IsVersalNetSeries());
    SetChecksum();
}


/******************************************************************************/
void VersalSubSysImageHeader::SetPCRMeasurementIndex(bool versalnet)
{
    // Use default value (0xFFFF) from BIF options for all arch types
    VersalSubSysImageHeaderTable->pcrMeasurementIndex = GetPCRMeasurementIndex();
}

/******************************************************************************/
void VersalSubSysImageHeader::SetPCRNumber(bool versalnet)
{
    // Use default value (0xFFFF) from BIF options for all arch types
    VersalSubSysImageHeaderTable->pcrNumber = GetPCRNumber();
}

/******************************************************************************/
void VersalSubSysImageHeader::SetChecksum(void)
{
    VersalSubSysImageHeaderTable->ihChecksum = ComputeWordChecksum(VersalSubSysImageHeaderTable, sizeof(VersalImageHeaderStructure) - sizeof(VersalSubSysImageHeaderTable->ihChecksum) /* bytes*/);
}

/******************************************************************************/
void VersalSubSysImageHeader::SetPartitionHeaderOffset(uint32_t addr)
{
    VersalSubSysImageHeaderTable->partitionHeaderWordOffset = addr / sizeof(uint32_t);
}

/******************************************************************************/
void VersalSubSysImageHeader::SetDataSectionCount(void)
{
    VersalSubSysImageHeaderTable->dataSectionCount = 0;
    for (std::list<ImageHeader*>::iterator imageItr = imgList.begin(); imageItr != imgList.end(); imageItr++)
    {
        VersalSubSysImageHeaderTable->dataSectionCount += (*imageItr)->NumPartitions();
    }
}

/******************************************************************************/
void VersalSubSysImageHeader::SetMetaHdrRevokeId(uint32_t id)
{
    VersalSubSysImageHeaderTable->metaHdrRevokeId = id;
}

/******************************************************************************/
void VersalSubSysImageHeader::SetImageHeaderIds(void)
{
    VersalSubSysImageHeaderTable->imageId = imageId;
    VersalSubSysImageHeaderTable->uniqueId = (uniqueId != 0xFFFFFFFF) ? uniqueId : 0;
    VersalSubSysImageHeaderTable->parentUniqueId = (ss_parentUniqueId != 0xFFFFFFFF) ? ss_parentUniqueId : 0;
    VersalSubSysImageHeaderTable->functionId = (functionId != 0xFFFFFFFF) ? functionId : 0;
}

/******************************************************************************/
void VersalSubSysImageHeader::SetImageHeaderAttributes(void)
{
    if (delayLoad)
    {
        VersalSubSysImageHeaderTable->imageAttributes |= (1 << vihDelayLoadShift);
    }
    if (delayHandoff)
    {
        VersalSubSysImageHeaderTable->imageAttributes |= (1 << vihDelayHandoffShift);
    }
    if (memCopyAddr != 0xFFFFFFFFFFFFFFFF)
    {
        VersalSubSysImageHeaderTable->imageAttributes |= (1 << vihCopyToMemoryShift);
    }
    if (isFullPowerDomain)
    {
        VersalSubSysImageHeaderTable->imageAttributes |= (1 << vihLowPowerDomainShift);
    }
    if (isLowPowerDomain)
    {
        VersalSubSysImageHeaderTable->imageAttributes |= (1 << vihFullPowerDomainShift);
    }
    if (isSystemPowerDomain)
    {
        VersalSubSysImageHeaderTable->imageAttributes |= (1 << vihSystemPowerDomainShift);
    }
    if (isPLPowerDomain)
    {
        VersalSubSysImageHeaderTable->imageAttributes |= (1 << vihPLPowerDomainShift);
    }
}

/******************************************************************************/
void VersalSubSysImageHeader::SetImageName(void)
{
    for (uint32_t i = 0; i<imageName.size(); i++)
    {
        VersalSubSysImageHeaderTable->imageName[i] = imageName[i];
    }
}

/******************************************************************************/
void VersalSubSysImageHeader::SetMemCopyAddress(void)
{
    if (memCopyAddr == 0xFFFFFFFFFFFFFFFF)
    {
        memCopyAddr = 0;
    }
    VersalSubSysImageHeaderTable->memcpyAddressLo = memCopyAddr;
    VersalSubSysImageHeaderTable->memcpyAddressHi = memCopyAddr >> 32;
}

/******************************************************************************/
uint64_t VersalSubSysImageHeader::GetMemCopyAddress(void)
{
    uint64_t mem_cpy_addr = 0;
    mem_cpy_addr = VersalSubSysImageHeaderTable->memcpyAddressHi;
    mem_cpy_addr = (mem_cpy_addr << 32) | (VersalSubSysImageHeaderTable->memcpyAddressLo);
    return mem_cpy_addr;
}

/******************************************************************************/
uint32_t VersalSubSysImageHeader::GetPartitionHeaderOffset(void)
{
    return VersalSubSysImageHeaderTable->partitionHeaderWordOffset * sizeof(uint32_t);
}

/******************************************************************************/
uint32_t VersalSubSysImageHeader::GetDataSectionCount(void)
{
    return VersalSubSysImageHeaderTable->dataSectionCount;
}

/******************************************************************************/
uint32_t VersalSubSysImageHeader::GetImageHeaderAttributes(void)
{
    return VersalSubSysImageHeaderTable->imageAttributes;
}

/******************************************************************************/
VersalSubSysImageHeader::VersalSubSysImageHeader(ImageBifOptions *imgOptions)
{
    VersalSubSysImageHeaderTable = NULL;

    imageName = imgOptions->GetImageName();
    imageId = imgOptions->GetImageId();
    delayLoad = imgOptions->GetDelayLoad();
    delayHandoff = imgOptions->GetDelayHandoff();
    memCopyAddr = imgOptions->GetMemCopyAddress();
    imageType = imgOptions->GetImageType();
    uniqueId = imgOptions->GetUniqueId();
    ss_parentUniqueId = imgOptions->GetParentUniqueId();
    functionId = imgOptions->GetFunctionId();
    pcrNumber = imgOptions->GetPcrNumber();
    pcrMeasurementIndex = imgOptions->GetPcrMeasurementIndex();

    isFullPowerDomain = false;
    isLowPowerDomain = false;
    isSystemPowerDomain = false;
    isPLPowerDomain = false;

    std::string name = "ImageHeader " + imageName;
    uint32_t size = sizeof(VersalImageHeaderStructure);
    auto temp_section = std::make_unique<Section>(name, size);
    section = temp_section.release();  // Transfer ownership to raw pointer member
    memset(section->Data.get(), 0, size);
    imgList.clear();
    num_of_images = 0;
}

/******************************************************************************/
VersalSubSysImageHeader::VersalSubSysImageHeader(std::ifstream& ifs)
{
    VersalImageHeaderStructure importedSubSysHeaderTable;

    long pos = ifs.tellg();
    ifs.read((char*)&importedSubSysHeaderTable, 4 * sizeof(uint32_t));

    char buffer[4];
    char name[256];
    uint32_t i = 0;

    do
    {
        ifs.read(buffer, 4);
        name[i + 0] = buffer[0];
        name[i + 1] = buffer[1];
        name[i + 2] = buffer[2];
        name[i + 3] = buffer[3];
        i += 4;
    } while (buffer[0] && buffer[1] && buffer[2] && buffer[3] && i<sizeof(name));

    if (i >= sizeof(name))
    {
        LOG_DEBUG(DEBUG_STAMP, "Image header name too long");
        LOG_ERROR("Failure parsing imported bootimage");
    }
    size_t nameLen = strlen(name);
    bool validName = (nameLen > 0 && nameLen < sizeof(name));
    if (validName)
    {
        for (size_t c = 0; c < nameLen; c++)
        {
            if (!isprint((unsigned char)name[c]))
            {
                validName = false;
                break;
            }
        }
    }
    imageName = validName ? name : "imported_subsystem";
    uint32_t size = sizeof(VersalImageHeaderStructure);

    ifs.seekg(pos);
    auto temp_section = std::make_unique<Section>("ImageHeader " + imageName, size);
    section = temp_section.release();  // Transfer ownership to raw pointer member
    VersalSubSysImageHeaderTable = (VersalImageHeaderStructure*)section->Data.get();
    ifs.read((char*)VersalSubSysImageHeaderTable, size);

    imageId = VersalSubSysImageHeaderTable->imageId;
    uniqueId = VersalSubSysImageHeaderTable->uniqueId;
    ss_parentUniqueId = VersalSubSysImageHeaderTable->parentUniqueId;
    functionId = VersalSubSysImageHeaderTable->functionId;
    delayLoad = ((VersalSubSysImageHeaderTable->imageAttributes >> vihDelayLoadShift) & vihDelayLoadMask);
    delayHandoff = ((VersalSubSysImageHeaderTable->imageAttributes >> vihDelayHandoffShift) & vihDelayHandoffMask);
    memCopyAddr = (uint64_t) (VersalSubSysImageHeaderTable->memcpyAddressLo) >> 32;
    memCopyAddr |= VersalSubSysImageHeaderTable->memcpyAddressHi;
    isPLPowerDomain = ((VersalSubSysImageHeaderTable->imageAttributes >> vihPLPowerDomainShift) & vihPLPowerDomainMask);
    isFullPowerDomain = ((VersalSubSysImageHeaderTable->imageAttributes >> vihFullPowerDomainShift) & vihFullPowerDomainMask);
    isLowPowerDomain = ((VersalSubSysImageHeaderTable->imageAttributes >> vihLowPowerDomainShift) & vihLowPowerDomainMask);
    isSystemPowerDomain = ((VersalSubSysImageHeaderTable->imageAttributes >> vihSystemPowerDomainShift) & vihSystemPowerDomainMask);
    pcrNumber = VersalSubSysImageHeaderTable->pcrNumber;
    pcrMeasurementIndex = VersalSubSysImageHeaderTable->pcrMeasurementIndex;

    /* Find the no. of image headers to be created */
    num_of_images = 0;

    uint32_t p_count = 0;
    if (VersalSubSysImageHeaderTable->dataSectionCount < 32)
    {
        p_count = VersalSubSysImageHeaderTable->dataSectionCount;
    }

    uint32_t p_offset = VersalSubSysImageHeaderTable->partitionHeaderWordOffset * sizeof(uint32_t);
    p_offset += 0x28;
    for (uint32_t i = 0; i < p_count; i++)
    {
        ifs.seekg(p_offset);
        num_of_images++;
        p_offset += sizeof(VersalPartitionHeaderTableStructure);
    }
}

/******************************************************************************/
void VersalImageHeader::CheckIdsInCdo(CdoSequence * seq, bool isVersalNetSeries, std::string cdo_filename)
{
    LINK * l = seq->cmds.next;
    while (l != &seq->cmds) 
    {
        CdoCommand * cmd = all2cmds(l);
        l = l->next;
        if(cmd->type == CdoCmdMarker)
        {
            char* marker_arch = (char*)cmd->buf;
            if (cmd->value == MARKER_ARCHITECTURE)
            {
               if (isVersalNetSeries && (strstr(marker_arch,"versalnet") == 0))
               {
                  LOG_WARNING("The architecture(%s) in '%s' does not match '-arch versalnet'", marker_arch, cdo_filename.c_str());
               }
               else if(!isVersalNetSeries && (strstr(marker_arch,"versal") == 0))
               {
                  LOG_WARNING("The architecture(%s) in '%s' does not match '-arch versal'", marker_arch, cdo_filename.c_str());
               }
            }
        }

        if(cmd->type == CdoCmdPmInitNode)
        {
            imageId = u32le(cmd->id);
            if (cmd->value == 0)
            {
                SetPowerDomains((uint8_t*)cmd->buf,cmd->count);
            }
        }
        if(cmd->type == CdoCmdLdrSetImageInfo)
        {
            if (imageId == u32le(cmd->id))
            {
                uniqueId = u32le(cmd->value);
                parentUniqueId = u32le(cmd->mask);
                functionId = u32le(cmd->count);
                uidInfoFoundInCdo = true;
            }
            break;
        }
    }
}

/******************************************************************************/
void VersalImageHeader::SetPowerDomains(uint8_t* buf, uint32_t count)
{
    uint32_t id = 0;
    for (uint32_t index = 0; index < count ; index++)
    {
        id = ReadLittleEndian32(buf);
        switch (id)
        {
        case POWER_FPD:
            isIhFullPowerDomain = true;
            break;
        case POWER_LPD:
            isIhLowPowerDomain = true;
            break;
        case POWER_SPD:
            isIhSystemPowerDomain = true;
            break;
        case POWER_PLD:
            isIhPLPowerDomain = true;
            break;
        default:
            break;
        }
        buf += sizeof(uint32_t);
    }
}

/******************************************************************************/
void VersalImageHeader::CreateWriteImageStorePartition()
{
    LOG_INFO("Creating Write Image Store CDO partition");
    std::ifstream s(Filename.c_str());
    if (!s)
    {
        LOG_ERROR("Cannot read file - %s ", Filename.c_str());
    }

    ByteFile data(Filename);
    uint32_t p_size_pad = data.len + ((4 - (data.len & 3)) & 3);
    auto tempBuffer = std::make_unique<uint8_t[]>(p_size_pad);
    memcpy(tempBuffer.get(), data.bytes, p_size_pad);

    size_t size = 0;
    size_t p_offset = 0;
    /* CDO Header */
    cdoHeader = std::make_unique<VersalCdoHeader>();
    cdoHeader->remaining_words = CDO_REMAINING_WORDS;
    cdoHeader->id_word = CDO_IDENTIFICATION; /* CDO */
    cdoHeader->version = CDO_VERSION; /* Version - 2.0 */

    /* Populate length and checksum later, as we can't estimate the exact size of CDO */
    cdoHeader->length = 0;
    cdoHeader->checksum = 0;
    size += sizeof(VersalCdoHeader);
    std::vector<uint8_t> p_buffer(size);
    memcpy(p_buffer.data(), cdoHeader.get(), sizeof(VersalCdoHeader));
    p_offset += sizeof(VersalCdoHeader);

    auto cdoCmd = std::make_unique<CdoCommandWriteImageStore>();
    cdoCmd->header = 0x00ff070D;
    cdoCmd->length = (p_size_pad / 4) + 2;
    cdoCmd->id = imageStorePdiInfo->id;
    auto cdoCmd_data = std::make_unique<uint8_t[]>(p_size_pad);
    memset(cdoCmd_data.get(), 0, p_size_pad);
    memcpy(cdoCmd_data.get(), tempBuffer.get(), p_size_pad);
    cdoCmd->data = cdoCmd_data.release();  // Transfer ownership to struct

    size += CDO_CMD_WRITE_IMAGE_STORE_SIZE;
    p_buffer.resize(size);
    memcpy(p_buffer.data() + p_offset, cdoCmd.get(), CDO_CMD_WRITE_IMAGE_STORE_SIZE);
    p_offset += CDO_CMD_WRITE_IMAGE_STORE_SIZE;

    size += p_size_pad;
    p_buffer.resize(size);
    memcpy(p_buffer.data() + p_offset, cdoCmd->data, p_size_pad);
    p_offset += p_size_pad;

    size += sizeof(CdoCommandHeader);
    p_buffer.resize(size);
    auto cmd_end = CdoCmdCdoEnd();
    memcpy(p_buffer.data() + p_offset, cmd_end, sizeof(CdoCommandHeader));

    /* Update CDO header lengths and checksum */
    cdoHeader->length = (size - sizeof(VersalCdoHeader)) / 4;
    cdoHeader->checksum = ~(cdoHeader->remaining_words + cdoHeader->id_word + cdoHeader->version + cdoHeader->length);
    memcpy(p_buffer.data(), cdoHeader.get(), sizeof(VersalCdoHeader));

    SetPartitionType(PartitionType::CONFIG_DATA_OBJ);
    auto partHdr = std::make_unique<VersalPartitionHeader>(this, imageStorePdiInfo->id);
    partHdr->execState = 0;
    partHdr->elfEndianess = 0;
    partHdr->firstValidIndex = true;
    partHdr->loadAddress = 0xFFFFFFFFFFFFFFFF;
    partHdr->execAddress = 0;
    partHdr->partitionSize = size;
    auto buffer_copy = std::make_unique<uint8_t[]>(size);
    memcpy(buffer_copy.get(), p_buffer.data(), size);
    partHdr.get()->partition = std::make_unique<VersalPartition>(partHdr.get(), buffer_copy.release(), size);  // Transfer ownership to VersalPartition
    partitionHeaderList.push_back(partHdr.release());
}

CdoSequence * VersalImageHeader::Decompressing_File(const char * filename)
{
    #if defined(_WIN64) || defined(_WIN32)
    LOG_ERROR("This is not supported in windows");
    CdoSequence * seq = NULL;
    return seq;
    #endif

    #if defined(__linux__)

    #ifdef ENABLE_WDI
    isl::iostreams::istream xilZipObj(filename);
    xilZipObj.seekg(0, std::ios::end);

    int length = xilZipObj.tellg();
    char* buffer = new char[length+1];
    buffer[length] = '\0';

    xilZipObj.seekg(0, std::ios::beg);
    xilZipObj.read(buffer, length);


    CdoSequence * seq = cdoseq_load_cdo_from_buffer(buffer, length);
    delete[] buffer;
    return seq;
    #else
    CdoSequence * seq = NULL;
    return seq; 
    #endif

    #endif
}
