/*
 * File:    GHSSecInfo.cpp
 *
 * Copyright (c) Freescale Semiconductor, Inc. All rights reserved.
 * See included license file for license details.
 */

#include "blfwk/GHSSecInfo.h"
#include <stdexcept>
#include "blfwk/Logging.h"
#include "blfwk/EndianUtilities.h"

//! The name of the GHS-specific section info table ELF section.
const char *const kSecInfoSectionName = ".secinfo";

using namespace blfwk;

//! The ELF file passed into this constructor as the \a elf argument must remain
//! valid for the life of this object.
//!
//! \param elf The ELF file parser. An assertion is raised if this is NULL.
GHSSecInfo::GHSSecInfo(StELFFile *elf)
    : m_elf(elf)
    , m_hasInfo(false)
    , m_info(0)
    , m_entryCount(0)
{
    assert(elf);

    // look up the section. if it's not there just leave m_info and m_entryCount to 0
    unsigned sectionIndex = m_elf->getIndexOfSectionWithName(kSecInfoSectionName);
    if (sectionIndex == SHN_UNDEF)
    {
        return;
    }

    // get the section data
    const Elf32_Shdr &secInfo = m_elf->getSectionAtIndex(sectionIndex);
    if (secInfo.sh_type != SHT_PROGBITS)
    {
        // .secinfo section isn't the right type, so something is wrong
        return;
    }

    m_hasInfo = true;
    m_info = (ghs_secinfo_t *)m_elf->getSectionDataAtIndex(sectionIndex);
    m_entryCount = secInfo.sh_size / sizeof(ghs_secinfo_t);
}

//! Looks up \a addr for \a length in the .secinfo array. Only if that address is in the
//! .secinfo array does this section need to be filled. If the section is found but the
//! length does not match the \a length argument, a message is logged at the
//! #Logger::kWarning level.
//!
//! If the .secinfo section is not present in the ELF file, this method always returns
//! true.
//!
//! \param addr The start address of the section to query.
//! \param length The length of the section. If a section with a start address matching
//!        \a addr is found, its length must match \a length to be considered.
//!
//! \retval true The section matching \a addr and \a length was found and should be filled.
//!        True is also returned when the ELF file does not have a .secinfo section.
//! \retval false The section was not found and should not be filled.
bool GHSSecInfo::isSectionFilled(uint32_t addr, uint32_t length)
{
    if (!m_hasInfo)
    {
        return true;
    }

    unsigned i;
    for (i = 0; i < m_entryCount; ++i)
    {
        // byte swap these values into host endianness
        uint32_t clearAddr = ENDIAN_LITTLE_TO_HOST_U32(m_info[i].m_clearAddr);
        uint32_t numBytesToClear = ENDIAN_LITTLE_TO_HOST_U32(m_info[i].m_numBytesToClear);

        // we only consider non-zero length clear regions
        if ((addr == clearAddr) && (numBytesToClear != 0))
        {
            // it is an error if the address matches but the length does not
            if (length != numBytesToClear)
            {
                Log::log(Logger::kWarning, "ELF Error: Size mismatch @ sect=%u, .secinfo=%u at addr 0x%08X\n", length,
                         numBytesToClear, addr);
            }
            return true;
        }
    }

    return false;
}

//! Simply calls through to isSectionFilled(uint32_t, uint32_t) to determine
//! if \a section should be filled.
//!
//! If the .secinfo section is not present in the ELF file, this method always returns
//! true.
bool GHSSecInfo::isSectionFilled(const Elf32_Shdr &section)
{
    return isSectionFilled(section.sh_addr, section.sh_size);
}
