/*
 * Copyright (C) Jan 2013 Mellanox Technologies Ltd. All rights reserved.
 *
 * This software is available to you under a choice of one of two
 * licenses.  You may choose to be licensed under the terms of the GNU
 * General Public License (GPL) Version 2, available from the file
 * COPYING in the main directory of this source tree, or the
 * OpenIB.org BSD license below:
 *
 *     Redistribution and use in source and binary forms, with or
 *     without modification, are permitted provided that the following
 *     conditions are met:
 *
 *      - Redistributions of source code must retain the above
 *        copyright notice, this list of conditions and the following
 *        disclaimer.
 *
 *      - Redistributions in binary form must reproduce the above
 *        copyright notice, this list of conditions and the following
 *        disclaimer in the documentation and/or other materials
 *        provided with the distribution.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */


#include <stdlib.h>
#include <string.h>
#include <errno.h>

#include "flint_base.h"
#include "flint_io.h"
#include "fw_ops.h"
#include "fs4_ops.h"
#include "fs3_ops.h"
#include "fs2_ops.h"
#include "fsctrl_ops.h"

#ifdef CABLES_SUPP
#include "cablefw_ops.h"
#endif

#ifndef NO_MFA_SUPPORT
#include <mfa.h>
#endif

#define BAD_CRC_MSG "Bad CRC."
extern const char *g_sectNames[];

bool FwOperations::readBufAux(FBase& f, u_int32_t o, void *d, int l, const char*
                              p)
{
    bool rc = true;
    READBUF(f, o, d, l, p);
    return rc;
}

#ifndef NO_MFA_SUPPORT

int FwOperations::getFileSignature(const char *fname)
{
    FILE *fin;
    char tmpb[16];
    int res = 0;

    if (!(fin = fopen(fname, "r"))) {
        // abit ugly , need to establish a correct ret val
        return IMG_SIG_OPEN_FILE_FAILED;
    }
    if (!fgets(tmpb, sizeof(tmpb), fin)) {
        goto clean_up;
    }
    if (strlen(tmpb) < 4) {
        goto clean_up;
    }

    if (!strncmp(tmpb, "MTFW", 4)) {
        res = IMG_SIG_TYPE_BIN;
    }
    if (!strncmp(tmpb, "MFAR", 4)) {
        res = IMG_SIG_TYPE_MFA;
    }
    if (!strncmp(tmpb, "MTCF", 4)) {
        res = IMG_SIG_TYPE_CF;
    }

clean_up:
    fclose(fin);
    return res;
}


int FwOperations::getBufferSignature(u_int8_t *buf, u_int32_t size)
{
    int res = 0;

    if (size < 4) {
        return 0;
    }
    if (!strncmp((char*)buf, "MTFW", 4)) {
        res = IMG_SIG_TYPE_BIN;
    }
    if (!strncmp((char*)buf, "MFAR", 4)) {
        res = IMG_SIG_TYPE_MFA;
    }

    return res;
}

int FwOperations::getMfaImgInner(char *fileName, u_int8_t *mfa_buf, int size,
                                 char *psid, u_int8_t **imgbuf, char *errBuf, int errBufSize)
{
    int image_type = 1; //FW image
    mfa_desc *mfa_d = NULL;
    int res = -1;

    // open mfa file
    if (fileName) {
        res = mfa_open_file(&mfa_d, fileName);
    } else if (mfa_buf && size != 0) {
        res = mfa_open_buf(&mfa_d, mfa_buf, size);
    } else {
        WriteToErrBuff(errBuf, "Interanl error: bad parameters to getMfaImg", errBufSize);
        return res;
    }

    if (res) {
        res = res < 0 ? res : -1 * res;
        WriteToErrBuff(errBuf, "Failed to open mfa file", errBufSize);
        if (mfa_d) {
            mfa_close(mfa_d);
        }
        return res;
    }

    if (psid == NULL) {
        WriteToErrBuff(errBuf, "Internal error: PSID must be supplied", errBufSize);
        if (mfa_d) {
            mfa_close(mfa_d);
        }
        return -1; //No psid => no image
    }

    // get appropriate image for requested PSID from mfa file
    res = mfa_get_image(mfa_d, psid, image_type, (char*)"", imgbuf);

    // if res != 0 there is an error
    if (res) {
        const char *errStr = mfa_get_last_error(mfa_d);
        // if error field is empty return generic message
        if (!errStr || strlen(errStr) == 0) {
            errStr = "Failed to get MFA Image";
        }

        WriteToErrBuff(errBuf, errStr, errBufSize);
    }

    // close mfa file
    mfa_close(mfa_d);
    return res;
}

int FwOperations::getMfaImg(char *fileName, char *psid, u_int8_t **imgbuf, char *errBuf, int errBufSize)
{
    return getMfaImgInner(fileName, NULL, 0,
                          psid,  imgbuf, errBuf, errBufSize);
}


int FwOperations::getMfaImg(u_int8_t *mfa_buf, int size, char *psid, u_int8_t **imgbuf, char *errBuf, int errBufSize)
{
    return getMfaImgInner(NULL, mfa_buf, size,
                          psid, imgbuf, errBuf, errBufSize);
}

#endif


void FwOperations::FwCleanUp()
{
    if (_ioAccess) {
        _ioAccess->close();
        delete _ioAccess;
        _ioAccess = (FBase*)NULL;
    }
    if (_fname) {
        delete[] _fname;
        _fname = (char*)NULL;
    }
    if (_devName) {
        delete[] _devName;
        _devName = (char*)NULL;
    }
    if (_fwParams.fileHndl) {
        delete[] _fwParams.fileHndl;
        _fwParams.fileHndl = (char*)NULL;
    }
    if (_fwParams.mstHndl) {
        delete[] _fwParams.mstHndl;
        _fwParams.mstHndl = (char*)NULL;
    }
    if (_fwParams.psid) {
        delete[] _fwParams.psid;
        _fwParams.psid = (char*)NULL;
    }
}

bool FwOperations::FwVerifyAdv(ExtVerifyParams& verifyParams)
{
    return FwVerify(verifyParams.verifyCallBackFunc, verifyParams.isStripedImage, verifyParams.showItoc, verifyParams.ignoreDToc);
}

void FwOperations::FwInitCom()
{
    memset(&_fwImgInfo, 0, sizeof(_fwImgInfo));
}

bool FwOperations::IsFsCtrlOperations()
{
    return false;
}

void FwOperations::GetFwParams(fw_ops_params_t& fwParams)
{
    fwParams = _fwParams;
}

void FwOperations::getSupporteHwId(u_int32_t **supportedHwId, u_int32_t &supportedHwIdNum)
{
    *supportedHwId    = _fwImgInfo.supportedHwId;
    supportedHwIdNum = _fwImgInfo.supportedHwIdNum;

}

bool FwOperations::checkBoot2(u_int32_t beg, u_int32_t offs, u_int32_t& next, bool fullRead, const char *pref, VerifyCallBack verifyCallBackFunc)
{
    u_int32_t size = 0x0;

    char *pr = new char[strlen(pref) + 512];
    sprintf(pr, "%s /0x%08x/ (BOOT2)", pref, offs + beg);
    // Size
    if (!(*_ioAccess).read(offs + beg + 4, &size)) {
        errmsg("%s - read error (%s)\n", pr, (*_ioAccess).err());
        delete[] pr;
        return false;
    }
    TOCPU1(size);
    if (size > 1048576 || size < 4) {
        report_callback(verifyCallBackFunc, "%s /0x%08x/ - unexpected size (0x%x)\n", pr, offs + beg + 4, size);
        delete[] pr;
        return false;
    }
    _fwImgInfo.bootSize = (size + 4) * 4;

    // Get absolute address on flash when checking BOOT2 for FS3 image format (for FS2 its always displayed as contiguous)
    // Adrianc: why dont we show them both in the same way when running verify.
    u_int32_t boot2AbsAddr = ((this->FwType() == FIT_FS3 || this->FwType() == FIT_FS4) && _ioAccess->is_flash()) ? \
                             _ioAccess->get_phys_from_cont(beg, _fwImgInfo.cntxLog2ChunkSize, (_fwImgInfo.imgStart != 0)) : beg;

    sprintf(pr, "%s /0x%08x-0x%08x (0x%06x)/ (BOOT2)", pref, offs + boot2AbsAddr,
            offs + boot2AbsAddr + (size + 4) * 4 - 1, (size + 4) * 4);

    if ((_ioAccess->is_flash() && fullRead == true) || !_ioAccess->is_flash()) {
        Crc16 crc;
        u_int32_t *buff = new u_int32_t[size + 4];
        bool rc = readBufAux((*_ioAccess), offs + beg, buff, size * 4 + 16, pr);
        if (!rc) {
            delete[] pr;
            delete[] buff;
            return rc;
        }
        // we hold for FS3 an image cache so we selectevely update it in UpdateImgCache() call
        UpdateImgCache((u_int8_t*)buff, offs + beg, size * 4 + 16);
        TOCPUn(buff, size + 4);
        CRC1n(crc, buff, size + 4);
        CRC1n(_ioAccess->get_image_crc(), buff, size + 4);


        crc.finish();

        u_int32_t crc_act = buff[size + 3];
        delete[] buff;
        if (crc.get() != crc_act) {
            report_callback(verifyCallBackFunc, "%s /0x%08x/ - wrong CRC (exp:0x%x, act:0x%x)\n",
                            pr, offs + beg, crc.get(), crc_act);
            delete[] pr;
            return errmsg(MLXFW_BAD_CRC_ERR, BAD_CRC_MSG);
        }
        _ioAccess->get_image_crc() << crc_act;
        // TODO: Print CRC
        if (0) {
            report_callback(verifyCallBackFunc, "%s - OK (CRC:0x%04x)\n", pr, (crc_act & 0xffff));
        } else {
            report_callback(verifyCallBackFunc, "%s - OK\n", pr);
        }
    }
    next = offs + size * 4 + 16;
    delete[] pr;
    return true;
} // checkBoot2


bool FwOperations::CheckAndPrintCrcRes(char *pr, bool blank_crc, u_int32_t off, u_int32_t crc_act, u_int32_t crc_exp, bool ignore_crc,
                                       VerifyCallBack verifyCallBackFunc)
{
    if (ignore_crc) {
        report_callback(verifyCallBackFunc, "%s - CRC IGNORED\n", pr);
    } else {
        if (blank_crc) {
            report_callback(verifyCallBackFunc, "%s - BLANK CRC (0xffff)\n", pr);
        } else if (crc_exp == crc_act) {
            report_callback(verifyCallBackFunc, "%s - OK\n", pr);
        } else {
            report_callback(verifyCallBackFunc, "%s /0x%08x/ - wrong CRC (exp:0x%x, act:0x%x)\n",
                            pr, off, crc_exp, crc_act);
            return errmsg(BAD_CRC_MSG);
        }
    }
    return true;
}

#define CIB_MAJOR 10
#define OLD_FORMAT_MAX_MINOR_CIB 10
#define SIB_MAJOR 11
#define OLD_FORMAT_MAX_MINOR_SIB 1

/*
 * currently we have 3 FW version format
 * 0: MM.mm.ssss
 * 1: MM.mmbb.ssss
 * 2: MM.mm.bsss / MM.mm.bbss for branch !=0
 * Where:
 * M: major digit
 * m: minor digit
 * b: branch digit
 * s: subminor digit
 */
int FwOperations::GetFwVerFormat(u_int16_t fwVer[3])
{
    if (fwVer[1] > 99) {
        return 1;
    }

    if ((fwVer[0] == CIB_MAJOR && fwVer[1] <= OLD_FORMAT_MAX_MINOR_CIB) ||
        (fwVer[0] == SIB_MAJOR && fwVer[1] <= OLD_FORMAT_MAX_MINOR_SIB)) {
        return 0;
    }

    return 2;
}

FwVerInfo FwOperations::FwVerLessThan(u_int16_t r1[3], u_int16_t r2[3])
{
    // copy version arrays to local variables
    u_int16_t fwVer1[3];
    u_int16_t fwVer2[3];
    u_int16_t fwVersionFormatR1 = 0;
    u_int16_t fwVersionFormatR2 = 0;
    memcpy(fwVer1, r1, sizeof(fwVer1));
    memcpy(fwVer2, r2, sizeof(fwVer2));

    fwVersionFormatR1 = GetFwVerFormat(fwVer1);
    fwVersionFormatR2 = GetFwVerFormat(fwVer2);
    // try to decide according to format
    if (fwVersionFormatR1 > fwVersionFormatR2) {
        return FVI_GREATER;
    } else if (fwVersionFormatR1 < fwVersionFormatR2) {
        return FVI_SMALLER;
    }
    // same format compare each field
    for (int i = 0; i < 3; i++) {
        if (fwVer1[i] < fwVer2[i]) {
            return FVI_SMALLER;
        } else if (fwVer1[i] > fwVer2[i]) {
            return FVI_GREATER;
        }
    }
    return FVI_EQUAL; // equal versions
}
const u_int32_t FwOperations::_cntx_magic_pattern[4] = {
    0x4D544657,   // Ascii of "MTFW"
    0x8CDFD000,   // Random data
    0xDEAD9270,
    0x4154BEEF
};

const u_int32_t FwOperations::_fs4_magic_pattern[4] = {
    0x4D544657,
    0xABCDEF00,
    0xFADE1234,
    0x5678DEAD
};

const u_int32_t FwOperations::_cntx_image_start_pos[FwOperations::CNTX_START_POS_SIZE] = {
    0,
    0x10000,
    0x20000,
    0x40000,
    0x80000,
    0x100000,
    0x200000,
    0x400000,
    0x800000,
    0x1000000
};

bool FwOperations::FindMagicPattern(FBase *ioAccess, u_int32_t addr,
                                    u_int32_t const cntx_magic_pattern[])
{
    if (addr + 16 > ioAccess->get_size()) {
        return false;
    }
    for (int i = 0; i < 4; i++) {
        u_int32_t w;
        READ4_NOERRMSG((*ioAccess), addr + i * 4, &w);
        TOCPU1(w);
        if (w != cntx_magic_pattern[i]) {
            //printf("-D- Looking for magic pattern %d addr %06x: Exp=%08x Act=%08x\n", i, addr + i * 4, _cntx_magic_pattern[i], w);
            return false;
        }
    }

    return true;
}

// FindAllImageStart
// OUT: start_locations: set to the start addresses of the found image markers (in accending order)
// OUT: found_images:    Number of found images (and number of valid entries in the start_locations array).
bool FwOperations::FindAllImageStart(FBase *ioAccess,
                                     u_int32_t start_locations[CNTX_START_POS_SIZE],
                                     u_int32_t *found_images,
                                     u_int32_t const cntx_magic_pattern[])
{

    int needed_pos_num;

    needed_pos_num = CNTX_START_POS_SIZE;

    if (ioAccess->is_flash()) {
        if ( (((Flash*)ioAccess)->get_dev_id() == CX2_HW_ID) ||
             (((Flash*)ioAccess)->get_dev_id() == IS4_HW_ID)) {
            needed_pos_num = OLD_CNTX_START_POS_SIZE;
        }
    }
    /* WA: due to bug on SwichIB first GA FW (FW doesnt look at chip select field in mfba)
     *     when reading from flash address 0x400000 it wraps around to 0x0 causing more than one
     *     valid image to be found. as a WA we dont check at 0x400000. basic flash operations
     *     are affected when attempting to access addressess greater than 0x3fffff.
     */
    if (((Flash*)ioAccess)->get_dev_id() == SWITCH_IB_HW_ID) {
        needed_pos_num -= 1;
    }

    ioAccess->set_address_convertor(0, 0);
    if (found_images) {
        *found_images = 0;
        for (int i = 0; i < needed_pos_num; i++) {
            if (FindMagicPattern(ioAccess, _cntx_image_start_pos[i], cntx_magic_pattern)) {
                start_locations[*found_images] = _cntx_image_start_pos[i];
                (*found_images)++;
            }
        }
    }

    return true;
}
// CAN BE IN ANOTHER MODULE
bool FwOperations::GetSectData(std::vector<u_int8_t>& file_sect, const u_int32_t *buff, const u_int32_t size)
{

    file_sect.clear();
    file_sect.insert(file_sect.end(),
                     (u_int8_t*)buff,
                     (u_int8_t*)buff + size);


    return true;
}


bool FwOperations::FwAccessCreate(fw_ops_params_t& fwParams, FBase **ioAccessP)
{
    // FBase *ioAccess = *ioAccessP;
    if (fwParams.hndlType == FHT_FW_FILE) {
#ifndef NO_MFA_SUPPORT
        int sig = getFileSignature(fwParams.fileHndl);
        if (sig == IMG_SIG_OPEN_FILE_FAILED) { //i.e we failed to open file
            WriteToErrBuff(fwParams.errBuff, strerror(errno), fwParams.errBuffSize);
            return false;
        }
        if (sig == IMG_SIG_TYPE_BIN || sig == IMG_SIG_TYPE_CF) {
            *ioAccessP = new FImage;
            if (!(*ioAccessP)->open(fwParams.fileHndl, false, !fwParams.shortErrors)) {
                WriteToErrBuff(fwParams.errBuff, (*ioAccessP)->err(), fwParams.errBuffSize);
                delete *ioAccessP;
                return false;
            }
        } else if (sig == IMG_SIG_TYPE_MFA) {
            u_int8_t *imgbuf = NULL;
            int sz;
            // get image from mfa file
            if ((sz = getMfaImg(fwParams.fileHndl, fwParams.psid, &imgbuf, fwParams.errBuff, fwParams.errBuffSize)) < 0) {
                return false;
            }
            *ioAccessP = new FImage;
            if (!((FImage*)(*ioAccessP))->open((u_int32_t*)imgbuf, (u_int32_t)sz, !fwParams.shortErrors)) {
                mfa_release_image(imgbuf);
                WriteToErrBuff(fwParams.errBuff, (*ioAccessP)->err(), fwParams.errBuffSize);
                delete *ioAccessP;
                return false;
            }
            mfa_release_image(imgbuf);
        } else {
            WriteToErrBuff(fwParams.errBuff, "Invalid Image signature.", fwParams.errBuffSize);
            return false; //Unknown signature
        }
#else
        *ioAccessP = new FImage;
        if (!(*ioAccessP)->open(fwParams.fileHndl, false, !fwParams.shortErrors)) {
            WriteToErrBuff(fwParams.errBuff, (*ioAccessP)->err(), fwParams.errBuffSize);
            delete *ioAccessP;
            return false;
        }
#endif
    } else if (fwParams.hndlType == FHT_FW_BUFF) {
        u_int32_t numInfo = fwParams.buffSize;
#ifndef NO_MFA_SUPPORT
        int sig = getBufferSignature((u_int8_t*)fwParams.buffHndl, numInfo);
        if (sig == IMG_SIG_TYPE_BIN) {
            *ioAccessP = new FImage;
            if (!((FImage*)*ioAccessP)->open(fwParams.buffHndl, (u_int32_t)numInfo, !fwParams.shortErrors)) {
                WriteToErrBuff(fwParams.errBuff, (*ioAccessP)->err(), fwParams.errBuffSize);
                delete *ioAccessP;
                return false;
            }
        } else if (sig == IMG_SIG_TYPE_MFA) {
            u_int8_t *imgbuf;
            int sz;
            // get image from mfa file
            if ((sz = getMfaImg((u_int8_t*)fwParams.buffHndl, numInfo, fwParams.psid, &imgbuf, fwParams.errBuff, fwParams.errBuffSize)) < 0) {
                return false;
            }
            *ioAccessP = new FImage;
            if (!((FImage*)*ioAccessP)->open((u_int32_t*)imgbuf, (u_int32_t)sz, !fwParams.shortErrors)) {
                mfa_release_image(imgbuf);
                WriteToErrBuff(fwParams.errBuff, (*ioAccessP)->err(), fwParams.errBuffSize);
                delete *ioAccessP;
                return false;
            }
            mfa_release_image(imgbuf);
        } else {
            WriteToErrBuff(fwParams.errBuff, "Invalid Image signature.", fwParams.errBuffSize);
            return false;//Unknown signature
        }
#else
        *ioAccessP = new FImage;
        if (!((FImage*)*ioAccessP)->open(fwParams.buffHndl, numInfo, !fwParams.shortErrors)) {
            WriteToErrBuff(fwParams.errBuff, (*ioAccessP)->err(), fwParams.errBuffSize);
            delete *ioAccessP;
            return false;
        }
#endif
    } else if (fwParams.hndlType == FHT_UEFI_DEV) {
        *ioAccessP = new Flash;
        if (!((Flash*)*ioAccessP)->open(fwParams.uefiHndl, fwParams.uefiExtra, false, !fwParams.shortErrors)) {
            WriteToErrBuff(fwParams.errBuff, (*ioAccessP)->err(), fwParams.errBuffSize);
            delete *ioAccessP;
            return false;
        }
    } else if (fwParams.hndlType == FHT_MST_DEV) {
        *ioAccessP = new Flash;
        if (!((Flash*)*ioAccessP)->open(fwParams.mstHndl, fwParams.forceLock, fwParams.readOnly, fwParams.numOfBanks, \
                                        fwParams.flashParams, fwParams.ignoreCacheRep, !fwParams.shortErrors, fwParams.cx3FwAccess)) {
            // TODO: release memory here ?
            WriteToErrBuff(fwParams.errBuff, (*ioAccessP)->err(), fwParams.errBuffSize);
            delete *ioAccessP;
            return false;
        }
        //set no flash verify if needed (default =false)
        ((Flash*)*ioAccessP)->set_no_flash_verify(fwParams.noFlashVerify);
        // work with 64KB sector size if possible to increase performace in full fw burn
        (((Flash*)*ioAccessP)->set_flash_working_mode(Flash::Fwm_64KB));
    } else {
        WriteToErrBuff(fwParams.errBuff, "Unknown Handle Type.", fwParams.errBuffSize);
        return false;
    }
    return true;
}

u_int8_t FwOperations::IsFS4Image(FBase& f, u_int32_t *found_images)
{
    u_int32_t data;
    u_int8_t image_version;
    u_int32_t image_start[CNTX_START_POS_SIZE] = {0};

    FindAllImageStart(&f, image_start, found_images, _fs4_magic_pattern);

    if (*found_images) {
        //check if the image_format_version is ok
        READ4_NOERRMSG(f, image_start[0] + 0x10, &data);
        TOCPU1(data);
        image_version = data >> 24;
        if (image_version == 1) {//1 is the current version
            return FS_FS4_GEN;
        } else {
            return FS_UNKNOWN_IMG;
        }
    }

    return FS_UNKNOWN_IMG;
}

u_int8_t FwOperations::IsFS3OrFS2Image(FBase& f, u_int32_t *found_images)
{
    u_int32_t data;
    u_int8_t image_version;
    u_int32_t image_start[CNTX_START_POS_SIZE] = {0};
    FindAllImageStart(&f, image_start, found_images, _cntx_magic_pattern);
    if (found_images) {
        READ4_NOERRMSG(f, image_start[0] + FS3_IND_ADDR, &data);
        TOCPU1(data);
        image_version = data >> 24;
        if (image_version == IMG_VER_FS3) {
            return FS_FS3_GEN;
        } else {
            // TODO: if the img format version is unknown we should fail instead of considering it FS2
            return FS_FS2_GEN;
        }
    }
    return FS_UNKNOWN_IMG;
}

u_int8_t FwOperations::IsCableImage(FBase& f)
{
    char data[5] = {0};
    READ4_NOERRMSG(f, 0, (u_int32_t*)&data);
    if (!strncmp(data, "MTCF", 4)) {
        return FS_FC1_GEN;
    }
    return FS_UNKNOWN_IMG;
}

u_int8_t FwOperations::CheckFwFormat(FBase& f, bool getFwFormatFromImg)
{
    u_int8_t v;
    u_int32_t found_images = 0;

    if (f.is_flash() && !getFwFormatFromImg) {
        return GetFwFormatFromHwDevID(((Flash*)&f)->get_dev_id());
    } else {
        v = IsCableImage(f);
        if (v != FS_UNKNOWN_IMG) {
            return v;
        }
        //First check if it is FS4
        v = IsFS4Image(f, &found_images);
        if (found_images) {
            return v;
        }
        //If not FS4 then check if it is FS3 or FS2
        return IsFS3OrFS2Image(f, &found_images);
    }
    return FS_UNKNOWN_IMG;
}


bool FwOperations::CheckBinVersion(u_int8_t binVerMajor, u_int8_t binVerMinor)
{
    if (binVerMajor == 0 && binVerMinor == 0) {
        return true;
    }

    if (binVerMajor > _maxBinMajorVer /*FS4_MAX_BIN_VER_MAJOR*/) {
        return errmsg(MLXFW_UNSUPPORTED_BIN_VER_ERR,
                      "Unsupported binary version (%d.%d) please update to latest MFT package",
                      binVerMajor,
                      binVerMinor);
    }
    if (binVerMajor < _minBinMajorVer/*FS4_MIN_BIN_VER_MAJOR*/
        || (binVerMajor == _minBinMajorVer    /*FS4_MIN_BIN_VER_MAJOR*/
            && binVerMinor < _minBinMinorVer)) {
        return errmsg(MLXFW_UNSUPPORTED_BIN_VER_ERR,
                      "Unsupported binary version (%d.%d) minimal supported version (%d.%d)", \
                      binVerMajor,
                      binVerMinor,
                      _minBinMajorVer /*FS4_MIN_BIN_VER_MAJOR*/,
                      _minBinMinorVer /*FS4_MIN_BIN_VER_MINOR*/);
    }

    return true;
}

FwOperations* FwOperations::FwOperationsCreate(void *fwHndl, void *info, char *psid, fw_hndl_type_t hndlType, char *errBuff, int buffSize)
{
    fw_ops_params_t fwParams;
    memset(&fwParams, 0, sizeof(fwParams));
    fwParams.psid = psid;
    fwParams.hndlType = hndlType;
    fwParams.errBuff = errBuff;
    fwParams.errBuffSize = buffSize;
    fwParams.shortErrors = true;

    if (hndlType == FHT_FW_FILE) {
        fwParams.fileHndl = (char*)fwHndl;
    } else if (hndlType == FHT_FW_BUFF) {
        fwParams.buffHndl = (u_int32_t*)fwHndl;
        fwParams.buffSize = *((u_int32_t*)info);
    } else if (hndlType == FHT_UEFI_DEV) {
        fwParams.uefiHndl = (uefi_Dev_t*)fwHndl;
        fwParams.uefiExtra = (uefi_dev_extra_t*)info;
    } else if (hndlType == FHT_MST_DEV) {
        fwParams.mstHndl = (char*)fwHndl;
        fwParams.forceLock = false;
        fwParams.readOnly = false;
        fwParams.numOfBanks = -1;
        fwParams.flashParams = (flash_params_t*)NULL;
        fwParams.ignoreCacheRep = 0;
        fwParams.noFlashVerify = false;
        fwParams.cx3FwAccess = 0;
    } else if (hndlType == FHT_CABLE_DEV) {
        fwParams.buffHndl = (u_int32_t*)fwHndl;
    }
    return FwOperationsCreate(fwParams);
}

bool FwOperations::imageDevOperationsCreate(fw_ops_params_t& devParams, fw_ops_params_t& imgParams, FwOperations **devFwOps, FwOperations **imgFwOps)
{
    *imgFwOps = FwOperationsCreate(imgParams);
    if (!(*imgFwOps)) {
        return false;
    }

    if ((*imgFwOps)->FwType() == FIT_FS2) {
        *devFwOps = FwOperationsCreate(devParams);
        if (!(*devFwOps)) {
            return false;
        }
        return true;
    }

    fw_info_t imgQuery;
    memset(&imgQuery, 0, sizeof(fw_info_t));
    if (!(*imgFwOps)->FwQuery(&imgQuery)) {
        return false;
    }
    if (imgQuery.fs3_info.security_mode == SM_NONE) {
        devParams.noFwCtrl = true;
    }
    *devFwOps = FwOperationsCreate(devParams);
    if (!(*devFwOps)) {
        return false;
    }
    return true;
}

void FwOperations::BackUpFwParams(fw_ops_params_t& fwParams)
{
    _fwParams.hndlType = fwParams.hndlType;
    _fwParams.buffHndl = fwParams.buffHndl;
    _fwParams.buffSize = fwParams.buffSize;
    _fwParams.cx3FwAccess = fwParams.cx3FwAccess;
    _fwParams.errBuff = (char*)NULL;
    _fwParams.errBuffSize = 0;
    _fwParams.fileHndl = (fwParams.hndlType == FHT_FW_FILE && fwParams.fileHndl) ? \
                         strncpy((char*)(new char[(strlen(fwParams.fileHndl) + 1)]), fwParams.fileHndl, strlen(fwParams.fileHndl) + 1) : (char*)NULL;
    // no support for flash params
    _fwParams.flashParams = (flash_params_t*)NULL;
    _fwParams.forceLock = fwParams.forceLock;
    _fwParams.ignoreCacheRep = fwParams.ignoreCacheRep;
    _fwParams.mstHndl = (fwParams.hndlType == FHT_MST_DEV && fwParams.mstHndl) ? \
                        strncpy((char*)(new char[(strlen(fwParams.mstHndl) + 1)]), fwParams.mstHndl, strlen(fwParams.mstHndl) + 1) : (char*)NULL;
    _fwParams.noFlashVerify = fwParams.noFlashVerify;
    _fwParams.numOfBanks = fwParams.numOfBanks;
    _fwParams.psid = fwParams.psid ? strncpy((char*)(new char[(strlen(fwParams.psid) + 1)]), fwParams.psid, strlen(fwParams.psid) + 1) : (char*)NULL;
    _fwParams.readOnly = fwParams.readOnly;
    _fwParams.shortErrors = fwParams.shortErrors;
    _fwParams.uefiExtra = fwParams.uefiExtra;
    _fwParams.uefiHndl = fwParams.uefiHndl;
    _fwParams.isCableFw = fwParams.isCableFw;
}

FwOperations* FwOperations::FwOperationsCreate(fw_ops_params_t& fwParams)
{
    FwOperations *fwops;
    u_int8_t fwFormat;
    FBase *ioAccess = (FBase*)NULL;
    FwCompsMgr *fwCompsAccess = (FwCompsMgr*)NULL;
    bool getFwFormatFromImg = false;
#ifdef CABLES_SUPP
    if (fwParams.hndlType == FHT_CABLE_DEV) {
        fwops = new CableFwOperations(fwParams.mstHndl);
        if (!fwops->FwInit()) {
            WriteToErrBuff(fwParams.errBuff, fwops->err(), fwParams.errBuffSize);
            delete fwops;
            return (FwOperations*)NULL;
        }
        fwops->_devName = strcpy(new char[strlen(fwParams.mstHndl) + 1], fwParams.mstHndl);
    } else
#endif
    {
        if ((!fwParams.ignoreCacheRep && !fwParams.noFwCtrl && fwParams.hndlType == FHT_MST_DEV) ||
            (fwParams.hndlType == FHT_UEFI_DEV     &&
             fwParams.uefiExtra != NULL           &&
             fwParams.uefiExtra->dev_info != NULL &&
             !fwParams.uefiExtra->dev_info->no_fw_ctrl)) {
            if (fwParams.hndlType == FHT_MST_DEV) {
                fwCompsAccess = new FwCompsMgr(fwParams.mstHndl);
            } else if (fwParams.hndlType == FHT_UEFI_DEV) {
                fwCompsAccess = new FwCompsMgr(fwParams.uefiHndl, fwParams.uefiExtra);
            }
            fw_comps_error_t fwCompsErr = fwCompsAccess->getLastError();
            if (fwCompsErr != FWCOMPS_SUCCESS) {
                delete fwCompsAccess;
                fwCompsAccess = (FwCompsMgr*) NULL;
                if (fwCompsErr == FWCOMPS_MTCR_OPEN_DEVICE_ERROR) {
                    // mtcr lib failed to open the provided device.
                    WriteToErrBuff(fwParams.errBuff, "Failed to open device", fwParams.errBuffSize);
                    return (FwOperations*)NULL;
                }
            } else {
                fwInfoT fwInfo;
                if (fwParams.forceLock) {
                    fwCompsAccess->forceRelease();
                }
                if (fwParams.mccUnsupported &&
                    fwCompsAccess->queryFwInfo(&fwInfo) == true &&
                    fwInfo.security_type.secure_fw == 0) {
                    delete fwCompsAccess;
                    fwCompsAccess = (FwCompsMgr*) NULL;
                } else {
                    fwFormat = FS_FSCTRL_GEN;
                    goto init_fwops;
                }
            }

        }
        if (!FwAccessCreate(fwParams, &ioAccess)) {
            return (FwOperations*)NULL;
        }
        if (fwParams.hndlType == FHT_UEFI_DEV) {
            // IN UEFI we don't have an access to read devID from cr-space so we are reading it from FW  image signature
            getFwFormatFromImg = true;
        }

        fwFormat = CheckFwFormat(*ioAccess, getFwFormatFromImg);
init_fwops:
        switch (fwFormat) {
        case FS_FS2_GEN: {
                fwops = new Fs2Operations(ioAccess);
                break;
            }

        case FS_FS3_GEN: {
                fwops = new Fs3Operations(ioAccess);
                break;
            }

        case FS_FS4_GEN: {
                fwops = new Fs4Operations(ioAccess);
                break;
            }

        case FS_FSCTRL_GEN: {
                if (!fwCompsAccess) {
                    return (FwOperations*)NULL;
                }
                fwops = new FsCtrlOperations(fwCompsAccess);
                break;
            }

#ifdef CABLES_SUPP
        case FS_FC1_GEN: {
                fwops = new CableFwOperations(ioAccess);
                break;
            }

#endif
        default:
            delete ioAccess;
            WriteToErrBuff(fwParams.errBuff, "invalid Firmware Format (found FS Gen 1)", fwParams.errBuffSize);
            return (FwOperations*)NULL;
        }
        // save initialization parameters
        fwops->BackUpFwParams(fwParams);

        fwops->_advErrors = !fwParams.shortErrors;
        fwops->FwInit();
        if (fwParams.hndlType == FHT_FW_FILE) {
            fwops->_fname = strcpy(new char[strlen(fwParams.fileHndl) + 1], fwParams.fileHndl);
        }
        if (fwParams.hndlType == FHT_MST_DEV) {
            fwops->_devName = strcpy(new char[strlen(fwParams.mstHndl) + 1], fwParams.mstHndl);
        }
    }
    return fwops;
}

u_int32_t FwOperations::CalcImageCRC(u_int32_t *buff, u_int32_t size)
{
    Crc16 crc;
    TOCPUn(buff, size);
    CRCn(crc, buff, size);
    CPUTOn(buff, size);
    crc.finish();
    u_int32_t new_crc = crc.get();
    return new_crc;
}

bool FwOperations::writeImageEx(ProgressCallBackEx progressFuncEx, void *progressUserData, ProgressCallBack progressFunc, u_int32_t addr, void *data, int cnt, bool isPhysAddr, bool readModifyWrite, int totalSz, int alreadyWrittenSz)
{
    u_int8_t   *p = (u_int8_t*)data;
    u_int32_t curr_addr = addr;
    u_int32_t towrite = cnt;
    totalSz = totalSz == -1 ? cnt : totalSz;
    int origFlashWorkingMode = Flash::Fwm_Default;
    bool rc;
    while (towrite) {
        // Write
        int trans;
        if (_ioAccess->is_flash()) {
            if (readModifyWrite) {
                // perform write with the smallest supported sector size
                origFlashWorkingMode = ((Flash*)_ioAccess)->get_flash_working_mode();
                ((Flash*)_ioAccess)->set_flash_working_mode(Flash::Fwm_Default);
            }
            trans = (towrite > (int)Flash::TRANS) ? (int)Flash::TRANS : towrite;
            if (isPhysAddr) {
                if (readModifyWrite) {
                    rc = ((Flash*)_ioAccess)->read_modify_write_phy(curr_addr, p, trans);
                } else {
                    rc = ((Flash*)_ioAccess)->write_phy(curr_addr, p, trans);
                }
            } else {
                if (readModifyWrite) {
                    rc = ((Flash*)_ioAccess)->read_modify_write(curr_addr, p, trans);
                } else {
                    rc = ((Flash*)_ioAccess)->write(curr_addr, p, trans);
                }
            }
            if (readModifyWrite) {
                // restore erase sector size
                ((Flash*)_ioAccess)->set_flash_working_mode(origFlashWorkingMode);
            }
            if (!rc) {
                return errmsg(MLXFW_FLASH_WRITE_ERR, "Flash write failed: %s", _ioAccess->err());
            }
        } else {
            trans = towrite;
            if (!((FImage*)_ioAccess)->write(curr_addr, p, trans)) {
                return errmsg("%s", _ioAccess->err());
            }
        }
        p += trans;
        curr_addr += trans;
        towrite -= trans;

        // Report
        if (progressFunc != NULL || progressFuncEx != NULL) {
            u_int32_t new_perc = ((cnt - towrite + alreadyWrittenSz) * 100) / totalSz;
            if (progressFunc != NULL && progressFunc((int)new_perc)) {
                return errmsg("Aborting... received interrupt signal");
            }
            if (progressFuncEx != NULL && progressFuncEx((int)new_perc, progressUserData)) {
                return errmsg("Aborting... received interrupt signal");
            }
        }
    }
    return true;
} //  Flash::WriteImage


bool FwOperations::writeImage(ProgressCallBack progressFunc, u_int32_t addr, void *data, int cnt, bool isPhysAddr, bool readModifyWrite, int totalSz, int alreadyWrittenSz)
{
    return writeImageEx((ProgressCallBackEx)NULL, NULL, progressFunc, addr, data, cnt, isPhysAddr, readModifyWrite, totalSz, alreadyWrittenSz);
}

bool FwOperations::CheckMac(u_int64_t mac)
{
    if ((mac >> 40) & 0x1) {
        return errmsg("Multicast bit (bit 40) is set");
    }

    if (mac >> 48) {
        return errmsg("More than 48 bits are used");
    }

    return true;
}

bool FwOperations::CheckMac(guid_t mac)
{
    u_int64_t bigMac = ((u_int64_t)mac.h << 32 & 0xffffffff00000000ULL) | ((u_int64_t)mac.l & 0x00000000ffffffffULL );
    return CheckMac(bigMac);
}

void FwOperations::recalcSectionCrc(u_int8_t *buf, u_int32_t data_size)
{

    Crc16 crc;
    u_int32_t crcRes;
    for (u_int32_t i = 0; i < data_size; i += 4) {
        crc << __be32_to_cpu(*(u_int32_t*)(buf + i));
    }
    crc.finish();
    crcRes = crc.get();
    *((u_int32_t*)(buf + data_size)) = __cpu_to_be32(crcRes);
}

chip_type_t FwOperations::getChipType()
{
    int i = 0;
    while (hwDevData[i].name != NULL) {
        int j = 0;
        while (hwDevData[i].swDevIds[j] != 0) {
            if (hwDevData[i].swDevIds[j] == _fwImgInfo.ext_info.dev_type) {
                return hwDevData[i].chipType;
            }
            j++;
        }
        i++;
    }
    return CT_UNKNOWN;
}

bool FwOperations::getInfoFromHwDevid(u_int32_t hwDevId, chip_type_t& chipT, const u_int32_t **swIds)
{
    int i = 0;
    while (hwDevData[i].name != NULL) {
        if (hwDevData[i].hwDevId == hwDevId) {
            chipT = hwDevData[i].chipType;
            *swIds = hwDevData[i].swDevIds;
            return true;
        }
        i++;
    }
    return errmsg(MLXFW_DEV_ID_ERR, "Failed to identify device ID(MT%d).", hwDevId);
}

FwOperations::HwDevData FwOperations::getInfoFromChipType(chip_type_t chipT) const
{
    int i = 0;
    while (hwDevData[i].name != NULL) {
        if (hwDevData[i].chipType == chipT) {
            return hwDevData[i];
        }
        i++;
    }
    return hwDevData[i];
}

// TODO:combine both databases(hwDevData and hwDev2Str) and remove old unsupporded devices i.e infinihost infinihost_iii_ex infinihost_iii_lx
const FwOperations::HwDevData FwOperations::hwDevData[] = {
    { "ConnectX",          CX2_HW_ID, CT_CONNECTX, CFT_HCA, 2,  {25408, 25418, 26418, 26438,
                                                                 26428, 25448, 26448, 26468,
                                                                 25458, 26458, 26478, 26488,
                                                                 4097, 4098, 0}, {{UNKNOWN_BIN, {0}}}},
    { "ConnectX-3",        CX3_HW_ID, CT_CONNECTX, CFT_HCA, 2,  {4099, 4100, 4101, 4102,
                                                                 4104, 4105, 4106,
                                                                 4107, 4108, 4109, 4110,
                                                                 4111, 4112, 0}, {{UNKNOWN_BIN, {0}}}},
    { "ConnectX-3Pro",    CX3_PRO_HW_ID, CT_CONNECTX, CFT_HCA, 2, {4103, 0}, {{UNKNOWN_BIN, {0}}}},
    { "Connect_IB",       CONNECT_IB_HW_ID, CT_CONNECT_IB, CFT_HCA, 2, {CONNECT_IB_SW_ID, 4114, 4115, 4116,
                                                                        4117, 4118, 4119, 4120,
                                                                        4121, 4122, 4123, 4124, 0}, {{UNKNOWN_BIN, {0}}}},
    { "InfiniScale IV",   IS4_HW_ID,        CT_IS4,          CFT_SWITCH,  0, {48436, 48437, 48438, 0}, {{UNKNOWN_BIN, {0}}}},
    { "SwitchX",          SWITCHX_HW_ID,    CT_SWITCHX,      CFT_SWITCH,  0, {51000, 0}, {{UNKNOWN_BIN, {0}}}},
    { "Switch_IB",        SWITCH_IB_HW_ID,  CT_SWITCH_IB,    CFT_SWITCH,  0, {52000, 0}, {{UNKNOWN_BIN, {0}}}},
    { "ConnectX-4",       CX4_HW_ID,        CT_CONNECTX4,    CFT_HCA,     0, {4115, 0}, {{UNKNOWN_BIN, {0}}}},
    { "ConnectX-4LX",     CX4LX_HW_ID,      CT_CONNECTX4_LX, CFT_HCA,     0, {4117, 0}, {{UNKNOWN_BIN, {0}}}},
    { "ConnectX-5",       CX5_HW_ID,        CT_CONNECTX5,    CFT_HCA,     0, {4119, 4121, 0}, {{CX5_LOW_BIN, {4119, 0}},
                                                                                               {CX5_HIGH_BIN, {4119, 4121, 0}},
                                                                                               {UNKNOWN_BIN, {0}}}},
    { "ConnectX-6",       CX6_HW_ID,        CT_CONNECTX6,    CFT_HCA,     0, {4123, 0}, {{UNKNOWN_BIN, {0}}}},
    { "BlueField",        BF_HW_ID,         CT_BLUEFIELD,    CFT_HCA,     0, {41680, 41681, 41682, 0}, {{UNKNOWN_BIN, {0}}}},
    { "Spectrum",         SPECTRUM_HW_ID,   CT_SPECTRUM,     CFT_SWITCH,  0, {52100, 0}, {{UNKNOWN_BIN, {0}}}},
    { "Switch_IB2",       SWITCH_IB2_HW_ID, CT_SWITCH_IB2,   CFT_SWITCH,  0, {53000, 0}, {{UNKNOWN_BIN, {0}}}},
    { "Quantum",          QUANTUM_HW_ID,    CT_QUANTUM,      CFT_SWITCH,  0, {54000, 0}, {{UNKNOWN_BIN, {0}}}},
    { (char*)NULL,       0,                CT_UNKNOWN,      CFT_UNKNOWN, 0, {0}, {{UNKNOWN_BIN, {0}}}},// zero devid terminator
};

const FwOperations::HwDev2Str FwOperations::hwDev2Str[] = {
    {"ConnectX-2",        CX2_HW_ID,        0xB0},
    {"ConnectIB",         CONNECT_IB_HW_ID, 0x00},
    {"ConnectX-3 A0",     CX3_HW_ID,        0x00},
    {"ConnectX-3 A1",     CX3_HW_ID,        0x01},
    {"ConnectX-3Pro",     CX3_PRO_HW_ID,    0x00},
    {"ConnectX-4",        CX4_HW_ID,        0x00},
    {"ConnectX-4LX",      CX4LX_HW_ID,      0x00},
    {"ConnectX-5",        CX5_HW_ID,        0x00},
    {"ConnectX-6",        CX6_HW_ID,        0x00},
    {"BlueField",         BF_HW_ID,         0x00},
    {"SwitchX A0",        SWITCHX_HW_ID,    0x00},
    {"SwitchX A1",        SWITCHX_HW_ID,    0x01},
    {"InfiniScale IV A0", IS4_HW_ID,        0xA0},
    {"InfiniScale IV A1", IS4_HW_ID,        0xA1},
    {"SwitchIB A0",       SWITCH_IB_HW_ID,  0x00},
    {"Spectrum A0",       SPECTRUM_HW_ID,   0x00},
    {"SwitchIB2 A0",      SWITCH_IB2_HW_ID, 0x00},
    {"Quantum A0",        QUANTUM_HW_ID,    0x00},
    {"Spectrum A1",       SPECTRUM_HW_ID,   0x01},
    { (char*)NULL,       (u_int32_t)0, (u_int8_t)0x00},      // zero device ID terminator
};

#define ARR_SIZE(arr) sizeof(arr) / sizeof(arr[0])
#define MAX_HW_NAME_LEN 100
bool FwOperations::HWIdRevToName(u_int32_t hw_id, u_int8_t rev_id, char *hw_name)
{

    for (int i = 0; hwDev2Str[i].hwDevId != 0; i++) {
        const HwDev2Str *hwDev2StrMem = &(hwDev2Str[i]);

        if (hwDev2StrMem->hwDevId == hw_id && hwDev2StrMem->revId == rev_id) {
            int len = strlen(hwDev2StrMem->name);
            if (len >= MAX_HW_NAME_LEN) {
                return errmsg("Internal error: Length of device name: %d exceeds the maximum allowed size: %d", len, MAX_HW_NAME_LEN - 1);
            }
            strcpy(hw_name, hwDev2StrMem->name);
            return true;
        }
    }
    // When the device or rev is unknown we use the hw ID and rev to display it.
    sprintf(hw_name, "MT%d-%02X", hw_id, rev_id);
    return true;
}


// This function gets the HW ID of the target device and the dev ID from
// the image. It then matches the 2 IDs and returns an error in case of
// mismatch. The match is not 1:1 , since the FW image contains the SW
// dev id, and a single hw dev id may match multiple SW dev IDs.
//
bool FwOperations::CheckMatchingHwDevId(u_int32_t hwDevId, u_int32_t rev_id, u_int32_t *supportedHwId, u_int32_t supportedHwIdNum)
{

    char supp_hw_id_list[MAX_NUM_SUPP_HW_LIST_STR] = {'\0'};
    char supp_hw_id_list_tmp[MAX_NUM_SUPP_HW_LIST_STR];
    char curr_hw_id_name[MAX_HW_NAME_LEN];

    for (u_int32_t i = 0; i < supportedHwIdNum; i++) {
        u_int32_t currSupportedHwId = supportedHwId[i];
        u_int32_t supp_hw_id  = currSupportedHwId & 0xffff;
        u_int32_t supp_rev_id = (currSupportedHwId >> 16) & 0xff;
        u_int32_t tmp_size_of_list;
        char hw_name[MAX_HW_NAME_LEN];

        if (currSupportedHwId == 0) {
            break;
        }
        // Check if device is supported!
        if (supp_hw_id == hwDevId && supp_rev_id == rev_id) {
            return true;
        }
        // Append checked to list of supported device in order to print it in the error if we this device is not supported

        // Get the HW name of current supported HW ID
        if (!HWIdRevToName(supp_hw_id, supp_rev_id, hw_name)) {
            return false;
        }
        // Check if we don't exceed the array size we have
        tmp_size_of_list = strlen(supp_hw_id_list) + strlen(hw_name) + 2;
        if (tmp_size_of_list >= MAX_NUM_SUPP_HW_LIST_STR) {
            return errmsg("Internal error: Size of supported devs list: %d exceeds the maximum allowed size: %d",
                          tmp_size_of_list, MAX_NUM_SUPP_HW_LIST_STR - 1);
        }

        if (supp_hw_id_list[0] == '\0') {
            sprintf(supp_hw_id_list, "%s", hw_name);
        } else {
            strcpy(supp_hw_id_list_tmp, supp_hw_id_list);
            sprintf(supp_hw_id_list, "%s, %s", supp_hw_id_list_tmp, hw_name);
        }
    }
    // If we get here, this FW cannot be burnt in the current device.
    // Get the Device name
    if (!HWIdRevToName(hwDevId, rev_id, curr_hw_id_name)) {
        return false;
    }

    return errmsg(MLXFW_DEVICE_IMAGE_MISMATCH_ERR, "FW image file cannot be programmed to device %s, it is intended for: %s only",
                  curr_hw_id_name, supp_hw_id_list);
}
bool FwOperations::CheckMatchingDevId(u_int32_t hwDevId, u_int32_t imageDevId)
{

    const HwDevData *devData = (const HwDevData*)NULL;
    const char *hwDevName = (const char*)NULL;

    // First, find the HW device that the SW id matches
    for (int i = 0; hwDevData[i].hwDevId != 0; i++) {
        if (hwDevData[i].hwDevId == hwDevId) {
            hwDevName = hwDevData[i].name; // TODO: Check bug if device not found
        }

        if (devData == NULL) {
            for (int j = 0; hwDevData[i].swDevIds[j]; j++) {
                if (hwDevData[i].swDevIds[j] == imageDevId) {
                    devData = &hwDevData[i];
                    break;
                }
            }
        }
    }

    if (devData == NULL) {
        report_warn("Unknown device id (%d) in the given FW image. Skipping HW match check.\n",
                    imageDevId);
        return true;
    } else if (devData->hwDevId != hwDevId) {
        return errmsg("Trying to burn a \"%s\" image on a \"%s\" device.",
                      devData->name,
                      hwDevName);
    }

    return true;
}

bool FwOperations::CheckMatchingBinning(u_int32_t hwDevId, BinIdT binningVal, u_int32_t imageDevId)
{
    const HwDevData *devData = (const HwDevData*)NULL;

    // First, find the HW device that the Hw id matches
    for (int i = 0; hwDevData[i].hwDevId != 0; i++) {
        if (hwDevData[i].hwDevId == hwDevId) {
            devData = &hwDevData[i];
        }
    }
    if (devData != NULL) {
        // Find the bin speed and match its SW ID to the ImageDevID
        for (int j = 0; devData->binningId[j].binId != UNKNOWN_BIN; j++) {
            if (devData->binningId[j].binId == binningVal) {
                int k = 0;
                while (devData->binningId[j].swId[k] != 0) {
                    if (imageDevId == devData->binningId[j].swId[k]) {
                        return true;
                    }
                    k++;
                }
                return errmsg("FW image does not match the device bin info.\n");
            }
        }
    }
    return true;
}

void FwOperations::FwDebugPrint(char *str)
{
    if (_printFunc != NULL) {
        _printFunc(str);
    }
}

bool FwOperations::FwSetPrint(PrintCallBack PrintFunc)
{
    _printFunc = PrintFunc;
    return true;
}

bool FwOperations::CheckPSID(FwOperations &imageOps, u_int8_t allow_psid_change)
{
    if (!allow_psid_change) {
        if (strncmp( _fwImgInfo.ext_info.psid, imageOps._fwImgInfo.ext_info.psid, PSID_LEN)) {
            return errmsg(MLXFW_PSID_MISMATCH_ERR, "Image PSID is %s, it cannot be burnt into current device (PSID: %s)",
                          imageOps._fwImgInfo.ext_info.psid, _fwImgInfo.ext_info.psid);
        }
    }
    return true;
}

bool FwOperations::CheckFwVersion(FwOperations &imageOps, u_int8_t forceVersion)
{
    FwVerInfo updateRequired;
    if (!forceVersion) {
        updateRequired = FwVerLessThan(_fwImgInfo.ext_info.fw_ver, imageOps._fwImgInfo.ext_info.fw_ver);
        if (updateRequired != FVI_SMALLER) {
            return errmsg(MLXFW_FW_ALREADY_UPDATED_ERR, "FW is already updated.");
        }
    }
    return true;
}

bool FwOperations::FwSwReset()
{
    if (!_ioAccess->is_flash()) {
        return errmsg("operation supported only for switch devices: InfiniScaleIV SwitchX and SwitchIB over an IB interface");
    }
    if (!((Flash*)_ioAccess)->sw_reset()) {
        return errmsg("%s",  _ioAccess->err());
    }
    return true;
}

void FwOperations::WriteToErrBuff(char *errBuff, const char *errStr, int bufSize)
{
    if (bufSize > 0) {
        if (bufSize > (int)strlen(errStr)) {
            strncpy(errBuff, errStr, bufSize);
        } else {
            strncpy(errBuff, errStr, bufSize - 4);
            strcpy(&errBuff[bufSize - 4], "...");
        }
    }
    return;
}

bool FwOperations::UpdateImgCache(u_int8_t *buff, u_int32_t addr, u_int32_t size)
{
    //avoid compiler warrnings
    (void)buff;
    (void)addr;
    (void)size;
    //in FS2 we dont have ImgCache, just in FS3 so we define a defult behaviour.
    return true;
}

bool FwOperations::CntxEthOnly(u_int32_t devid)
{
    return (devid == 25448) ||      // ETH
           (devid == 26448) ||      // ETH
           (devid == 25458) ||      //
           (devid == 26458) ||      //
           (devid == 26468) ||
           (devid == 26478);
}

// RomInfo implementation

FwOperations::RomInfo::RomInfo(const std::vector<u_int8_t>& romSector, bool resEndi)
{
    expRomFound = !romSector.empty();
    romSect = romSector;
    if (resEndi) {
        TOCPUn(&romSect[0], romSect.size() / 4);
    }
    numOfExpRom = 0;
    expRomComDevid = 0;
    expRomWarning = false;
    expRomErrMsgValid = false;
    noRomChecksum = false;
    memset(expRomErrMsg, 0, sizeof(expRomErrMsg));
    memset(expRomWarningMsg, 0, sizeof(expRomWarningMsg));
    memset(&romsInfo, 0, (sizeof(rom_info_t) * MAX_ROMS_NUM));
}


bool FwOperations::RomInfo::initRomsInfo(roms_info_t * info)
{
    if (info == NULL) {
        return errmsg("invalid roms_info_t pointer.");
    }
    info->exp_rom_found = expRomFound;
    info->num_of_exp_rom = numOfExpRom;
    info->no_rom_checksum = noRomChecksum;
    info->exp_rom_com_devid = expRomComDevid;
    info->exp_rom_warning = expRomWarning;
    info->exp_rom_err_msg_valid = expRomErrMsgValid;
    //copy strings and rom_info
    for (int i = 0; i < MAX_ROM_ERR_MSG_LEN; i++) {
        info->exp_rom_warning_msg[i] = expRomWarningMsg[i];
        info->exp_rom_err_msg[i] = expRomErrMsg[i];
    }
    for (int i = 0; i < MAX_ROMS_NUM; i++) {
        //copy rom_info struct
        info->rom_info[i].exp_rom_product_id = romsInfo[i].exp_rom_product_id; // 0 - invalid.
        info->rom_info[i].exp_rom_dev_id = romsInfo[i].exp_rom_dev_id;
        info->rom_info[i].exp_rom_supp_cpu_arch = romsInfo[i].exp_rom_supp_cpu_arch;
        info->rom_info[i].exp_rom_port = romsInfo[i].exp_rom_port;
        info->rom_info[i].exp_rom_proto = romsInfo[i].exp_rom_proto;
        info->rom_info[i].exp_rom_num_ver_fields = romsInfo[i].exp_rom_num_ver_fields;
        for (int j = 0; j < 3; j++) {
            info->rom_info[i].exp_rom_ver[j] = romsInfo[i].exp_rom_ver[j];
        }
    }

    return true;
}

bool FwOperations::RomInfo::ParseInfo()
{
    if (!GetExpRomVersion()) {
        snprintf(expRomErrMsg, MAX_ROM_ERR_MSG_LEN, "%s",  err());
        expRomErrMsgValid = true;
        //printf("-D-expRomErrMsg: %s \n", expRomErrMsg);
    }
    //printf("-D- expRomFound: %d   \n",expRomFound);
    //printf("-D- numOfExpRom: %d   \n",numOfExpRom);
    //printf("-D- noRomChecksum: %d   \n",noRomChecksum);
    //printf("-D- expRomComDevid: %d   \n",expRomComDevid);
    //printf("-D- expRomWarning: %d   \n",expRomWarning);
    //printf("-D- expRomErrMsgValid: %d   \n",expRomErrMsgValid);
    return true;
}

#define MAGIC_LEN 32

bool FwOperations::RomInfo::GetExpRomVersion()
{
    char magicString[MAGIC_LEN] = {"mlxsignX"};
    u_int32_t magicLen = strlen(magicString);
    bool magicFound = false;
    u_int32_t verOffset;
    u_int32_t romChecksumRange;

    // We do this HACK in order not to have mlxsign: word in our code so if mlxfwops will be part
    // of rom, no mlxsign: string will appear
    magicString[magicLen - 1] = ':';

    if (romSect.empty()) {
        return errmsg("Expansion Rom section not found.");
    }
    // When checking the version of the expansion rom, only the first image has
    // to be checked. This is because the second image  the uefi image does not
    // have to comply with checksumming to 0. To do this you have to read  byte
    // 2 (third) of the image  and multiply by 512 to get the size of the x86
    // image.

    // Checksum:
    if (romSect.size() < 4) {
        return errmsg("ROM size (0x%x) is too small",
                      (u_int32_t) romSect.size());
    }

    // restore endianess is done in the constructor if needed.
    /*  // FOR WE DON'T CHECKSUM UNTIL WE DECIDED REGARDING THE NEW FORMAT.
     */
    // We will look for the magic string in whole ROM instead of the first part of it.
    romChecksumRange = romSect.size();

    for (u_int32_t i = 0; i < romChecksumRange; i++) {
        for (u_int32_t j = 0; j < magicLen; j++) {
            if (romSect[i + j] != magicString[j]) {
                break;
            } else if (j == magicLen - 1) {
                magicFound = true;
            }
        }


        if (magicFound) {
            // Get the ROM info after the mlxsign
            bool rc;
            rom_info_t *currRom;

            if (numOfExpRom == MAX_ROMS_NUM) {
                expRomWarning = true;
                snprintf(expRomWarningMsg,
                         MAX_ROM_ERR_MSG_LEN,
                         "Number of exp ROMs exceeds the maximum allowed number (%d)",
                         MAX_ROMS_NUM);
                // Here we want to warn regarding this issue without checksum.
                return true;
            }

            currRom = &(romsInfo[numOfExpRom]);
            verOffset = i + magicLen;
            rc = GetExpRomVerForOneRom(verOffset);
            if (rc != true) {
                return rc;
            }

            // Get the device ID and check if it mismatches with other ROMs
            if (expRomComDevid != MISS_MATCH_DEV_ID) { // When the DevId is already mismatched, no end to any check
                if (currRom->exp_rom_dev_id != EXP_ROM_GEN_DEVID) {  // When we have a device ID on the ROM
                    if (expRomComDevid == EXP_ROM_GEN_DEVID) { // Update the common DevId at the first time we find ID
                        expRomComDevid = currRom->exp_rom_dev_id;
                    } else { // Check if we have the same IDs, if yes, continue
                        if (currRom->exp_rom_dev_id != expRomComDevid) { // There is a mismatch between ROMs
                            expRomComDevid = MISS_MATCH_DEV_ID;
                            expRomWarning = true;
                            snprintf(expRomWarningMsg,
                                     MAX_ROM_ERR_MSG_LEN,
                                     "The device IDs of the ROMs mismatched.");
                        }
                    }
                }
            }

            magicFound = false;      // Clean the magic_found to start search for another magic string
            i += (ROM_INFO_SIZE - 1); // Increase the index to point to the end of the ROM info.
            numOfExpRom++;
        }
    }

    // TODO: ADD CHECKSUM CHECK
    if (!numOfExpRom) {
        return errmsg("Cannot get ROM version. Signature not found.");

    }

    if (!noRomChecksum) { // No need for checksum on some ROMs like uEFI
        u_int8_t romChecksum = 0;
        romChecksumRange = romSect[2] * 512;
        if (romChecksumRange > romSect.size()) {
            return errmsg(
                "ROM size field (0x%2x) is larger than actual ROM size (0x%x)",
                romChecksumRange, (u_int32_t) romSect.size());
        } else if (romChecksumRange == 0) {
            return errmsg(
                "ROM size field is 0. Unknown ROM format or corrupted ROM.");
        }

        for (u_int32_t i = 0; i < romChecksumRange; i++) {
            romChecksum += romSect[i];
        }

        if (romChecksum != 0) {
            expRomWarning = true;
            snprintf(
                expRomWarningMsg,
                MAX_ROM_ERR_MSG_LEN,
                "Bad ROM Checksum (0x%02x), ROM info may not be displayed correctly.",
                romChecksum);
        }
    }

    return true;
}

u_int8_t FwOperations::RomInfo::getNumVerFromProdId(u_int16_t prodId)
{
    if (prodId == 0xF) {
        return 1;
    } else {
        return 3;
    }
}

bool FwOperations::RomInfo::GetExpRomVerForOneRom(u_int32_t verOffset)
{

    u_int32_t tmp;
    u_int32_t offs4;
    u_int32_t offs8;
    rom_info_t *romInfo;

    if (numOfExpRom == MAX_ROMS_NUM) {
        expRomWarning = true;
        snprintf(expRomWarningMsg,
                 MAX_ROM_ERR_MSG_LEN,
                 "Number of exp ROMs exceeds the maximum allowed number: %d",
                 MAX_ROMS_NUM);
        return true;
    }
    romInfo = &(romsInfo[numOfExpRom]);

    // Following mlxsign: refer to layout in Flash Programminng application note.

    // Get expansion rom product ID
    tmp = __le32_to_cpu(*((u_int32_t*) &romSect[verOffset]));
    offs4 = __le32_to_cpu(*((u_int32_t*) &romSect[verOffset + 4]));

    romInfo->exp_rom_product_id = tmp >> 16;
    romInfo->exp_rom_num_ver_fields = FwOperations::RomInfo::getNumVerFromProdId(romInfo->exp_rom_product_id);

    // Get ROM version
    romInfo->exp_rom_ver[0] = tmp & 0xff; // always valid
    if (romInfo->exp_rom_product_id != 0xf) {
        romInfo->exp_rom_ver[1] = offs4 >> 16 & 0xff;
        romInfo->exp_rom_ver[2] = offs4 & 0xffff;
    }

    if (romInfo->exp_rom_product_id == 0x11 || romInfo->exp_rom_product_id == 0x21) {
        noRomChecksum = true;
    }

    if (romInfo->exp_rom_product_id >= 0x10) {
        offs8 = __le32_to_cpu(*((u_int32_t*) &romSect[verOffset + 8]));
        romInfo->exp_rom_supp_cpu_arch = (offs8 >> 8) & 0xf;
        romInfo->exp_rom_dev_id = offs8 >> 16;
        //0x12 is CLP we have only 1 version field and no port
        if (romInfo->exp_rom_product_id != 0x12) {
            romInfo->exp_rom_port = (offs8 >> 12) & 0xf;
            romInfo->exp_rom_proto = offs8 & 0xff;
        }
    } else if (romInfo->exp_rom_product_id == 0xf) {
        // get string length
        u_int32_ba tmp_ba = __le32_to_cpu(*((u_int32_t*) &romSect[verOffset + 0xc]));
        u_int32_t str_len = u_int32_t(tmp_ba.range(15, 8));
        u_int32_t sign_length = u_int32_t(tmp_ba.range(7, 0));
        u_int32_t dws_num = ((str_len + 3) / 4) + 4;

        if (sign_length < dws_num) {
            return errmsg(
                "The Signature length (%d) and the ROM version string length (%d) are not coordinated",
                sign_length, str_len);
        }

        int svnv;
        char free_str[FREE_STR_MAX_LEN];
        strncpy(free_str, (char*) &romSect[verOffset + 0x10], str_len);
        free_str[str_len] = '\0';
        if (sscanf((char*) free_str, "%d", &svnv) == 1) {
            romInfo->exp_rom_ver[0] = svnv;
        }

        tmp_ba = __le32_to_cpu(*((u_int32_t*) &romSect[0x18]));
        u_int32_t dev_id_off = u_int32_t(tmp_ba.range(15, 0)) + 4;

        if (dev_id_off >= romSect.size()) {
            return errmsg(
                "The device ID offset %#x is out of range. ROM size: %#x",
                dev_id_off, (u_int32_t) romSect.size());
        }

        // get devid
        tmp_ba = __le32_to_cpu(*((u_int32_t*) &romSect[dev_id_off]));
        romInfo->exp_rom_dev_id = u_int32_t(tmp_ba.range(31, 16));
        u_int32_t vendor_id = u_int32_t(tmp_ba.range(15, 0));

        if (vendor_id != MELLANOX_VENDOR_ID) {
            expRomWarning = true;
            snprintf(expRomWarningMsg,
                     MAX_ROM_ERR_MSG_LEN,
                     "The Exp-ROM PCI vendor ID: %#x does not match the expected value: %#x.",
                     vendor_id, MELLANOX_VENDOR_ID);
        }

    }
    return true;
}

bool FwOperations::ReadImageFile(const char *fimage, u_int8_t*&file_data, int &file_size)
{
#ifndef UEFI_BUILD
    FILE *fh;

    if ((fh = fopen(fimage, "rb")) == NULL) {
        return errmsg("Can not open %s: %s\n", fimage, strerror(errno));
    }

    if (fseek(fh, 0, SEEK_END) < 0) {
        fclose(fh);
        return errmsg("Failed to get size of the file \"%s\": %s\n", fimage, strerror(errno));
    }

    int read_file_size = ftell(fh);
    if (read_file_size < 0) {
        fclose(fh);
        return errmsg("Failed to get size of the file \"%s\": %s\n", fimage, strerror(errno));
    }
    rewind(fh);

    file_size = read_file_size;
    file_data = new u_int8_t[file_size];
    if (fread(file_data, 1, read_file_size, fh) != (size_t)read_file_size) {
        delete[] file_data;
        fclose(fh);
        return errmsg("Failed to read from %s: %s\n", fimage, strerror(errno));
    }
    fclose(fh);
    return true;
#else
    return errmsg("Not implemented.\n");
#endif
}

void FwOperations::SetDevFlags(chip_type_t chipType, u_int32_t devType, fw_img_type_t fwType, bool &ibDev, bool &ethDev)
{

    (void)devType;
    if (chipType == CT_IS4) {
        ibDev =  true;
        ethDev = false;
    } else if (chipType == CT_SWITCHX) {
        ibDev = true;
        ethDev = true;
    } else {
        ibDev  = (fwType == FIT_FS3 && chipType != CT_SPECTRUM) || (chipType == CT_CONNECTX && !CntxEthOnly(devType));
        ethDev = (chipType == CT_CONNECTX) || (chipType == CT_SPECTRUM) || (chipType == CT_CONNECTX4) || \
                 (chipType == CT_CONNECTX4_LX) || (chipType == CT_CONNECTX5) || (chipType == CT_BLUEFIELD) || \
                 (chipType == CT_CONNECTX6);
    }

    if ((!ibDev && !ethDev) || chipType == CT_UNKNOWN) {
        // Unknown device id - for forward compat - assume that ConnectX is MP and
        // prev HCAs are IB only (these flags are for printing only - no real harm can be done).
        // TODO: FS2 does not mean ConnectX now.
        ibDev = true;
        if (fwType == FIT_FS2) {
            ethDev = true;
        } else {
            ethDev = false;
        }
    }
}

bool FwOperations::IsFwSupportingRomModify(u_int16_t fw_ver[3])
{
    u_int16_t supported_fw[3] = {MAJOR_MOD_ROM_FW,  MINOR_MOD_ROM_FW, SUBMINOR_MOD_ROM_FW};
    // only used in connectx (FS2)
    FwVerInfo verInfo = FwVerLessThan(fw_ver, supported_fw);
    return ((verInfo == FVI_EQUAL) || (verInfo == FVI_GREATER));
}

bool FwOperations::checkMatchingExpRomDevId(const fw_info_t& info)
{
    /*
       if ((info.fw_info.roms_info.num_of_exp_rom > 0) && (info.fw_info.dev_type)
            && (info.fw_info.roms_info.exp_rom_com_devid != EXP_ROM_GEN_DEVID) \
            && (info.fw_info.roms_info.exp_rom_com_devid != MISS_MATCH_DEV_ID)
            && (info.fw_info.dev_type != info.fw_info.roms_info.exp_rom_com_devid)) {
        return false;
       }
       return true; */
    return checkMatchingExpRomDevId(info.fw_info.dev_type, info.fw_info.roms_info);
}


bool FwOperations::checkMatchingExpRomDevId(u_int16_t dev_type, const roms_info_t& roms_info)
{
    if ((roms_info.num_of_exp_rom > 0) && (dev_type)
        && (roms_info.exp_rom_com_devid != EXP_ROM_GEN_DEVID) \
        && (roms_info.exp_rom_com_devid != MISS_MATCH_DEV_ID)
        && (dev_type != roms_info.exp_rom_com_devid)) {
        return false;
    }
    return true;
}


bool FwOperations::FwWriteBlock(u_int32_t addr, std::vector<u_int8_t> dataVec, ProgressCallBack progressFunc)
{
    if (dataVec.empty()) {
        return errmsg("no data to write.");
    }
    // make sure we work on device
    if (!_ioAccess->is_flash()) {
        return errmsg("no flash detected.(command is only supported on flash)");
    }

    //check if flash is big enough
    if ((addr + dataVec.size()) > ((Flash*)_ioAccess)->get_size()) {
        return errmsg("Writing %#x bytes from address %#x is out of flash limits (%#x bytes)\n",
                      (unsigned int)(dataVec.size()), (unsigned int)addr, (unsigned int)_ioAccess->get_size());
    }

    if (!writeImage(progressFunc, addr, &dataVec[0], (int)dataVec.size())) {
        return false;
    }
    return true;
};


bool FwOperations::CreateBasicImageFromData(u_int32_t *data, u_int32_t dataSize,
                                            FwOperations **newImgOps)
{
    fwOpsParams imgOpsParams;
    memset(&imgOpsParams, 0, sizeof(imgOpsParams));
    char errBuff[1024] = {0};

    imgOpsParams.psid      = (char*)NULL;
    imgOpsParams.buffHndl  = data;
    imgOpsParams.buffSize = dataSize;
    imgOpsParams.errBuff = errBuff;
    imgOpsParams.errBuffSize = 1024;
    imgOpsParams.hndlType = FHT_FW_BUFF;

    *newImgOps = FwOperationsCreate(imgOpsParams);
    if (*newImgOps == NULL) {
        return errmsg("Internal error: Failed to create modified image: %s",
                      errBuff);
    }
    if (!(*newImgOps)->FwVerify((VerifyCallBack)NULL)) {
        errmsg("Internal error: Modified image failed to verify: %s",
               (*newImgOps)->err());
        (*newImgOps)->FwCleanUp();
        delete (*newImgOps);
        return false;
    }

    return true;
};


bool FwOperations::FwBurnData(u_int32_t *data, u_int32_t dataSize, ProgressCallBack progressFunc)
{
    burnDataParamsT params;
    params.data = data;
    params.dataSize = dataSize;
    params.progressFunc = progressFunc;
    params.calcSha = false;
    return FwBurnData(params);
}
bool FwOperations::FwBurnData(burnDataParamsT& burnDataParams)
{
    u_int32_t *data = burnDataParams.data;
    u_int32_t dataSize = burnDataParams.dataSize;
    ProgressCallBack progressFunc = burnDataParams.progressFunc;
    FwOperations *newImgOps;
    ExtBurnParams burnParams = ExtBurnParams();

    if (!CreateBasicImageFromData(data, dataSize, &newImgOps)) {
        return false;
    }

    if (burnDataParams.calcSha && !newImgOps->FwInsertSHA256((PrintCallBack)NULL)) {
        errmsg("Inserting SHA256/SHA512 failed: %s", newImgOps->err());
        newImgOps->FwCleanUp();
        delete newImgOps;
        return false;
    }

    burnParams.updateParamsForBasicImage(progressFunc);

    if (!FwBurnAdvanced(newImgOps, burnParams)) {
        newImgOps->FwCleanUp();
        delete newImgOps;
        return errmsg("Failed to re-burn image after modify: %s", err());
    }

    newImgOps->FwCleanUp();
    delete newImgOps;

    return true;
}


bool FwOperations::getRomsInfo(FBase *io, roms_info_t& romsInfo)
{
    std::vector<u_int8_t> romSector;
    romSector.clear();
    romSector.resize(io->get_size());
    if (!io->read(0, &romSector[0], io->get_size())) {
        return false;
    }
    RomInfo info(romSector, false);
    info.ParseInfo();
    info.initRomsInfo(&romsInfo);
    return true;
}

const char* FwOperations::expRomType2Str(u_int16_t type)
{
    switch (type) {
    case 0x1:
        return "CLP1";

    case 0x2:
        return "CLP2";

    case 0x3:
        return "CLP3";

    case 0x4:
        return "CLP4";

    case 0xf:
        return "CLP";         // hack as 0xf isnt always CLP (its type is defined in the free string inside the ROM)

    case 0x10:
        return "PXE";

    case 0x11:
        return "UEFI";

    case 0x12:
        return "CLP";

    case 0x21:
        return "FCODE";

    default:
        return (const char*)NULL;
    }
    return (const char*)NULL;
}

bool FwOperations::FwSetTimeStamp(struct tools_open_ts_entry& timestamp, struct tools_open_fw_version& fwVer)
{
    (void)timestamp;
    (void)fwVer;
    return errmsg("Operation not supported.");
}

bool FwOperations::FwResetTimeStamp()
{
    return errmsg("Operation not supported.");
}

bool FwOperations::FwQueryTimeStamp(struct tools_open_ts_entry& timestamp, struct tools_open_fw_version& fwVer, bool queryRunning)
{
    (void)timestamp;
    (void)fwVer;
    (void)queryRunning;
    return errmsg("Operation not supported.");
}

Tlv_Status_t FwOperations::GetTsObj(TimeStampIFC **tsObj)
{
    (void)tsObj;
    errmsg("Unsupported FW type.");
    return TS_TIMESTAMPING_NOT_SUPPORTED;
}

bool FwOperations::FwInsertSHA256(PrintCallBack)
{
    return errmsg("FwInsertSHA256 not supported");
}

bool FwOperations::FwSignWithOneRSAKey(const char*, const char*, PrintCallBack)
{
    return errmsg("FwSignWithOneRSAKey not supported");
}

bool FwOperations::FwSignWithTwoRSAKeys(const char*, const char*, const char*, const char*, PrintCallBack)
{
    return errmsg("FwSignWithTwoRSAKeys not supported");
}

bool FwOperations::FwSignWithHmac(const char*)
{
    return errmsg("FwSignWithHmac not supported");
}

bool FwOperations::PrepItocSectionsForHmac(vector<u_int8_t>& critical, vector<u_int8_t>& non_critical)
{
    (void)critical;
    (void)non_critical;
    return errmsg("PrepItocSectionsForHmac not supported");
}

bool FwOperations::IsCriticalSection(u_int8_t sect_type)
{
    (void)sect_type;
    return errmsg("IsCriticalSection not supported");
}


bool FwOperations::CalcHMAC(const vector<u_int8_t>& key, const vector<u_int8_t>& data, vector<u_int8_t>& digest)
{
    (void)key;
    (void)data;
    (void)digest;
    return errmsg("CalcHMAC not supported");
}

bool FwOperations::FwExtract4MBImage(vector<u_int8_t>& img, bool maskMagicPatternAndDevToc)
{
    (void)img;
    (void)maskMagicPatternAndDevToc;
    return errmsg("Operation not supported");
}

bool FwOperations::FwSetPublicKeys(char *fname, PrintCallBack callBackFunc)
{
    (void) fname;
    (void) callBackFunc;
    return errmsg("Operation not supported");
}

bool FwOperations::FwSetForbiddenVersions(char *fname, PrintCallBack callBackFunc)
{
    (void) fname;
    (void) callBackFunc;
    return errmsg("Operation not supported");
}

bool FwOperations::FwReadBlock(u_int32_t addr, u_int32_t size, std::vector<u_int8_t>& dataVec)
{
    if (addr + size > _ioAccess->get_size()) {
        return errmsg(MLXFW_BAD_PARAM_ERR, "Reading %#x bytes from address %#x is out of flash limits (%#x bytes)\n",
                      size, (unsigned int)addr, (unsigned int)_ioAccess->get_size());
    }
    //read from flash/image
    if (!_ioAccess->read(addr, &dataVec[0], size)) {
        return errmsg(MLXFW_BAD_PARAM_ERR, "%s", _ioAccess->err());
    }
    return true;
}

u_int8_t FwOperations::GetFwFormatFromHwDevID(u_int32_t hwDevId)
{
    if ((hwDevId == CX2_HW_ID)       ||
        ( hwDevId == CX3_HW_ID)      ||
        ( hwDevId == IS4_HW_ID)      ||
        ( hwDevId == SWITCHX_HW_ID)  ||
        ( hwDevId == CX3_PRO_HW_ID)) {
        return FS_FS2_GEN;
    } else if ( (hwDevId == CONNECT_IB_HW_ID) ||
                (hwDevId == SWITCH_IB_HW_ID)  ||
                (hwDevId == CX4_HW_ID)        ||
                (hwDevId == CX4LX_HW_ID)      ||
                (hwDevId == SPECTRUM_HW_ID)   ||
                (hwDevId == SWITCH_IB2_HW_ID)) {
        return FS_FS3_GEN;
    } else if (hwDevId == CX5_HW_ID ||
               hwDevId == CX6_HW_ID ||
               hwDevId == BF_HW_ID  ||
               hwDevId == QUANTUM_HW_ID)  {
        return FS_FS4_GEN;
    }
    return FS_UNKNOWN_IMG;
}

const char*  FwOperations::FwGetReSignMsgStr()
{
    return (const char*)NULL;
}


bool FwOperations::TestAndSetTimeStamp(FwOperations *imageOps)
{
    Tlv_Status_t rc;
    Tlv_Status_t devTsQueryRc;
    bool retRc = true;
    TimeStampIFC *imgTsObj;
    TimeStampIFC *devTsObj;
    bool tsFoundOnImage = false;
    struct tools_open_ts_entry imgTs;
    struct tools_open_fw_version imgFwVer;
    struct tools_open_ts_entry devTs;
    struct tools_open_fw_version devFwVer;
    memset(&imgTs, 0, sizeof(imgTs));
    memset(&imgFwVer, 0, sizeof(imgFwVer));
    memset(&devTs, 0, sizeof(devTs));
    memset(&devFwVer, 0, sizeof(devFwVer));

    if (_ioAccess && !_ioAccess->is_flash()) {
        // no need to test timestamp on image
        return true;
    }

    if (_fwParams.ignoreCacheRep) {
        // direct flash access no check is needed
        return true;
    }
    if (imageOps->_ioAccess && imageOps->_ioAccess->is_flash()) {
        return errmsg("TestAndSetTimeStamp bad params");
    }
    if (imageOps->GetTsObj(&imgTsObj)) {
        return errmsg("%s", imageOps->err());
    }
    rc = GetTsObj(&devTsObj);
    if (rc) {
        delete imgTsObj;
        return rc == TS_TIMESTAMPING_NOT_SUPPORTED ? true : false;
    }
    // check if device supports timestamping or if device is not in livefish
    devTsQueryRc = devTsObj->queryTimeStamp(devTs, devFwVer);
    if (devTsQueryRc == TS_TIMESTAMPING_NOT_SUPPORTED || devTsQueryRc == TS_UNSUPPORTED_ICMD_VERSION) {
        retRc = true;
        goto cleanup;
    } else if (devTsQueryRc && devTsQueryRc != TS_NO_VALID_TIMESTAMP) {
        retRc = errmsg("%s", devTsObj->err());
        goto cleanup;
    }

    // Option 1 image was timestampped need to try and set it on device
    // Option 2 image was not timestampped but device was timestampped
    rc = imgTsObj->queryTimeStamp(imgTs, imgFwVer);
    if (rc == TS_OK) {
        tsFoundOnImage = true;
    } else if (rc != TS_TLV_NOT_FOUND) {
        retRc = errmsg("%s", imgTsObj->err());
        goto cleanup;
    }

    if (tsFoundOnImage) {
        // timestamp found on image, attempt to set it on device
        rc = devTsObj->setTimeStamp(imgTs, imgFwVer);
        if (rc == TS_OK) {
            retRc = true;
        } else {
            retRc = errmsg("%s", devTsObj->err());
        }
    } else {
        if (devTsQueryRc == TS_NO_VALID_TIMESTAMP) {
            // no timestamp on image and no valid timestamp on device check if we got running timestamp if we do then fail
            devTsQueryRc = devTsObj->queryTimeStamp(devTs, devFwVer, true);
            if (devTsQueryRc == TS_OK) {
                // we got running timestamp return error
                retRc = errmsg("No valid timestamp detected. please set a valid timestamp on image/device or reset timestamps on device.");

            } else if (devTsQueryRc == TS_NO_VALID_TIMESTAMP) {
                // timestamping not used on device.
                retRc = true;
            } else {
                retRc = errmsg("%s", devTsObj->err());
            }
        } else {
            fw_info_t fw_query;
            memset(&fw_query, 0, sizeof(fw_info_t));
            if (!imageOps->FwQuery(&fw_query, true)) {
                return errmsg("Failed to query the image\n");
            }
            // we got a valid timestamp on device but not on image! compare the FW version
            if (devFwVer.fw_ver_major == fw_query.fw_info.fw_ver[0] &&
                devFwVer.fw_ver_minor == fw_query.fw_info.fw_ver[1] &&
                devFwVer.fw_ver_subminor == fw_query.fw_info.fw_ver[2]) {
                // versions match allow update
                retRc = true;
            } else {
                retRc = errmsg("Stamped FW version mismatch: %d.%d.%04d differs from %d.%d.%04d", devFwVer.fw_ver_major, \
                               devFwVer.fw_ver_minor, \
                               devFwVer.fw_ver_subminor, \
                               fw_query.fw_info.fw_ver[0], \
                               fw_query.fw_info.fw_ver[1], \
                               fw_query.fw_info.fw_ver[2]);
            }
        }
    }
cleanup:
    delete imgTsObj;
    delete devTsObj;
    return retRc;
}
