HEX
Server: nginx/1.22.1
System: Linux VM-16-9-centos 3.10.0-1160.99.1.el7.x86_64 #1 SMP Wed Sep 13 14:19:20 UTC 2023 x86_64
User: www (1001)
PHP: 7.3.31
Disabled: passthru,exec,system,putenv,chroot,chgrp,chown,shell_exec,popen,proc_open,pcntl_exec,ini_alter,ini_restore,dl,openlog,syslog,readlink,symlink,popepassthru,pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,imap_open,apache_setenv
Upload Files
File: //bin/mlxburn
#!/bin/sh
# -*-Tcl-*- (for emacs) \
# This file invokes mlxburn tool \
tcl_lib_path=/usr/lib64/mft/tcl/lib:; \
tclsh=/usr/lib64/mft/tcl/bin/tclsh8.6; \
mbindir=/usr/bin; \
#@POST_MST_BIN_DIR@; \
# \
# The following lines should be updated the rpm post script to override the \
# tclsh and tcl_lib_path in case we install the MFT in a non default path. \
# PLEASE PAY ATTENTION TO UPDATE THE POST SCRIPT IF YOU CHANGE THE PARAMS tclsh AND tcl_lib_path \
#@POST_TCLSH@; \
#@POST_TCL_LIB_PATH@; \
# \
export LD_LIBRARY_PATH="${tcl_lib_path}$LD_LIBRARY_PATH"; \
export PATH="${mbindir}:$PATH"; \
exec $tclsh "$0" "$@"

set MFT_VERSION_STR "mft 4.17.2-12"
set TOOLS_GIT_SHA   "c2a6843"
set BUILD_TIME      "Aug 23 2021, 11:23:03"
#MTL-HEADER##################################################################
#                 - Mellanox Confidential and Proprietary -
#
# Copyright (C) Jan. 2004, Mellanox Technologies Ltd.  ALL RIGHTS RESERVED.
#
# Except as specifically permitted herein, no portion of the information,
# including but not limited to object code and source code, may be reproduced,
# modified, distributed, republished or otherwise exploited in any form or by
# any means for any purpose without the prior written permission of Mellanox
# Technologies Ltd. Use of software subject to the terms and conditions
# detailed in the file "LICENSE.txt".
# End of legal section.
#############################################################################
# Id ..........$Id$
# Revision.....$Revision$
# Date.........$Date$
# Author.......$Author$
#EOH#########################################################################



################################################################################
################################################################################
##
## inifline TCL package (from sourceforge tcllib) is located 'inline'
## in this file to minimize external dependencies.
##
################################################################################
################################################################################



# ini.tcl --
#
#       Querying and modifying old-style windows configuration files (.ini)
#
# Copyright (c) 2003    Aaron Faupell <afaupell@users.sourceforge.net>
#
# See the file "license.terms" for information on usage and redistribution
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
#
# RCS: @(#) $Id$

# package provide inifile 1.0

namespace eval ini {
    set nexthandle 0
    set commentchar \;
}

proc ::ini::open {ini {mode r+}} {
    variable nexthandle

    if { ![regexp {^(w|r)\+?$} $mode] } {
        error "$mode is not a valid access mode"
    }

    ::set fh ini$nexthandle
    ::set tmp [::open $ini $mode]
    fconfigure $tmp -translation crlf

    namespace eval ::ini::$fh {
        array set data     {}
        array set comments {}
        array set sections {}
    }
    ::set ::ini::${fh}::channel $tmp
    ::set ::ini::${fh}::file    [_normalize $ini]
    ::set ::ini::${fh}::mode    $mode

    incr nexthandle
    if { [string match "r*" $mode] } {
        _loadfile $fh
    }
    return $fh
}

# close the file and delete all stored info about it
# this does not save any changes. see ::ini::commit

proc ::ini::close {fh} {
    _valid_ns $fh
    ::close [::set ::ini::${fh}::channel]
    namespace delete ::ini::$fh
}

# write all changes to disk

proc ::ini::commit {fh} {
    _valid_ns $fh
    namespace eval ::ini::$fh {
        if { $mode == "r" } {
            error "cannot write to read-only file"
        }
        set char $::ini::commentchar
        seek $channel 0 start
        foreach sec [array names sections] {
            if { [info exists comments($sec)] } {
                puts $channel "$char [join $comments($sec) "\n$char "]\n"
            }
            puts $channel "\[$sec\]"
            foreach key [lsort -dictionary [array names data [::ini::_globescape $sec]\000*]] {
                ::set key [lindex [split $key \000] 1]
                if {[info exists comments($sec\000$key)]} {
                    puts $channel "$char [join $comments($sec\000$key) "\n$char "]"
                }
                puts $channel "$key=$data($sec\000$key)"
            }
            puts $channel ""
        }
        catch { unset char sec key }
        close $channel
        set channel [::open $file r+]
        set mode r+
    }
    return
}

# internal command to read in a file
# see open and revert for public commands

proc ::ini::_loadfile {fh} {
    namespace eval ::ini::$fh {
        ::set cur {}
        ::set com {}
        set char $::ini::commentchar
        seek $channel 0 start

        foreach line [split [read $channel] "\n"] {
            if { [string match "$char*" $line] } {
                lappend com [string trim [string range $line [string length $char] end]]
            } elseif { [string match {\[*\]} $line] } {
                ::set cur [string range $line 1 end-1]
                if { $cur == "" } { continue }
                ::set sections($cur) 1
                if { $com != "" } {
                    ::set comments($cur) $com
                    ::set com {}
                }
            } elseif { [string match {*=*} $line] } {
                ::set line [split $line =]
                ::set key [string trim [lindex $line 0]]
                if { $key == "" || $cur == "" } { continue }
                ::set value [string trim [join [lrange $line 1 end] =]]
                if { [regexp "^(\".*\")\s+${char}(.*)$" $value -> 1 2] } {
                    set value $1
                    lappend com $2
                }
                ::set data($cur\000$key) $value
                if { $com != "" } {
                    ::set comments($cur\000$key) $com
                    ::set com {}
                }
            }
        }
        unset char cur com
        catch { unset line key value 1 2 }
    }
}

# internal command to escape glob special characters

proc ::ini::_globescape {string} {
    return [string map {* \\* ? \\? \\ \\\\ \[ \\\[ \] \\\]} $string]
}

# internal command to check if a section or key is nonexistant

proc ::ini::_exists {fh sec args} {
    if { ![info exists ::ini::${fh}::sections($sec)] } {
        error "no such section \"$sec\""
    }
    if { [llength $args] > 0 } {
        ::set key [lindex $args 0]
        if { ![info exists ::ini::${fh}::data($sec\000$key)] } {
            error "can't read key \"$key\""
        }
    }
}

# internal command to check validity of a handle

if { [package vcompare [package provide Tcl] 8.6] < 0 } {
    proc ::ini::_normalize {path} {
        return $path
    }
    proc ::ini::_valid_ns {name} {
        variable ::ini::${name}::data
        if { ![info exists data] } {
            error "$name is not an open INI file"
        }
    }
} else {
    proc ::ini::_normalize {path} {
        fix_file_path $path
    }
    proc ::ini::_valid_ns {name} {
        if { ![namespace exists ::ini::$name] } {
            error "$name is not an open INI file"
        }
    }
}

# return all section names

proc ::ini::sections {fh} {
    _valid_ns $fh
    return [array names ::ini::${fh}::sections]
}

# return boolean indicating existance of section or key in section

proc ::ini::exists {fh sec {key {}}} {
    _valid_ns $fh
    if { $key == "" } {
        return [info exists ::ini::${fh}::sections($sec)]
    }
    return [info exists ::ini::${fh}::data($sec\000$key)]
}

# return all key names of section
# error if section is nonexistant

proc ::ini::keys {fh sec} {
    _valid_ns $fh
    _exists $fh $sec
    ::set keys {}
    foreach x [array names ::ini::${fh}::data [_globescape $sec]\000*] {
        lappend keys [lindex [split $x \000] 1]
    }
    return $keys
}

# return all key value pairs of section
# error if section is nonexistant

proc ::ini::get {fh sec} {
    _valid_ns $fh
    _exists $fh $sec
    upvar 0 ::ini::${fh}::data data
    ::set r {}
    foreach x [array names data [_globescape $sec]\000*] {
        lappend r [lindex [split $x \000] 1] $data($x)
    }
    return $r
}

# return the value of a key
# error if key or section is nonexistant

proc ::ini::value {fh sec key} {
    _valid_ns $fh
    _exists $fh $sec $key
    return [::set ::ini::${fh}::data($sec\000$key)]
}

# set the value of a key
# new section or key names are created

proc ::ini::set {fh sec key value} {
    _valid_ns $fh
    ::set sec [string trim $sec]
    ::set key [string trim $key]
    if { $sec == "" || $key == "" } {
        error "section or key may not be empty"
    }
    ::set ::ini::${fh}::data($sec\000$key) $value
    ::set ::ini::${fh}::sections($sec) 1
    return $value
}

# delete a key or an entire section
# may delete nonexistant keys and sections

proc ::ini::delete {fh sec {key {}}} {
    _valid_ns $fh
    if { $key == "" } {
        array unset ::ini::${fh}::data [_globescape $sec]\000*
        array unset ::ini::${fh}::sections [_globescape $sec]
    }
    catch {unset ::ini::${fh}::data($sec\000$key)}
}

# read and set comments for sections and keys
# may comment nonexistant sections and keys

proc ::ini::comment {fh sec {key {}} args} {
    _valid_ns $fh
    upvar 0 ::ini::${fh}::comments comments
    if { $key == "" && [llength $args] == 0 } {
        if { $key == "" } {
            if { ![info exists comments($sec)] } { return {} }
            return $comments($sec)
        }
        if { ![info exists comments($sec\000$key)] } { return {} }
        return $comments($sec\000$key)
    }
    if { $key == "" } {
        eval [linsert $args 0 lappend comments($sec)]
    } else {
        eval [linsert $args 0 lappend comments($sec\000$key)]
    }
}

# return the physical filename for the handle

proc ::ini::filename {fh} {
    _valid_ns $fh
    return [::set ::ini::${fh}::file]
}

# reload the file from disk losing all changes since the last commit

proc ::ini::revert {fh} {
    _valid_ns $fh
    namespace eval ::ini::$fh {
        array set data     {}
        array set comments {}
        array set sections {}
    }
    if { ![string match "w*" $mode] } {
        _loadfile $fh
    }
}


################################################################################
################################################################################
##
## End of inifile package.
##
################################################################################
################################################################################


################################################################################
################################################################################
##
## g_puts local implementation
##
################################################################################
################################################################################


set G_PUTS_DEBUG   0
set G_PUTS_WARNING 0
set G_PUTS_INFORM  1

proc g_puts {str} {
   global G_PUTS_DEBUG
   global G_PUTS_WARNING
   global G_PUTS_INFORM

   set dbgLevel [string range $str 0 2]
   set allowPrint 0

   switch -- $dbgLevel {
      "-D-" { set allowPrint $G_PUTS_DEBUG   }
      "-W-" { set allowPrint $G_PUTS_WARNING }
      "-I-" { set allowPrint $G_PUTS_INFORM  }

      default { set allowPrint 1}

   }

   if {$allowPrint} {
      puts $str
   }
}

################################################################################
################################################################################
##
## End of g_puts package.
##
################################################################################
################################################################################



################################################################################
################################################################################
##
## getopt local implementation
##
################################################################################
################################################################################


set optind 0

proc getopt { argslist optstring optret argret } {
   global optind optindc
   upvar $optret retvar
   upvar $argret optarg

   # default settings for a normal return
   set optarg ""
   set retvar ""
   set retval 0

   # check if we are in a single char mode or support long
   # option string
   if {[string match "* *" $optstring]} {
      set longOptMode 1
   } else {
      set longOptMode 0
   }

   # check if we're past the end of the args list
   if { $optind < [ llength $argslist ] } then {

      # if we got -- or an option that doesn't begin with -, return (skipping
      # the --).  otherwise process the option arg.
      switch -glob -- [ set arg [ lindex $argslist $optind ]] {
         "--" {
            incr optind
         }

         "-" {
            set optarg "Illegal option: '-'"
            set retvar $optarg
            set retval -1
         }

         "-*" {
            # opt needs to return the name of the option
            regexp -- {-([^:]+):?} $arg d1 opt
            incr optind

            if {$longOptMode} {
               # options are defined as words optionaly containing ":"
               if { [ lsearch -regexp $optstring "^$opt:?\$" ] >= 0 } then {
                  set retvar $opt
                  set retval 1
                  if { [ lsearch -regexp $optstring "^$opt:\$" ] >= 0 } then {
                     if { $optind < [ llength $argslist ] } then {
                        set optarg [lindex $argslist $optind]
                        incr optind
                     } else {
                        set optarg "Option requires an argument -- $opt"
                        set retvar $optarg
                        set retval -1
                     }
                  }
               } else {
                  set optarg "Illegal option -- $opt"
                  set retvar $optarg
                  set retval -1
               }
            } else {
               # traditional single char options
               if { [ string match "*$opt*" $optstring ] } then {
                  set retvar $opt
                  set retval 1
                  if { [ string match "*$opt:*" $optstring ] } then {
                     if { $optind < [ llength $argslist ] } then {
                        set optarg [lindex $argslist $optind]
                        incr optind
                     } else {
                        set optarg "Option requires an argument -- $opt"
                        set retvar $optarg
                        set retval -1
                     }
                  }
               } else {
                  set optarg "Illegal option -- $opt"
                  set retvar $optarg
                  set retval -1
               }
            }
         }
      }
   }

   return $retval
}


################################################################################
################################################################################
##
## End of getopt package.
##
################################################################################
################################################################################

# Get an array key in teh form of Major:Minor format and return the major key
proc get_major_key {major_minor {separate "@"}} {
    return [lindex [split $major_minor $separate] 0]
}
#######################################
##
## VPD Access Functions:
##
#######################################

array set VPD_I2C_ACCESS {
   vpd_size              0x100
   vpd_offset            0x0
   log2_vpd_eeprom_size  15
   vpd_num_of_eeproms    1
   vpd_address           0x56
   vpd_i2c_16bit         true
   vpd_endian            little
}

proc load_i2c_setup { ini_file} {
   global VPD_I2C_ACCESS

   set sect "ADAPTER"

   set fh [::ini::open $ini_file r]

   foreach key [array names VPD_I2C_ACCESS] {
      if {[::ini::exists $fh $sect $key]} {
         g_puts "-D- i2c setup: Set Key $sect:$key from \"$VPD_I2C_ACCESS($key)\" to \"[::ini::value $fh $sect $key]\""
         set VPD_I2C_ACCESS($key) [::ini::value $fh $sect $key]
      }
   }

   ::ini::close $fh
}


proc vpd_i2c_setup {dev setup_file} {
   global VPD_I2C_ACCESS

   # check if the i2c executable is reachable
   # This is done by running with no parameters and checking the expected help string
   set e ""
   catch {set res [exec i2c]} e

   if {![regexp {All parameters are interpreted as hexa values} $e]} {
      error "i2c command not found. Cant access VPD via I2C"
   }


   # Load conf file (the FW's ini file)
   if {[llength $setup_file]} {
      foreach f $setup_file {
         load_i2c_setup $f
      }
   } else {
      # try to auto detect settings:

      # address width:
      # The heuristic is as follows:
      # First byte in vpd is 0x82. Second (the msb fo the ro data size) is
      # usually 0.
      # When reading from iaw of 1 eeprom using iaw of 2, the first byte is
      # duplicated. So I assume that if first 2 bytes are the same,
      # the ia2=2 setting is wrong.
      # Note: This is a heuristic. It can be overriden with the -vpd_conf
      #       true settings.
      # Try to read the first DW with i2c addres width (iaw) set to 2
      set w [vpd_read_bytes_i2c 0 4]
      if {[lindex $w 0] ==  [lindex $w 1]} {
         g_puts "-D- 2 first bytes of VPD are equal. Probably bad addr width -  trying to use i2c addr size = 1"
         set VPD_I2C_ACCESS(vpd_i2c_16bit) 0
      }
   }
}

array set VPD_PCI_ACCESS {
   slot     0
   address  0
   data     0
}

set lspci  /sbin/lspci
set setpci /sbin/setpci
set mellanox_vendor_id 15b3

proc lrun { cmd } {
   global errorInfo errorCode

   g_puts "-D- Running: $cmd"
   if {[catch {set res [eval "exec $cmd"]} e]} {
      set errc [lindex $errorCode end] ; set erri $errorInfo

      # g_puts "-D- errInfo=\"$errorInfo\" errCode=\"$errorCode\""
      if {$errc != "NONE" && $errc != 0 } {
          error "Failed running $cmd (returned $errc): $e"
      }
      set res $e
   }
   g_puts "-D- res=$res"
   return $res
}

proc vpd_pci_setup_windows {dev} {
    global setpci
    global VPD_PCI_ACCESS

    set setpci "win_mini_setpci.exe"

    if {![regexp {pciconf} $dev]} {
        error "VPD access is supported only by \"pciconfX\" devices"
    }

    set VPD_PCI_ACCESS(slot)     $dev
    set vpd_cap 48

    set VPD_PCI_ACCESS(address)  [format "%x" [expr "0x$vpd_cap" + 2]]
    set VPD_PCI_ACCESS(data)     [format "%x" [expr "0x$vpd_cap" + 4]]

    g_puts "-W- vpd_pci_setup_windows: using hard coded VPD CAP values"
}

proc vpd_pci_setup {dev_id dev_ix devid_is_slot} {
   global lspci setpci mellanox_vendor_id
   global VPD_PCI_ACCESS
   global tcl_platform


   # For linux, user = root check is done in the bash wrapper
   set default_path "/sbin"

   foreach f {lspci setpci} {
      if {[catch {set exe_path [exec which $f]} e]} {
         g_puts "-D- which $f: $e . Assuming $f path is $default_path"
         # be carefull - tricky stuff bellow note the "set $f" instead of "set f"
         set $f         "$default_path/$f"
         set exe_path   "$default_path/$f"
      } else {
         g_puts "-D- Path of $f: $exe_path"
         set $f $exe_path
      }

      if {![file executable $exe_path]} {
         error "Can't access VPD via PCI - Failed to find \"$f\" tool. Make sure it is in the path."
      }
   }


   if {$devid_is_slot} {
      set slot $dev_id
   } else {
      set dev_id_hex [format "%x" $dev_id]
      set cmd "$lspci -d $mellanox_vendor_id:$dev_id_hex"
      set res [lrun $cmd]

      set res [split $res "\n"]
      g_puts "-D- Found [llength $res] devices of type $dev_id on PCI"

      if {[llength $res] <= $dev_ix} {
         error "Device id $dev_id, index $dev_ix not found on PCI."
      }

      set slot [lindex [split [lindex $res $dev_ix] " "] 0]
      g_puts "-D- Using device [lindex $res $dev_ix] to access VPD (slot $slot)"
   }

   # Remove domain from slot if domain == 0.
   # On Suse9.3, lspci displays domain 0, but setpci does not support domains ???
   # regsub {^0000:} $slot {} slot


   set cmd "$lspci -v -s $slot"
   set res [lrun $cmd]

   if {![regexp {Capabilities:\s*\[(\S+)\]\s*Vital Product Data} $res d1 vpd_cap]} {
      error "The Given Device Either Doesn't Support VPD Capabilities, Not Ran as root or in LiveFish" 
   }

   g_puts "-D- VPD Capabilities add offset $vpd_cap"

   set VPD_PCI_ACCESS(slot)     $slot
   set VPD_PCI_ACCESS(address)  [format "%x" [expr "0x$vpd_cap" + 2]]
   set VPD_PCI_ACCESS(data)     [format "%x" [expr "0x$vpd_cap" + 4]]
}

proc vpd_mst_setup { dev } {
    set cmd "mst status -vv"
    regsub {^0000:} $dev {} short_dev
    if {![g_exec $cmd status res]} {
        g_puts "-E- Failed to get mst devices info by the command $cmd"
        cexit 1
    }
    set pci_dev ""

    set start_index [expr [lsearch -regexp $res "PCI devices"] + 3]
    set next_list_index [lsearch -regexp -start $start_index $res "--------"]
    if { $next_list_index == -1} {
        set last_index [llength $res]
    } else {
        set last_index [expr $next_list_index - 2]
    }

    for {set i $start_index} { $i < $last_index} {incr i} {
        set current_info [regexp -inline -all -- {\S+} [lindex $res $i]]
        set current_info [string map {"," " "} $current_info]
        if {[lsearch -start 1 $current_info $dev] != -1} {
            set pci_dev [lindex $current_info 2]
            break
        }
        if {[lsearch -start 1 $current_info 0000:$dev] != -1} {
            set pci_dev [lindex $current_info 2]
            break
        }
        if {[lsearch -start 1 $current_info $short_dev] != -1} {
            set pci_dev [lindex $current_info 2]
            break
        }
    }
    if { $pci_dev != ""} {
        vpd_pci_setup $pci_dev 0 1
    } else {
        error "Cant access VPD through PCI using device \"$dev\", Unknown device name."
    }

}

proc vpd_access_setup { dev access_through setup_file } {
   global vpd_read_proc
   global vpd_write_proc
   global use_mstflint
   global tcl_platform

   switch $access_through {
      PCI {
        if {$use_mstflint} {
            vpd_pci_setup $dev 0 1
        } elseif {$tcl_platform(platform) == "windows" && [regexp {mt(\d+)_pci(conf|_cr)(\d)} $dev d1 devid pci_type dev_ix]} {
            if {[regexp {[:#]} $dev]} {
                error "VPD operations are not supported on remote devices"
            }
            vpd_pci_setup_windows $dev
        } else {
            if { $tcl_platform(platform) != "windows" } {
                vpd_mst_setup $dev
            } else {
                error "Cant access VPD through PCI using device \"$dev\". Bad device name format."
            }
        }
        set vpd_read_proc  vpd_read_bytes_pci
        set vpd_write_proc vpd_write_bytes_pci
        }

      I2C {
         vpd_i2c_setup $dev $setup_file
         set vpd_read_proc  vpd_read_bytes_i2c
         set vpd_write_proc vpd_access_i2c
      }

      default {
         error "Bad VPD access method ($access_through). Allowed: PCI, I2C"
      }
   }
}

proc vpd_pci_wait_ready {val op} {
   global setpci
   global VPD_PCI_ACCESS

   set    max_retries 200
   set    retries     0
   set    ready       [expr !$val]

   while {$retries < $max_retries && $ready != $val} {
      if {$retries > 0} {
         # Polling too fast causes the op to never be ready. Probably a FW bug. Sleeping 2ms between polls is a good WA for now.
         after 2
      }

      set cmd "$setpci -s $VPD_PCI_ACCESS(slot) $VPD_PCI_ACCESS(address).W"
      set res [lrun $cmd]

      regexp -line {^([a-fA-F0-9]+)$} $res d1 res
      set ready [expr "0x$res" >> 15]

      incr retries
   }

   if {$ready != $val} {
       error "VPD $op not finished after $max_retries retries"
   } elseif {$retries > 1} {
       g_puts "-D- vpd $op ready after $retries retries"
   }
}

proc vpd_write_bytes_pci {offset data {block 4}} {
   global setpci
   global VPD_PCI_ACCESS

   if {$block != 4} {
      error "Internal error: vpd_write_bytes_pci: got block of $block. Only 4 is allowed."
   }

   if {$offset % $block} {
      error "Internal error: vpd_write_bytes_pci: offset ($offset) is not $block bytes aligned"
   }

   g_puts "-D- vpd_write_bytes_pci: $offset , $data"

   if {($block) != [llength $data]} {
      error "Internal error: vpd_write_bytes_pci: Write data length ([llength $data]) does not match block size ($block)"
   }

   # set data [regsub -all {0x}  $data {}]
   # set data [regsub -all {\s+} $data {}]

   # Write data:
   set data_word 0
   for {set i 0} { $i < 4} {incr i} {
      #orenk
      if {[lindex $data $i] > 0xff} {
          error "Internal error: vpd_write_bytes_pci: data byte at offset $offset + $i == [lindex $data $i]"
      }
      set data_word [expr $data_word | ([lindex $data $i] << (8 * $i))]
   }
   set data_word [format "0x%x" $data_word]
   set cmd "$setpci -s $VPD_PCI_ACCESS(slot) $VPD_PCI_ACCESS(data).L=$data_word"
   set res [lrun $cmd]

   # Write command and address:
   # set vlag bit in VPD reg (for write)
   set offset [format "0x%x" [expr $offset | 0x8000]]

   set cmd "$setpci -s $VPD_PCI_ACCESS(slot) $VPD_PCI_ACCESS(address).W=$offset"
   set res [lrun $cmd]

   # Wait for write complete
   vpd_pci_wait_ready 0 "write"
}


# return a list of $block bytes
# error on error.
proc vpd_read_bytes_pci { offset {block 1} } {
   global setpci
   global VPD_PCI_ACCESS

   if {$block != 1 && $block != 4} {
      error "vpd_read_bytes_pci: got block of $block. Only 1 and 4 are allowed."
   }

   set cmd "$setpci -s $VPD_PCI_ACCESS(slot) $VPD_PCI_ACCESS(address).W=$offset"
   set res [lrun $cmd]

   # TODO: May check flag bit is set to 1 in control word
   vpd_pci_wait_ready 1 "read"

   set cmd "$setpci -s $VPD_PCI_ACCESS(slot) $VPD_PCI_ACCESS(data).L"
   set res [lrun $cmd]

   # fliter error meessages if any
   regexp -line {^([a-fA-F0-9]+)$} $res d1 res

   set ret_list ""
   for {set i 0} { $i < $block } { incr i} {
      lappend ret_list [format "%02x" [expr ("0x$res" >> ($i * 8)) & 0xff]]
   }


   g_puts "-D- VPD $offset=$res=$ret_list"

   return $ret_list
}

#
# vpd_access_XXX : Read / Write access to the VPD
# offset: byte offset in the VPD space.
# data:   For write op: list of bytes
# block:  Num of bytes requested to be read/written in current access.
#         Must match the data size for writes.
#

proc vpd_read_bytes_i2c { offset {block 1}} {
   set data {}
   return [vpd_access_i2c $offset $data $block]
}

proc vpd_access_i2c { offset data {block 1} } {
   global mst_dev
   global VPD_I2C_ACCESS

   if {[llength $data] == 0} {
      # read op
      set op r
   } else {
      set op w
      set data [regsub -all {0x}  $data {}]
      set data [regsub -all {\s+} $data {}]

      if {($block * 2) != [string length $data]} {
         error "Internal error: vpd_access_i2c ($op) : Write data length ([string length $data]) does not match block size ($block)"
      }
   }

   set address_width 1
   if {$VPD_I2C_ACCESS(vpd_i2c_16bit) == "true" || $VPD_I2C_ACCESS(vpd_i2c_16bit) == "1"  } {
      set address_width 2
   }

   # DEBUG
   # set address_width 1

   set slv_address $VPD_I2C_ACCESS(vpd_address)
   set offset      [format "0x%x" [expr $offset + $VPD_I2C_ACCESS(vpd_offset)]]

   set cmd "i2c -a $address_width -x $block $mst_dev $op $slv_address $offset $data"
   g_puts "-D- Running: $cmd"
   if {[catch {set res [eval "exec $cmd"]} e]} {
      error "Failed running $cmd: $e"
   }

   set ret_list ""

   if {$op == "r"} {
      for {set i 0} { $i < $block } { incr i} {
         lappend ret_list [string range $res [expr 2 * $i] [expr 2 * $i + 1]  ]
      }

      g_puts "-D- VPD $offset=$res=$ret_list"
   }

   return $ret_list
}


proc calc_checksum { hex_data } {

   set sum 0
   foreach ival $hex_data {
      set sum [expr $sum + $ival]
   }
   set sum [expr $sum % 0x100]
   if {$sum == 0} {
      return 0x00
   } else {
      #         return [format "0x%02x" [expr 0x100 - $sum]]
      return $sum
   }
}

proc vpd_check_field_name {fieldName} {
    return [regexp {^[A-Za-z1-9_][A-Za-z1-9_]$} $fieldName]
}

proc vpd_check_field_bin_data {fieldData} {
    return [expr ([string length $fieldData] % 2 == 0) && \
                  [regexp {^[0-9a-fA-F]*$} $fieldData]]
}

proc write_vpd_data {start_offset data } {
   global vpd_write_proc
   set block_size     4

   set pad_pre {}
   set pad_suf {}

   # In PCI VPD is written in DWORDS (4 bytes) chunks
   # if the data is not 4 bytes aligned - read-modify-write the
   # first and last words.

   set pre_pad_size [expr $start_offset % $block_size]
   set pre_pad_offs [expr $start_offset - $pre_pad_size]

   set suf_pad_offs [expr $start_offset + [llength $data]]
   set suf_pad_size [expr ((($suf_pad_offs + $block_size - 1) / $block_size ) \
                           * $block_size) - $suf_pad_offs]

   g_puts "-D- write_vpd_data: offs=$start_offset size=[llength $data] pre_pad=$pre_pad_size suf_pad=$suf_pad_size"

   g_puts "-D- pre  padded data (len = [llength $data]): $data"
   if {$pre_pad_size} {
      set pad  [read_vpd_data $pre_pad_offs $pre_pad_size]
      set data [concat $pad $data]
   }

   if {$suf_pad_size} {
      set pad  [read_vpd_data $suf_pad_offs $suf_pad_size]
      set data [concat $data $pad]
   }

   g_puts "-D- post padded data (len = [llength $data]): $data"

   # Now write the padded data:
   set dlen [llength $data]
   for {set i 0} {$i < $dlen} {incr i $block_size} {
      set cur_data [lrange $data $i [expr $i + $block_size - 1]]
      set offs [expr $pre_pad_offs + $i]
      g_puts "-D- Calling: $vpd_write_proc {$offs} {$cur_data} {$block_size} "
      $vpd_write_proc $offs $cur_data $block_size
   }
}


proc read_vpd_data { start_offset size } {

   global vpd_read_proc

   set block_size     4

   set start_offset_aligned [expr $start_offset - ($start_offset % $block_size)]
   set size_aligned [expr $size + ($start_offset % $block_size)]

   g_puts "-D- read_vpd_data $start_offset $size : start_offset_aligned = $start_offset_aligned"

   # read byte by byte into a list
   set vpd_read_hex ""
   for {set i 0} {$i < $size_aligned} {incr i $block_size} {
      set offset [format "0x%x" [expr $start_offset_aligned + $i]]
      set ibyte [$vpd_read_proc $offset $block_size]
      if {$ibyte == -1}  {
         exit 1
      }

      foreach b $ibyte {
         append vpd_read_hex " 0x$b"
      }
   }

   if {($start_offset % $block_size) || (($start_offset + $size) % $block_size) } {
      # More data than the user asked - Need to trim

      set start_idx [expr $start_offset - $start_offset_aligned]
      set end_idx   [expr $start_idx + $size - 1]

      g_puts "-D- vpd trim 0 - [llength $vpd_read_hex] to $start_idx - $end_idx"

      set vpd_read_hex [lrange $vpd_read_hex $start_idx $end_idx]
   }

   g_puts "-D- read_vpd_data $start_offset $size : $vpd_read_hex"
   return $vpd_read_hex
}


proc read_vpd_data_YYY { start_offset size } {
   global vpd_read_proc

   # read byte by byte into a list
   set vpd_read_hex ""
   for {set i 0} {$i < $size} {incr i} {
      set offset [format "0x%x" [expr $start_offset + $i]]
      set ibyte [$vpd_read_proc $offset]
      if {$ibyte == -1}  {
         exit 1
      }
      set hbyte [format "0x%s" $ibyte]
      append vpd_read_hex " $hbyte"
   }

   g_puts "-D- read_vpd_data $start_offset $size : $vpd_read_hex"


   return $vpd_read_hex
}

proc hex_to_char { hex_list } {

   set vpd_string ""
   for {set i 0} {$i < [llength $hex_list]} {incr i} {
      set char [format "%c" [lindex $hex_list $i]]
      append vpd_string $char
   }
   return $vpd_string
}

proc hex_to_bin { hex_list } {
   set vpd_string ""
   for {set i 0} {$i < [llength $hex_list]} {incr i} {
      set char [format "%02x" [lindex $hex_list $i]]
      append vpd_string $char
   }
   return $vpd_string
}

#
# Read the full VPD tag and stor data as a list of hex bytes in
# vpd_data.
# Return length of tag (including header) on success.
#
proc vpd_read_tag {offset tag_id tag_name {max_len 0x100} } {
   global vpd_data

   set header [read_vpd_data  $offset  3]
   set id     [lindex $header 0]

   if {$id != $tag_id} {
      error "Failed to find tag \"$tag_name\" at vpd offset $offset (Expected tag id $tag_id , got $id)."
   }

   if {$id & 0x80} {
      set len [format "0x%x" [expr [lindex $header 1] + ([lindex $header 2] * 256)]]
   } else {
      # small resource
      set len "0x[expr $id & 0x7]"
      if {$id == 0x78} {
         set header $id
      } else {
         error "VPD: Unknown small resource header type ($id) at offset $offset"
      }
   }

   if {$len > $max_len} {
      error "VPD: $tag_name too long ($len bytes). Probably VPD access error or a corrupted VPD memory."
   }

   append vpd_data " $header [read_vpd_data [expr $offset + 3] $len]"

   return [format "0x%x" [expr $len + [llength $header]]]
}

proc toAscii { char } {
   scan $char %c value
   return $value
}

proc toBin { byte_str } {
   return [expr "0x$byte_str"]
}

proc read_vpd_to_vars {{tag_only 0}} {
   global VPD_R_TAGS VPD_RW_TAGS VPD_MISC_INFO
   global vpd_check_rw

   global vpd_data

   set tag_len [vpd_read_tag 0 0x82 "ID-TAG"]

   if {! $tag_len} {
      error "Bad VPD format - Can't find ID-TAG"
   }

   set VPD_R_TAGS(IDTAG:Offs)  0
   set VPD_R_TAGS(IDTAG:Id)    [lindex $vpd_data 0 ]
   set VPD_R_TAGS(IDTAG:Len)   [expr $tag_len - 3]
   set VPD_R_TAGS(IDTAG:Data)  [hex_to_char [lrange $vpd_data 3 [expr 2 + $VPD_R_TAGS(IDTAG:Len)]]]


   # VPD-RTAG

   set VPDRbase $tag_len
   set tag_len [vpd_read_tag $VPDRbase 0x90 "VPD-RTAG"]

   if {! $tag_len} {
      error "Bad VPD format - Can't find VPD-RTAG section"
   }
   set VPD_R_TAGS(VPDRTAG:Offs) [expr $VPDRbase]
   set VPD_R_TAGS(VPDRTAG:Id)   [lindex $vpd_data       $VPDRbase]

   set tag_len_hi     [lindex $vpd_data [expr $VPDRbase + 2]]
   set tag_len_lo     [lindex $vpd_data [expr $VPDRbase + 1]]

   set VPD_R_TAGS(VPDRTAG:Len) [format "0x%04x" [expr $tag_len_hi * 256 + $tag_len_lo]]
   set VPD_MISC_INFO(VPDRTAG:Status) "OK"

   set vpd_r_end      [expr $VPD_R_TAGS(VPDRTAG:Len) + $VPD_R_TAGS(VPDRTAG:Offs) + 2]

   # loop that read the first two characters
   # and enter the values to the relevent variables
   # (${first2char}:Id ,${first2char}:Len,${first2char}:Data)
   # the loop stop when it find "RV"

   set base [expr $VPDRbase+3]
   set fieldName [hex_to_char [lrange $vpd_data $base [expr $base + 1]]]
   set TagNames ""
   while { ![regexp "^RV$" $fieldName] } {
      set VPD_R_TAGS($fieldName:Offs) $base
      set VPD_R_TAGS($fieldName:Id)   $fieldName
      set VPD_R_TAGS($fieldName:Len)  [lindex $vpd_data [expr $base+2]]
      set VPD_R_TAGS($fieldName:Data) [hex_to_char [lrange $vpd_data [expr $base+3] [expr $base+3+$VPD_R_TAGS(${fieldName}:Len)-1]]]

      set base [expr $base+$VPD_R_TAGS($fieldName:Len)+3]
      if { $base > $vpd_r_end } {
         error "Bad VPD format - Can't find RV keyword"
      }
      set fieldName [hex_to_char [lrange $vpd_data $base [expr $base + 1]]]

   }

   set VPD_R_TAGS(RV:Id)    $fieldName
   set VPD_R_TAGS(RV:Len)  [lindex $vpd_data [expr $base+2]]
   set VPD_R_TAGS(RV:Data) [lrange $vpd_data [expr $base+3] [expr $base + 3 + $VPD_R_TAGS(RV:Len) - 1]]
   # Split RV data (spaces) and take the first byte as it (according to PCI spec) checksum.
   set RV_CHECKSUM_BYTE [lindex [regexp -all -inline {\S+} $VPD_R_TAGS(RV:Data)] 0]

   set vpd_hex_data [lrange $vpd_data 0 [expr $base+2]]
   set VPD_MISC_INFO(CS)   [expr [expr [calc_checksum $vpd_hex_data]+$RV_CHECKSUM_BYTE]%256]

   if {$VPD_MISC_INFO(CS)} {
      puts "Warning: VPD checksum is not zero ($VPD_MISC_INFO(CS)) . VPD is corrupted."
   }

   if {!$vpd_check_rw} {
      return
   }

   # RW section
   set base [expr $base+4]

   set tag_len [vpd_read_tag $base 0x91 "VPD-WTAG" 4096]
   if {! $tag_len } {
      error "VPD: Can't find VPD-WTAG"
   }

   vpd_read_tag [expr $base + $tag_len] 0x78 "VPD-END"

   set VPD_RW_TAGS(VPDWTAG:Offs) $base
   set VPD_RW_TAGS(VPDWTAG:Id)   [lindex $vpd_data $base]
   set VPD_RW_TAGS(VPDWTAG:Len)  [format "0x%04x" [expr $tag_len - 3]]

   set VPD_MISC_INFO(VPDWTAG:Status) "OK"

   set vpd_rw_end [expr $VPD_RW_TAGS(VPDWTAG:Offs) + $tag_len]

   if {$tag_only} {
      return
   }

   set base [expr $base+3]
   set fieldName [hex_to_char [lrange $vpd_data $base [expr $base + 1]]]
   while { ![regexp "^RW$" $fieldName] } {
      g_puts "-D- Checking keyword \"$fieldName\". Offs=$base"
      set VPD_RW_TAGS($fieldName:Offs) $base
      set VPD_RW_TAGS($fieldName:Id)   $fieldName
      set VPD_RW_TAGS($fieldName:Len)  [lindex $vpd_data [expr $base+2]]
      set VPD_RW_TAGS($fieldName:Data) [hex_to_char [lrange $vpd_data [expr $base+3] [expr $base + 2 + $VPD_RW_TAGS($fieldName:Len)]]]
      set VPD_RW_TAGS($fieldName:Bin)  [hex_to_bin  [lrange $vpd_data [expr $base+3] [expr $base + 2 + $VPD_RW_TAGS($fieldName:Len)]]]

      set base [expr $base+$VPD_RW_TAGS(${fieldName}:Len)+3]
      if { $base > $vpd_rw_end } {
         error "Failed to find RW keyword in VPD-W. Probably VPD access error or a corrupted VPD memory."
      }
      set fieldName [hex_to_char [lrange $vpd_data $base [expr $base + 1]]]
      if {![vpd_check_field_name $fieldName]} {
         error "Bad keyword name \"$fieldName\" found in VPD-W tag, offset $base.  Probably VPD access error or a corrupted VPD memory."
      }
   }

   set VPD_RW_TAGS(RW:Id)  $fieldName
   set VPD_RW_TAGS(RW:Len) [lindex $vpd_data [expr $base+2]]

   if { [lindex $vpd_data [expr $base+3+$VPD_RW_TAGS(RW:Len)]] == 0x78 } {
      set VPD_MISC_INFO(RW:Status) "OK"
      set VPD_MISC_INFO(ENDTAG:Id) [lindex $vpd_data [expr $base+3+$VPD_RW_TAGS(RW:Len)]]
   } else {
      set VPD_MISC_INFO(RW:Status) "FAIL"
      set VPD_MISC_INFO(ENDTAG:Id) [lindex $vpd_data 255]
   }

    #puts "-D- debug"
    #local_parray VPD_RW_TAGS
    #exit

}

proc prepare_vpd_tags {arrName flds} {
   upvar 1 $arrName arr
   if {![array exists arr]} {
      error "\"$arrName\" isn't an array"
   }

   set rw_bytes {}

   foreach f $flds {
    set fdat $arr($f:Data)

      for {set i 0} {$i < 2} {incr i} {
         lappend rw_bytes [toAscii [string range $f $i $i]]
      }

      if {$arr($f:IsBin)} {
        # Binary field
        set flen [expr [string length $fdat] / 2]
        lappend rw_bytes $flen
        for {set i 0} {$i < $flen * 2} {incr i 2} {
            lappend rw_bytes [toBin [string range $fdat $i [expr $i + 1]]]
         }
      } else {
	 # ASCII field
	 set flen [string length $fdat]
	 lappend rw_bytes $flen
         for {set i 0} {$i < $flen} {incr i} {
            lappend rw_bytes [toAscii [string range $fdat $i $i]]
         }
      }
   }

   set rw_hex_bytes {}
   foreach f $rw_bytes {
      lappend rw_hex_bytes [format "0x%02x" $f]
   }

   return $rw_hex_bytes
}

proc processVpdSetLine {arrName sLine} {
   upvar 1 $arrName arr1
   if {![array exists arr1]} {
      error "processVpdSetLine: \"$arrName\" isn't an array"
   }

   if {[regexp {^\s*([^\s=]+)\s*=(.*)$} $sLine d1 name val]} {
      set val [string trim $val]
      if {[regexp {([^:]+):BIN} $name d1 newName]} {
	 set name $newName
         set arr1($name:IsBin) 1
      } else {
         set arr1($name:IsBin) 0
      }

      set arr1($name:Id) $name
      set arr1($name:Data) $val
      lappend keys $name
    } else {
         error "Bad line format (expecting \"keyword = value\")."
    }

    return $name
}

proc loadConfFile {arrName fn} {
   upvar 1 $arrName arr
   if {![array exists arr]} {
      error "\"$arrName\" isn't an array"
   }

   array unset arr
   array set arr {}

   # Keys: list of the original order of keys in the conf file
   set keys {}

   set f [open $fn r]
   set ln 0
   while {[gets $f sLine] >= 0} {
      incr ln

      if {[regexp {^\s*[\#]} $sLine] || [regexp {^\s*$} $sLine]} {
         continue
      } else {
         if {[catch "lappend keys [processVpdSetLine arr $sLine]" e]} {
	     global errorInfo
	     g_puts "-D- processVpdSetLine error: $errorInfo"
             error "$fn:$ln: $e"
         }
      }
   }
   close $f

   return $keys
}

proc checkVpdRwFlds {data_file} {
   global NEW_VPD_RW_DATA

   set mustHaveFlds    {}
   set mustNotHaveFlds {RW}

   set msg "Fix \"$data_file\" file and retry."
   foreach f $mustHaveFlds {
      if {![info exists NEW_VPD_RW_DATA($f:Id)]} {
         error "Keyword \"$f\" must be assigned in the VPD-W tag. $msg"
      }
   }

   if {[info exists NEW_VPD_RW_DATA(RW:Id)]} {
      error "\"RW\" keyword in the VPD-W tag must not be assigned (it is generated automatically by this tool). $msg"
   }

   foreach f_id [array names NEW_VPD_RW_DATA *:Id] {
      set f [get_major_key $f_id ":"]

      if {![vpd_check_field_name $f]} {
         error "Bad VPD-W keyword name ($f) - keyword names must have 2 alpha-numeric characters exactly. $msg"
      }

      if {$NEW_VPD_RW_DATA($f:IsBin) && ![vpd_check_field_bin_data $NEW_VPD_RW_DATA($f:Data)]} {
	  error "In binary VPD keyword \"$f\": data must be an even length hex string"
      }

      set max_len 255
      if {$NEW_VPD_RW_DATA($f:IsBin)} {
          set max_len 510
      }

      if {[string length $NEW_VPD_RW_DATA($f:Data)] > $max_len} {
          error "VPD keyword \"$f\": Data too long"           
      }
   }
}

proc vpd_add_rw_fld {fldsLen rwResourceLen} {
   set rwSizeLeft [expr $rwResourceLen - $fldsLen]
   g_puts "-D- Adding RW section of size $rwSizeLeft bytes"

   # For CX3 4KB eeprom , there are multiple RW kewords in the RW tag.
   # Logic of filling:
   # - Add RW keywords of size 253 (so it takes 256 bytes with the header)
   # - stop when < 256 size is left.
   # - Special case - if only 1 or 2 bytes remaining at end of RW tag (no room for last RW keword),
   #   remove 2 bytes from the first RW tag.

   set rwKeyMaxSize 253
   if {($rwSizeLeft % 256) == 1 || ($rwSizeLeft % 256) == 2} {
       set rwKeyMaxSize 250
   }

   set rwFld {}
   while {$rwSizeLeft > 0} {
       if {$rwSizeLeft > $rwKeyMaxSize + 3} {
	   set currSize $rwKeyMaxSize
       } else {
           set currSize [expr $rwSizeLeft - 3]
       }

       # Compensation is only in teh first iteration (see algo above)
       set rwKeyMaxSize 253

       lappend rwFld [toAscii R]
       lappend rwFld [toAscii W]

       lappend rwFld $currSize

       for {set i 0} {$i < $currSize} {incr i} {
	  lappend rwFld 0
       }

       set rwSizeLeft [expr $rwSizeLeft - ($currSize + 3)]
   }

   set rwHexFld {}
   foreach f $rwFld {
      lappend rwHexFld [format "0x%02x" $f]
   }
   return $rwHexFld
}

proc verify_vpd {rwOffs rw_bytes} {

   set len [llength $rw_bytes]
   set rd_bytes [read_vpd_data $rwOffs $len]
   for {set i 0} {$i < $len} {incr i} {
      set wb [lindex $rw_bytes $i]
      set rb [lindex $rd_bytes $i]

      if {$wb != $rb} {
         error [format "VPD-W programming verification at offset 0x%x: expected 0x%2x, got 0x%2x" \
                [expr $rwOffs + $i] $wb $rb]
      }
   }
}

proc confirmWrite {keys full_write} {
   global NEW_VPD_RW_DATA
   global VPD_RW_TAGS
   global force

   set pp "to"
   if {$full_write} {
       set op "write"
   } elseif {[info exist VPD_RW_TAGS([lindex $keys 0]:Id)]} {
       set op "set"
       set pp "in"
   } else {
       set op "add"
   }


   g_puts "-I- Going to $op the following keyword(s) $pp the VPD-W tag:\n"
   set fmt "  %-25s %s"
   puts [format $fmt "VPD-KEYWORD" "VALUE"]
   puts [format $fmt "-----------" "-----"]
   foreach f $keys {
      if {$NEW_VPD_RW_DATA($f:IsBin)} {
	 set from_old_arr "Bin"
	 set bin_mark     "(BIN)"
      } else {
	 set from_old_arr "Data"
	 set bin_mark     ""
      }

      if {[info exist VPD_RW_TAGS($f:Id)]} {
          set old_val "$VPD_RW_TAGS($f:$from_old_arr) --> "
      } else {
          set old_val ""
      }

      set desc "N/A"
      puts [format $fmt "$f$bin_mark$desc" "$old_val$NEW_VPD_RW_DATA($f:Data)"]
      g_puts "-D- Length = [string length $NEW_VPD_RW_DATA($f:Data)]"
   }

   puts ""
   puts -nonewline "-?- Continue write ? \[y/n\] "
   flush stdout
   if {![info exist force] || $force == 0} {
      set ans [gets stdin]
   } else {
      set ans "yes"
      puts $ans
   }

   if {!([string equal -nocase $ans "y"] || [string equal -nocase $ans "yes"])} {
      error "Aborted by user"
   }
}


#
# This function writes the fld=data values from the given conf file
# to the VPD-W data.
# It assumes read_vpd_to_vars was allready called - VPD_RW_TAGS is filled.
#
proc write_vpd_rw {data_file single_keyword} {
   global NEW_VPD_RW_DATA
   global VPD_RW_TAGS
   
   array set NEW_VPD_RW_DATA {}

   if {![info exist VPD_RW_TAGS(VPDWTAG:Len)]} {
      error "VPD-W resource length is not initialized. VPD read, or internal error"
   }

   if {![info exist VPD_RW_TAGS(VPDWTAG:Offs)]} {
      error "VPD-W resource offset is not initialized. VPD read, or internal error"
   }

   set rwLen  $VPD_RW_TAGS(VPDWTAG:Len)
   set rwOffs [expr $VPD_RW_TAGS(VPDWTAG:Offs) + 3]
   # The 3 is the minimal place for the RW place holder which
   # is added automatically
   set maxFldsLen [expr $rwLen - 3]

   if {$data_file != ""} {
       g_puts "-I- Loading VPD-W keywords file ($data_file)"
       set flds [loadConfFile NEW_VPD_RW_DATA $data_file]
       set all_flds $flds
       set full_write 1
   } else {
       #keyword:
       loadNewVpdRwFromCurrent
       set flds [processVpdSetLine NEW_VPD_RW_DATA $single_keyword]
       foreach f_id [array names NEW_VPD_RW_DATA *:Id] {
	   lappend all_flds [get_major_key $f_id ":"]
       }
       set full_write 0
   }

   checkVpdRwFlds $data_file
   set rw_bytes [prepare_vpd_tags NEW_VPD_RW_DATA $all_flds]

   set fieldsLen [llength $rw_bytes]

   if {$fieldsLen > $maxFldsLen} {
      set e    "The total length of the VPD keywords (and headers) to write is "
      append e "[llength $rw_bytes] bytes, which is bigger than the available VPD-W "
      append e " section size ($maxFldsLen bytes). Please re-edit $data_file file."

      error $e
   }

   confirmWrite $flds $full_write

   g_puts "-D- Adding RW section"
   set rw_bytes [concat $rw_bytes [vpd_add_rw_fld [llength $rw_bytes] $rwLen]]

   g_puts "-I- Writing data to VPD (Keywords length: $fieldsLen bytes, VPD-W size: $maxFldsLen bytes) ..."

   write_vpd_data $rwOffs $rw_bytes

   g_puts "-I- Verifying ..."

   # for I2C access, need to wait between read and write.
   after 10
   verify_vpd $rwOffs $rw_bytes

   g_puts "-I- VPD-W tag programming completed."
}


#
# This proc gets the pxe port enable string in form of
# coma separated port assignments.
# "port1=enable,port2=disable"
# Also accept on right side: enable|1 or disable|0
# Also accept on left  side: port1|1
#
# it processes the assignmet string to the V4 keyword bitmask, and then
# write it to VPD RW section.

proc set_vpd_pxe {assignment_str} {
    set assignment_list [split $assignment_str ","]

    set port_mask 0
    foreach a $assignment_list {
	if {![regexp {^(port|p)*(\d)=(enable|1|disable|0)} $a d1 d2 port_num value]} {
	    error "Bad PXE port assignment format ($a). Expected \"port(1|2)=enable|disable\""
	}

	if {$value == "enable" || $value == "1"} {
	    set value 1
	} else {
	    set value 0
	}

	set port_mask [expr "$port_mask | ($value << ($port_num - 1))"]
    }

    write_vpd_rw "" [format "V4=%04x" $port_mask]
}


proc loadNewVpdRwFromCurrent {} {
    global VPD_RW_TAGS
    global NEW_VPD_RW_DATA

    # Pass the current VPD-RW values to teh new array (as bin data)
    foreach f_id [array names VPD_RW_TAGS *:Id] {
	set f [get_major_key $f_id ":"]
	if {$f == "RW" || $f == "VPDWTAG"} {
	    continue
	}
	set NEW_VPD_RW_DATA($f:Id)   $f
	set NEW_VPD_RW_DATA($f:Data) $VPD_RW_TAGS($f:Bin)
	set NEW_VPD_RW_DATA($f:IsBin) 1
    }
}


array set VPD_RO_TAGS_DES {
   IDTAG       "Board Id"
   PN          "Part Number"
   SN          "Serial Number"
   EC          "Revision"
   RV          "Checksum Complement"
   CS          "Actual Checksum"
   MN          "Manufacture ID"
   V0          "Misc Info" 
}
set IBMEZZ_VPD_ID "MTL-0001"

proc local_parray {arr} {
     upvar 1 $arr loc

     if {[catch {set ns [array names loc]}]} {
         g_puts "-E- $_arr is not an array"
         return 1
     }
     if {[llength $ns] == 0} {
         g_puts "-D- $arr : EMPTY ARRAY"
     }
     foreach n [lsort $ns] {
         g_puts "-D- [format %-20s $arr\($n\)] = $loc($n)"
     }
     return 1
}

proc convertType2FwName {dev_type} {
    global g_dev_rev
    global g_dev_id
    global burner
    global mst_dev
    global additional_burn_flags
    global CX3_HW_ID
    global CX3_PRO_HW_ID

    g_puts "-D- convertType2FwName: g_dev_rev: $g_dev_rev"
    set fw_name "Unknown"
    if {[is_is4 $dev_type]} {
        set fw_name "fw-IS4.mlx"
    } elseif {[is_sx $dev_type]} {
        set fw_name "fw-sx.mlx"
    } elseif {[is_switchib $dev_type]} {
        set fw_name "fw-SwitchIB.mlx"
    } elseif {[is_switchib2 $dev_type]} {
        set fw_name "fw-SwitchIB-2.mlx"
    } elseif {[is_quantum $dev_type]} {
        set fw_name "fw-Quantum.mlx"
    } elseif {[is_quantum2 $dev_type]} {
        set fw_name "fw-Quantum-2.mlx"
    } elseif {[is_spectrum $dev_type]} {
        set fw_name "fw-SwitchEN.mlx"
    } elseif {[is_spectrum2 $dev_type]} {
        set fw_name "fw-Spectrum-2.mlx"
    } elseif {[is_spectrum3 $dev_type]} {
        set fw_name "fw-Spectrum-3.mlx"
    } elseif {[is_spectrum4 $dev_type]} {
        set fw_name "fw-Spectrum-4.mlx"
    } elseif {[is_amg $dev_type]} {
        set fw_name "fw-LinkXGearboxRetimer.mlx"
    } elseif {[is_connectx $dev_type]} {
        if {$g_dev_id == $CX3_HW_ID} {
            # ConnetX2-Pro device
            set fw_name "fw-ConnectX3-rel.mlx"
        } elseif  {$g_dev_id == $CX3_PRO_HW_ID} {
            # CX3 device
            set fw_name "fw-ConnectX3Pro-rel.mlx"
        } else {
            if {$g_dev_rev == 0xb0} {
                set fw_name "fw-ConnectX2-rel.mlx"
            }
        }
    } elseif {[is_connectib $dev_type]} {
        set fw_name "fw-ConnectIB.mlx"
    } elseif {[is_connectx4lx $dev_type]} {
        set fw_name "fw-ConnectX4Lx.mlx"
    } elseif {[is_connectx4 $dev_type]} {
        set fw_name "fw-ConnectX4.mlx"
    } elseif {[is_connectx5 $dev_type]} {
        set fw_name "fw-ConnectX5.mlx"
    } elseif {[is_connectx6 $dev_type]} {
        set fw_name "fw-ConnectX6.mlx"
    } elseif {[is_connectx6dx $dev_type]} {
        set fw_name "fw-ConnectX6Dx.mlx"
    } elseif {[is_connectx6lx $dev_type]} {
        set fw_name "fw-ConnectX6Lx.mlx"
    } elseif {[is_connectx7 $dev_type]} {
        set fw_name "fw-ConnectX7.mlx"
    } elseif {[is_bluefield $dev_type]} {
        set fw_name "fw-BlueField.mlx"
    } elseif {[is_bluefield2 $dev_type]} {
        set fw_name "fw-BlueField-2.mlx"
    } elseif {[is_bluefield3 $dev_type]} {
        set fw_name "fw-BlueField-3.mlx"
    }
    g_puts "-D- convertType2FwName: fw_name = $fw_name"
    return $fw_name
}

#
# VPD Funcs end
###########################

# input: numerical value in base 16, 8 or 10
# return a number in decimal format
# Throws if bad conversion
# prefix for num ("0x" , "0") have precedence over specified base
proc my_strtoul {num {base 0}} {
    switch -exact -- $base {
         0 -
         10 { }
         16 {
                if {![regexp {^0[xX]} $num]} {
                    set num "0x$num"
                }
            }
         8  {
                if {![regexp {^0} $num]} {
                    set num "0$num"
                }
            }
         default {
             error "Unsupported base for my_strtoul ($base)"
         }
     }
     return [expr $num]
}


proc is_valid_hex {hex} {
    if {[catch "expr $hex"]} {
        return 0
    } else {
        return 1
    }
}

# Mellanox guid2mac - Remove 2 middle bytes of the guids to gen 6 bytes mac
proc guid2mac {guid} {
    return [expr (($guid & 0xffffff) | (($guid >> 16) & 0xffffff000000))]
}

#
# This function derives macs and guids based on teh ConnectIB default method:
# Port1 guid:  base_guid      to (base_guid + 7)
# Port2 guid: (base_guid + 8) to (base_guid + 15
#
# it returns a list of uids: (port1_guid,port2_guid,port1_mac,port2_mac)
proc get_conectib_uids {base_guid} {
    return [list $base_guid [expr $base_guid + 8] [guid2mac $base_guid] [guid2mac [expr $base_guid + 8]]]
}

# This function gets a list of uids  (port1_guid,port2_guid,port1_mac,port2_mac)
# Returns the relevant ConnectIB mic params assignment
proc connectib_uids2mic_cmd {uid_list} {
    set param_base_names {"guids.guids\\[0\\].uid"
                          "guids.guids\\[1\\].uid"
                          "guids.macs\\[0\\].uid"
                          "guids.macs\\[1\\].uid"}

    #print "Assigned UIDs:"
    #print "  Port 1 GUID: %16lx" % (uid_list[0])
    #print "  Port 2 GUID: %16lx" % (uid_list[1])
    #print "  Port 1 MAC:  %16lx" % (uid_list[2])
    #print "  Port 2 MAC:  %16lx" % (uid_list[3])

    set mic_str ""
    foreach node {"mfg_info" "device_info"} {
        for {set i 0} {$i < 4} {incr i} {
             append mic_str [format "-%s.%s.hi=0x%08x " $node [lindex $param_base_names $i] [expr [lindex $uid_list $i] >> 32]]
             append mic_str [format "-%s.%s.lo=0x%08x " $node [lindex $param_base_names $i] [expr [lindex $uid_list $i]  & 0xffffffff]]
        }
    }
    return $mic_str
}

#
# Returns teh ConnectIB GUIDs paramenter assignment as derived from the base GUID.
#
#
proc get_connectib_guids {base_guid} {
    return [connectib_uids2mic_cmd [get_conectib_uids $base_guid]]
}

# this function sets base_guid and base_mac for ConnectX4
proc get_connectx4_uids {base_guid base_mac} {
    set param_base_names {"guids.guids.uid"
                          "guids.macs.uid"}
    set uid_list {$base_guid $base_mac}
    set mic_str ""
    foreach node {"mfg_info" "device_info"} {
        for {set i 0} {$i < 2} {incr i} {
             append mic_str [format "-%s.%s.hi=0x%08x " $node [lindex $param_base_names $i] [expr [lindex $uid_list $i] >> 32]]
             append mic_str [format "-%s.%s.lo=0x%08x " $node [lindex $param_base_names $i] [expr [lindex $uid_list $i]  & 0xffffffff]]
        }
    }
    return $mic_str
}

set SECURITY_MODE_NONE       "N/A"
set SECURITY_MODE_SHA_DIGEST "SHA-Digest"
set SECURITY_MODE_RSA        "RSA"
set SECURITY_MODE_UNKNOWN    "Unknown"

#
# Generate image:
#
proc generate_image {{only_print_version 0}} {
   global additional_mic_flags
   global additional_burn_flags
   global nofs_img
   global G_PUTS_WARNING
   global format
   global fw_file
   global fw_dir
   global bin_file
   global conf_file
   global conf_dir
   global input_img
   global gb_bin_file
   global exp_rom
   global user_data
   global vpd_r_file
   global base_guid
   global base_mac
   global tmp_dir
   global mst_dev
   global dev_type
   global burner
   global t2a
   global errorCode
   global files_to_delete
   global exp_rom_dir
   global hash_file
   global prof_file
   global g_dev_rev
   global CX_MAIN_ID
   global tcl_platform
   global must_burn_flags
   global conf_dir_list
   global MORE_PSID_MSG
   global SECURITY_MODE_NONE
   global SECURITY_MODE_SHA_DIGEST
   global SECURITY_MODE_RSA
   global SECURITY_MODE_UNKNOWN
   #
   # Default format - BINARY for HCAs, IMAGE for switches
   #
   if {$tcl_platform(platform) == "unix"} {
       foreach f {mic t2a} {
          if {[catch {set exe_path [exec which $f]} e]} {
             g_puts "-E- Image generation is not supported (can not find $f tool). This may be due to an MFT installation using --without-image-generation option or a corrupted installation."
             cexit 1
          }
       }
   }
   if {$format == ""} {
      if {[is_flash $dev_type]} {
         set format "BINARY"
      } else {
         set format "IMAGE"
      }
      g_puts "-D- Format not given - using $format"
   } elseif {$format != "IMAGE" && $format != "BINARY"} {
      g_puts "-E- Bad format given ($format). Allowed formats: IMAGE, BINARY."
      cexit 1
   }

   if {[is_flash $dev_type] && $format != "BINARY" && $mst_dev != ""} {
      g_puts "-E- The given format ($format) is not supported for HCA burn. Please use BINARY format (default) when burning."
      cexit 1
   }

   # patch for ConnectX - All connectx devids get a single FW id ver:
   if {[is_connectx $dev_type]} {
       set dev_type $CX_MAIN_ID
   }

   if {![is_fs3_dev $dev_type] && ($base_guid != "" || $vpd_r_file != "" || $base_mac != "")} {
       g_puts "-E- vpd_r_file, base_guid and base_mac options are applicable only for FS3/FS4 images."
       cexit 1
   }

   if {([is_connectib $dev_type] || [is_switchib $dev_type] || [is_switchib2 $dev_type] || [is_quantum $dev_type] || [is_quantum2 $dev_type]) && ($base_mac != "")} {
       g_puts "-E- base_mac is only applicable for ConnectX4/ConnectX4Lx/Spectrum/SwitchIB compatible images."
       cexit 1
   }
   if {([is_connectx4 $dev_type] || [is_connectx4lx $dev_type] || [is_bluefield $dev_type] || [is_bluefield2 $dev_type] || [is_bluefield3 $dev_type] || [is_connectx5 $dev_type] || [is_connectx6 $dev_type] || [is_connectx6dx $dev_type] || [is_connectx6lx $dev_type] || [is_connectx7 $dev_type] || [is_spectrum $dev_type] || [is_spectrum2 $dev_type] || [is_spectrum3 $dev_type] || [is_spectrum4 $dev_type] || [is_amg $dev_type]) && ((($base_mac != "") && ($base_guid == "")) || (($base_mac == "") && ($base_guid != "")))} {
       g_puts "-E- Both base_guid and base_mac must be specified."
       cexit 1
   }

   set dev $mst_dev

   # Get the fw file
   if {$fw_dir != ""} {
       set caught [catch {
           g_puts "-D- dev_type: $dev_type"
           set mlx_list [glob -nocomplain -directory $fw_dir *rel\.mlx | *IS4\.mlx  | *sx.mlx | *IB.mlx | *SwitchIB.mlx |  *ConnectX4.mlx | *ConnectX4Lx.mlx | *ConnectX5.mlx | *ConnectX6.mlx | *ConnectX6Dx.mlx | *ConnectX6Lx.mlx | *ConnectX7.mlx | *BlueField.mlx | *BlueField-2.mlx | *BlueField-3.mlx  | *SwitchEN.mlx | *SwitchIB-2.mlx | *Quantum.mlx | *Quantum-2.mlx | *Spectrum-2.mlx | *Spectrum-3.mlx | *Spectrum-4.mlx | *LinkXGearboxRetimer.mlx]
           set exp_fw_name [convertType2FwName $dev_type]
           set auto_detect_failed 1
           g_puts "-D- fw dir File: $mlx_list"
           foreach mlx_file $mlx_list {
               set file_name [file tail $mlx_file]
               g_puts "-D- Looking for suitable fw file, file_name: \"$file_name\", exp_file_name: \"$exp_fw_name\""
               if {$file_name == $exp_fw_name} {
                   set fw_file $mlx_file
                   set auto_detect_failed 0
                   break;
               }
           }
           if {$auto_detect_failed == 1} {
               error "Failed to find fw file in directory \"$fw_dir\" for device: $dev_type"
           }

       } e ]

       if {$caught} {
           g_puts "-E- Can't auto detect fw file: $e . Please specify fw (mlx) file using -fw flag ."
           cexit 1
       }

   }
   #
   # Transtate mlx to xml (t2a) if needed
   #

   if {[file extension $fw_file] == ".mlx"} {
      # Store t2a output in a tmp directory
      set mlx_file [open $fw_file]
      set first_line [gets $mlx_file]
      close $mlx_file
      if {[regexp {<!-- MT[0-9]{3,5} Firmware image} $first_line]} {
          set xml_file $fw_file
      } else {

          set xml_file [file tail $fw_file]
          regsub {.mlx$} $xml_file ".[pid].xml" xml_file
          set xml_file "$tmp_dir/$xml_file"

          # Run t2a

          set cmd "$t2a MT$dev_type \"$fw_file\" \"$xml_file\""

          #g_puts "-I- Translating $fw_file to xml format ..."
          g_puts "-D- Running $cmd"

          if {[catch {set res [eval "exec $cmd"]} e]} {
             # Warnings that are printed to stderr can cause the tcl error
             # Check exit status to see if it's a real error.
             g_puts "-D- errorCode = \"$errorCode\". e = \"$e\""

             if {[lindex $errorCode 0] == "CHILDSTATUS"} {
                g_puts "-E- Translation failed: $e"
                cexit 1
             } else {
                g_puts "-W- t2a: $e"
             }
          }

          lappend files_to_delete $xml_file
      }

   } elseif {[file extension $fw_file] == ".xml" || [file extension $fw_file] == ".BIN"} {
      set xml_file $fw_file
   } else {
      g_puts "-E- Illegal FW file extension (\"$fw_file\"), Supported extensions are: .xml, .mlx, .BIN"
      exit 1
   }

   set mic_cmd "mic -format $format"

   if {$G_PUTS_WARNING == 0} {
      append mic_cmd " -nowarn"
   }

   if {$nofs_img} {
      append mic_cmd " -nofs"
   }

   if {$only_print_version} {
      append mic_cmd " -fw $xml_file -fwver"
      g_puts "-D- Running: $mic_cmd"
      if {[catch {set res [eval "exec $mic_cmd"]} e]} {
        g_puts "-E- Image version query failed: $e"
        cexit 1
      }
      return $res
   }


   if {$conf_file == "" || $exp_rom == "AUTO"} {
      set caught [catch {
         g_puts "-I- Querying device ..."

         array set dev_qry {}

         set status {}; set output {}
         set cmd "$burner -d $dev $additional_burn_flags $must_burn_flags  q"
         if {![g_exec $cmd status output]} {
            g_puts "-E- Failed to query device $dev: [lindex $status end]"
            cexit 1
         }

         parse_query_report $output dev_qry
      } e]

      if {$caught} {
         g_puts "-E- Can't auto detect fw configuration file: $e . Please specify configuration (ini) file using -conf flag and expansion rom file (if needed) using -exp_rom flag."
         cexit 1
      }
   }

   #
   # Get INI file
   #
   if {$conf_file == ""} {
        if { $conf_dir_list == "" } {
            if {[string match "*MLXBURN-AUTO" $conf_dir]} {
                set conf_dir [file dirname $fw_file]
            }
            set dirs_list $conf_dir
        } else {
            set dirs_list $conf_dir_list
        }

        set err_msg ""
        foreach curr_conf_dir $dirs_list {
            set caught [catch {
                set psid $dev_qry(PSID)
                if {[regexp {^\s*$} $psid] || [regexp {^N.A$} $psid]} {
                    error "Invalid PSID on device $mst_dev ($psid)"
                }
                g_puts "-D- curr_conf_dir = $curr_conf_dir"
                set conf_file [get_matching_conf_file $psid $curr_conf_dir]
            } e]

            if {!$caught} {
                break
            }
            if {[regexp "(.)*${MORE_PSID_MSG}(.)*" $e]} {
                set err_msg $e
                break;
            }
            lappend err_msg $e
        }
        if {$conf_file == ""} {
            g_puts "-E- Can't auto detect fw configuration file: $err_msg . Please specify configuration (ini) file using -conf flag ."
            cexit 1
        }

    g_puts "-I- Using auto detected configuration file: $conf_file (PSID = $psid)"
   }

   #
   # Get hash file
   #
   if {[is_is4 $dev_type]} {

       set hash_dir [file dirname $fw_file]
       set exp_hash_file "$hash_dir/hash.csv"

       if {![file exist $exp_hash_file]} {
          g_puts "-W- Can't auto detect hash.csv file on $hash_dir ."
       } else {
          set hash_file $exp_hash_file
          g_puts "-I- Using auto detected hash file: $hash_file"
       }
   }

   # Auto detect exp rom:
   if {$exp_rom_dir == ""} {
       set exp_rom_dir [file join [file dirname $fw_file] "exp_rom"]
   }

   if {$exp_rom == "AUTO"} {
        if {$dev_qry(Rom_Info) == ""} {
            set exp_rom "NONE"
        } elseif {$dev_qry(Rom_Info) == "N/A"} {
            g_puts "-E- Can not auto detect expansion ROM to use. Please specify expansion ROM using -exp_rom flag."
            cexit 1
        } else {
            set exp_rom [select_exp_rom $dev_qry(Rom_Info) $exp_rom_dir]
        }
        g_puts "-I- Using auto detected expansion ROM file: $exp_rom"
   }
   if {$exp_rom == "NONE"} {
        set exp_rom ""
   }
   #
   # run mic to create bin image file
   #
   if {$bin_file == ""} {
       set bin_file [file tail $fw_file]
       regsub {.BIN$} $bin_file ".[pid].img" bin_file
       regsub {.mlx$} $bin_file ".[pid].bin" bin_file
       set bin_file [fix_file_path "$tmp_dir/$bin_file"]
       g_puts "-D- Using tmp image file: $bin_file"

       lappend files_to_delete $bin_file
   }
   append mic_cmd " -fw \"$xml_file\"  -conf \"$conf_file\" -wrimage \"$bin_file\" $additional_mic_flags"

   # gearbox binary:
   if {$gb_bin_file != ""} {
      global tcl_platform
      if {$tcl_platform(platform) == "windows"} {
        set gb_bin_file  [fix_file_path $gb_bin_file ]
      } 
      g_puts "appending -gb_bin_file"
      append mic_cmd " -gb_bin_file \"$gb_bin_file\""
   }

   # expansion rom:
   if {$exp_rom != ""} {
      append mic_cmd " -exp_rom \"$exp_rom\""
   }

   #user data
   if {$user_data != ""} {
      append mic_cmd " -user_data \"$user_data\""
   }

   # hash file
   if {$hash_file != ""} {
       append mic_cmd " -hash_file \"$hash_file\""
   }

   if {$prof_file != ""} {
       append mic_cmd " -prof_file \"$prof_file\""
   }

   if {[is_fs3_dev $dev_type]} {
       if {$vpd_r_file != ""} {
          set vpd_r_file [fix_file_path $vpd_r_file]
          append mic_cmd " -vpd_r $vpd_r_file"
       }

       if {$base_guid != ""} {
          if {[catch {set valid_base_guid [my_strtoul $base_guid 16]} e]} {
              g_puts "-E- Bad base_guid ($base_guid). Must be a valid hex number."
              cexit 1
          }

          append mic_cmd " "
          if {[is_connectx4 $dev_type] || [is_connectx4lx $dev_type] || [is_bluefield $dev_type] || [is_bluefield2 $dev_type] || [is_bluefield3 $dev_type] || [is_connectx5 $dev_type] || [is_connectx6 $dev_type] || [is_connectx6dx $dev_type] || [is_connectx6lx $dev_type] || [is_connectx7 $dev_type] || [is_spectrum $dev_type] || [is_spectrum2 $dev_type] || [is_spectrum3 $dev_type] || [is_spectrum4 $dev_type] || [is_amg $dev_type]} {
              # we append it when we deal with base_mac
          } else {
              append mic_cmd [get_connectib_guids $valid_base_guid]
          }
       }

       if {$base_mac != ""} {
          if {!([is_connectx4 $dev_type] || [is_bluefield $dev_type] || [is_bluefield2 $dev_type] || [is_bluefield3 $dev_type] || [is_connectx5 $dev_type] || [is_connectx6 $dev_type] || [is_connectx6dx $dev_type] || [is_connectx6lx $dev_type] || [is_connectx7 $dev_type] || [is_connectx4lx $dev_type] || [is_spectrum $dev_type] || [is_spectrum2 $dev_type] || [is_spectrum3 $dev_type] || [is_spectrum4 $dev_type] || [is_amg $dev_type])} {
          g_puts "-E- -base_mac flag is supported on ConnectX-4/ConnectX-4LX/Spectrum/Spectrum2/Spectrum3/Spectrum4/Amos GB only."
          }

          if {[catch {set valid_base_mac [my_strtoul $base_mac 16]} e]} {
              g_puts "-E- Bad base_mac ($base_mac). Must be a valid hex number."
              cexit 1
          }

          append mic_cmd " "
          append mic_cmd  [get_connectx4_uids $valid_base_guid $valid_base_mac]
       }
   }

   g_puts "-I- Generating image ..."
   g_puts "-D- Running $mic_cmd"

   if {[catch {eval "exec $mic_cmd"} e]} {
      if {[string first ".guids.guids.uid.hi" $e] != -1} {
       set e "[lindex [split $e \n] 0] this might indicate an outdated mlx file format."
      }
      g_puts "-E- Image generation failed: $e"
      cexit 1
   }

   #
   # Post process:
   #
   # if {$format == "IMAGE" && ![is_hca $dev_type] && $remove_directives} {
   #   g_puts "-D- Post processing: remove_img_directives"
   #   remove_img_directives $bin_file
   # }

   if {[is_fs3_dev $dev_type]} {
       handle_security $bin_file $fw_file
   }

   return $bin_file
}

proc get_quick_query {} {
    global additional_burn_flags

    if {[regexp {(.)*(\-qq)(.)*} $additional_burn_flags]} {
        return "-qq"
    }
    return ""
}

proc generate_bin_db {img_dir} {
    global BIN_FILE_DB
    global burner
    global must_burn_flags

    # BIN_FILE_DB:
    # Major key: filename. Minor Keys: flint Query fields
    set quick_query [get_quick_query]
    set bin_files [glob -nocomplain "$img_dir/*.bin"]
    array set qry_arr {}
    foreach f $bin_files {
        set cmd "$burner -i \"$f\" $quick_query $must_burn_flags  q"
        if {![g_exec $cmd status output]} {
            g_puts "-D- Failed to query file $f: [lindex $status end]."
            g_puts "-W- Failed to query file $f. Ignoring."
            continue
        }

        array unset qry_arr
        if {[parse_query_report $output qry_arr]} {
            set BIN_FILE_DB($f@FNAME) $f
            foreach n [array names qry_arr] {
                set BIN_FILE_DB($f@$n) $qry_arr($n)
            }
        }
    }
}

proc generate_exprom_db {exprom_dir} {
    global EXPROM_DB
    global burner
    global G_PUTS_DEBUG
    global must_burn_flags

    # BIN_FILE_DB:
    # Major key: filename. Minor Keys: rom Query fields
    set bin_files [glob -nocomplain "$exprom_dir/*" ]
    array set qry_arr {}
    foreach f $bin_files {
        set cmd "$burner -i \"$f\" $must_burn_flags qrom"
        if {![g_exec $cmd status output]} {
            g_puts "-D- Failed to query file $f: [lindex $status end]."
            g_puts "-W- Failed to query file $f. Ignoring."
            continue
        } else {
            # Make sure that we don't take bin with ROM but only ROM files
            set query_cmd "$burner -i \"$f\" $must_burn_flags q"
            if {[g_exec $query_cmd st ou]} {
                continue
            }
        }
        array unset qry_arr
        array unset exprom_arr
        if {[parse_query_report $output qry_arr]} {
            if {[parse_exprom_query $qry_arr(Rom_Info) exprom_arr]} {
                set EXPROM_DB($f@FNAME) $f
                foreach n [array names exprom_arr] {
                    set EXPROM_DB($f@$n) $exprom_arr($n)
                }
            }
        }
    }
    if {$G_PUTS_DEBUG} {
        local_parray EXPROM_DB
    }
}

proc select_binary_file {dev img_dir} {
    global BIN_FILE_DB
    global burner
    global force
    global must_burn_flags

    generate_bin_db $img_dir

    array set dev_qry {}

    set quick_query [get_quick_query]
    set status {}; set output {}
    set cmd "$burner -d $dev $quick_query $must_burn_flags q"
    if {![g_exec $cmd status output]} {
        g_puts "-E- Failed to query device $dev: [lindex $status end]"
        cexit 1
    }
    parse_query_report $output dev_qry

    set dev_psid $dev_qry(PSID)
    if {$dev_psid == ""} {
        g_puts "-E- Can not auto detect FW file. Device $dev PSID may not be configured."
    }

    set match_file ""
    foreach f_k [array names BIN_FILE_DB] {
        set f [get_major_key $f_k]
        if {$BIN_FILE_DB($f@PSID) == $dev_psid} {
            set match_file $f
        }
    }

    if {$match_file == ""} {
        g_puts "-E- Directory $img_dir does not contain an image that matches the device FW (No matching PSID)"
        cexit 1
    }

    return $match_file
}

proc select_exp_rom {dev_rom_info exp_rom_dir} {
    global EXPROM_DB

    array set dev_exprom {}
    if {![parse_exprom_query $dev_rom_info dev_exprom]} {
        cexit 1
    }

    generate_exprom_db $exp_rom_dir

    #
    # EXP ROM SELECTION RULES:
    # exp_rom type and device id must have exact match between new and old roms
    # PORT: accepts exact match, or if the new rom is port independant.
    #       No auto down-grade from port independant to a specific port.
    # PROTO: accepts exact match or moving from ETH or IB to VPI.
    #
    # Selection takes the first matching file. It is assumed only a single
    # matching file will be put in the exp_rom_dir

    set match_file ""
    foreach f_k [array names EXPROM_DB] {
        set f [get_major_key $f_k]
        g_puts "-D- Checking matching of Exp ROM file: $f"
        if {$EXPROM_DB($f@type)  == $dev_exprom(type) &&
            $EXPROM_DB($f@devid) == $dev_exprom(devid)} {
            if {$EXPROM_DB($f@port) == $dev_exprom(port) ||
                $EXPROM_DB($f@port) == 0} {

                if {$EXPROM_DB($f@proto) == $dev_exprom(proto) ||
                    ($EXPROM_DB($f@proto) == "VPI" && $dev_exprom(proto) == "IB") ||
                    ($EXPROM_DB($f@proto) == "VPI" && $dev_exprom(proto) == "ETH") } {
                    set match_file $f
                }
            }
        }
    }

    if {$match_file == ""} {
        g_puts "-E- Directory $exp_rom_dir does not contain an expansion ROM image that matches the device. You can specify expansion ROM manually using the -exp_rom flag"
        cexit 1
    }

    return $match_file
}


#########################################
#    READING VPD PROC [USING MLXVPD]    # 
#########################################
proc read_vpd {} {
    global mst_dev
    global vpd_check_rw
    global vpd_rw_flag
   
    set retVal 0
    set status {}; set output {}

    set vpd_rw_flag ""
    if {$vpd_check_rw} {
	set vpd_rw_flag "-vpd_rw"
    }
    set mlxvpdCmd "mlxvpd -d $mst_dev $vpd_rw_flag" 
    g_puts "-D- Running $mlxvpdCmd"

    if {![g_exec $mlxvpdCmd status output]} {
	g_puts "-E- Failed to Read VPD: [lindex $status end]"
	set retVal 1
    }

    foreach line $output {
        g_puts "$line"
    }
    return $retVal
}

proc burn_image {_pat} {
    global nofs
    global additional_burn_flags
    global bin_file
    global mst_dev
    global burn_cmd
    global burner
    global force
    global dev_type
    global must_burn_flags

    set nofs_flag ""
    if {$nofs} {
       set nofs_flag "-nofs"
    }

    if {$force} {
       append additional_burn_flags " -y"
    }

    set cmd "$burner -d $mst_dev $nofs_flag -i \"$bin_file\" $additional_burn_flags $must_burn_flags $burn_cmd"

    # g_puts "-D- Sleep before running $cmd"
    # after 10000
    g_puts "-D- Running $cmd"

    if {[catch {eval "exec $cmd >&@ stdout <@stdin"} e]} {
       global errorCode
       if {[lindex $errorCode 2] == 7} {
           g_puts "\n-I- Image burn skipped - Image is already up to date."
       } else {
           g_puts "-E- Image burn failed: $e"
           cexit 1
       }
    } else {
        g_puts "-I- Image burn completed successfully."
    }
}


proc parse_exprom_query {qry_str _arr} {
    upvar 1 $_arr QUERY_ARR

    # Rom Info:        type=GPXE version=0.9.5 devid=25418 port=1
    array set QUERY_ARR {
        type    ""
        version ""
        devid   ""
        proto   ""
        port    0
    }

    foreach exp $qry_str {
        set param_val [split $exp "="]
        if {[llength $param_val] != 2} {
            g_puts "-E- Bad Rom Info query format: $qry_str"
            return 0
        }
        set param [lindex $param_val 0]
        set val   [lindex $param_val 1]

        if {$param != "" && [array names QUERY_ARR $param] != ""} {
            set QUERY_ARR($param) $val
        }
    }
    return 1
}

proc parse_security_level {_query_output _verify_output} {
    global SECURITY_MODE_NONE
    global SECURITY_MODE_SHA_DIGEST
    global SECURITY_MODE_RSA
    global SECURITY_MODE_UNKNOWN
    
    set isLegacyUpdateMethod "0"
    
    foreach line $_query_output {
        if {[string first "Default Update Method:" $line] != -1} {
            if {[string first "Legacy" $line] != -1 } {
                set isLegacyUpdateMethod "1"
            }
        }
    }
    foreach line $_query_output {
        if {[string first "Security Attributes:" $line] != -1} {
            if {[string first "signed-fw" $line] != -1 ||
                [string first "secure-fw" $line] != -1   } {
                if {$isLegacyUpdateMethod == "1"} {
                    return $SECURITY_MODE_UNKNOWN
                }
                return $SECURITY_MODE_RSA
            }
        }        
        if {[string first "Default Update Method:" $line] != -1} {
            if {[string first "fw_ctrl" $line] != -1 } {
                return $SECURITY_MODE_SHA_DIGEST
            }
        }
    }
    foreach line $_verify_output {
        if {[string first "type" $line] != -1} {
            if {[string first "0xa0" $line] != -1 } {
                return $SECURITY_MODE_SHA_DIGEST
            }
        }
    }
    return $SECURITY_MODE_NONE
}

proc get_temp_file {prefix} {
    set t [clock clicks]
    return ${prefix}_$t
}

proc handle_security {bin_file fw_file} {
    global SECURITY_MODE_NONE
    global SECURITY_MODE_SHA_DIGEST
    global SECURITY_MODE_RSA
    global SECURITY_MODE_UNKNOWN

   # Query the security level of the image file
   set status {};
   set output {}
   set verify_output {};

   set cmd "flint -i $bin_file q full"
   if {![g_exec $cmd status output]} {
       g_puts "-E- Failed to query the image: [lindex $status end]"
       cexit 1
   }

   set cmd "flint -i $bin_file v showitoc"
   if {![g_exec $cmd status verify_output]} {
       g_puts "-E- Failed to run 'flint verify showitoc' on the image: [lindex $status end]"
       cexit 1
   }

   set security_level [parse_security_level $output $verify_output]

    if {$security_level == $SECURITY_MODE_SHA_DIGEST} {
        g_puts "-I- Adding SHA256 digest to the image..."
        set cmd "flint -i $bin_file sign"
        if {![g_exec $cmd status output]} {
            g_puts "-E- Failed to sign the image: [lindex $status end]"
            cexit 1
        }
    } elseif {$security_level == $SECURITY_MODE_RSA} {
       g_puts "-I- Signing the image..."
       # Extract the public keys
       set public_key_file [get_temp_file ${bin_file}_public_key]
       set cmd "mic -fw $fw_file -get_sect DEV_KEY_PUBLIC.data $public_key_file"
       if {![g_exec $cmd status output]} {
           g_puts "-E- Failed to extract the public key section from the mlx file: [lindex $status end]"
           file delete $public_key_file
           cexit 1
       }

       # Extract the private key
       set private_key_file [get_temp_file ${bin_file}_private_key]
       set cmd "mic -fw $fw_file -get_sect DEV_KEY_PEM.data $private_key_file"
       if {![g_exec $cmd status output]} {
           g_puts "-E- Failed to extract the private key section from the mlx file: [lindex $status end]"
           file delete $public_key_file
           file delete $private_key_file
           cexit 1
       }

       # Extract the UUID
       set uuid_file [get_temp_file ${bin_file}_uuid]
       set cmd "mic -fw $fw_file -get_sect DEV_KEY_UUID.data $uuid_file"
       if {![g_exec $cmd status output]} {
           g_puts "-E- Failed to extract the uuid section from the mlx file: [lindex $status end]"
           file delete $public_key_file
           file delete $private_key_file
           file delete $uuid_file
           cexit 1
       }
       # Read the string
       set uuid_fp [open $uuid_file r]
       set uuid_str [read $uuid_fp]
       set uuid_str [string trim $uuid_str]
       close $uuid_fp

       #TODO: extract the forbidden versions

       set cmd "flint -i $bin_file set_public_keys $public_key_file"
       if {![g_exec $cmd status output]} {
           g_puts "-E- Failed to set the public key section into the image: [lindex $status end]"
           file delete $public_key_file
           file delete $private_key_file
           file delete $uuid_file
           cexit 1
       }

       set cmd "flint -i $bin_file --private_key $private_key_file --key_uuid \"$uuid_str\" sign"

       if {![g_exec $cmd status output]} {
           g_puts "-E- Failed to sign the image: [lindex $status end]"
           file delete $public_key_file
           file delete $private_key_file
           file delete $uuid_file
           cexit 1
       }

       file delete $public_key_file
       file delete $private_key_file
       file delete $uuid_file
   } elseif {$security_level == $SECURITY_MODE_UNKNOWN} {
       g_puts "-E- Failed to handle security mode - Unknown mode"
       cexit 1
   }

}

#Query:
#Node GUID              0x000000ae1bc1edf0
#System Image GUID      0x0000000100000001
#Node Description       MT47396 Infiniscale-III Mellanox Technologies
#Board Serial Number    NO_BSN
#PSID                   MT_024000000
proc parse_query_report {_spark_output _arr} {
    upvar 1 $_arr QUERY_ARR

    array set QUERY_ARR {
        FW_Version          ""
        Node_GUID           ""
        System_Image_GUID   ""
        Node_Description    ""
        Board_Serial_Number ""
        PSID                ""
        Device_ID           ""
        GUIDs               ""
        Rom_Info            ""
    }

    set parameterLine ""
    foreach line $_spark_output {

        set param ""
        if {$line == "Query:"} {
            continue
        }
        set param [string trim [lindex [split $line :] 0]]

        # remove dots if there are.
        regsub -all "\\." $param "" param
        # replace space with under line
        regsub -all " " $param "_" param

        set val [string trim [lindex [split $line :] 1]]

        if {$param != "" && [array names QUERY_ARR $param] != ""} {
            set QUERY_ARR($param) $val
        }
    }
    return 1
}

proc query_image_hca {} {
    global mst_dev
    global burner
    global additional_burn_flags
    global must_burn_flags

    set retVal 0
    set status {}; set output {}
    if {![g_exec "$burner -d $mst_dev $additional_burn_flags $must_burn_flags q" status output]} {
        set retVal 1
        g_puts "-E- $burner query failed : [lindex $status end]"
    }
    foreach line $output {
        g_puts "-I- $line"
    }
    return $retVal
}

proc query_image_switch {_geo_addr {_print_header 0}} {
    global nofs
    global additional_burn_flags
    global bin_file
    global mst_dev
    global burn_cmd
    global burner
    global force

    if {$force} {
       append additional_burn_flags " -y"
    }

    set status {}; set output {}
    if {![g_exec "$burner -d $mst_dev q" status output]} {
        g_puts "-E- $_geo_addr : query error : [lindex $status end]"
    } else {
        array set QARR {}
        parse_query_report $output QARR
        if {$_print_header == 1} {
            g_puts "-I- Location FW Version  Node GUID           System Image GUID   BSN                 PSID                "
        }
        g_puts "-I- [format %-8s $_geo_addr] [format %-11s $QARR(FW_Version)] [format %-19s $QARR(Node_GUID)] [format %-18s $QARR(System_Image_GUID)] \
                [format %-19s $QARR(Board_Serial_Number)] [format %-18s $QARR(PSID)]"
    }
}

proc g_exec {_cmdLine {_statusByName _NO_STATUS} {_outputByName _NO_OUTPUT} {_silent 0}} {
    global errorCode

    if {$_statusByName != "_NO_STATUS"} {
        upvar 1 $_statusByName status
    }

    if {$_outputByName != "_NO_OUTPUT"} {
        upvar 1 $_outputByName output
    }

    set retVal 1
    set execError 0
    set output {} ; set status {}

    set pref ""
    g_puts "-D- Command: $_cmdLine"

    if {[catch {set output [eval "exec $_cmdLine"]} e]} {
        set execError 1
    }

    set output [split $output \n]
    foreach line $output {
        g_puts "-D- output: $line"
    }

    if {$execError} {
        set retVal 0
        set status $errorCode
        lappend status $e
    } else {
        set status {normal}
    }

    foreach line [split $status \n] {
        g_puts "-D- status: $line"
    }

    return $retVal
}

proc format_revision {tool_name} {
   global MFT_VERSION_STR
   global TOOLS_GIT_SHA
   global BUILD_TIME

   return "$tool_name, $MFT_VERSION_STR, built on $BUILD_TIME. Git SHA Hash: $TOOLS_GIT_SHA"
}

array set Device_IDs {
    CONNECTX    {25408 25418 26418 26428 25448 26448 26468 26478 25458 26458 26438 26488 4099 4103}
    ConnectIB   {4113}
    CONNECTX4   {4115}
    CONNECTX4LX {4117}
    CONNECTX5   {4119 4121}
    CONNECTX6   {4123}
    CONNECTX6DX {4125}
    CONNECTX6LX {4127}
    CONNECTX7   {4129}
    BLUEFIELD   {41680 41681 41682}
    BLUEFIELD2  {41684 41685 41686}
    BLUEFIELD3  {41690 41691 41692}
    IS4         {48436 48437 48438}
    SX          {51000}
    SwitchIB    {52000}
    SwitchIB2   {53000}
    Quantum     {54000}
    Quantum2    {54002}
    Spectrum    {52100}
    Spectrum2   {53100}
    Spectrum3   {53104}
    Spectrum4   {53120}
    AmosGB      {53108}
}

proc get_devs_id_str {pre_str HCAS} {

    set hca_devs_num [llength $HCAS]
    set devs "              $pre_str"
    for {set i 0} {$i < $hca_devs_num} {incr i} {
        set hca_dev [lindex $HCAS $i]
        append devs " $hca_dev"

        if {$i == [expr $hca_devs_num - 1]} {
            append devs ".\n"
        } else {
            append devs ","
            if {$i != 0 && [expr ($i + 1) % 7] == 0} {
                append devs "\n                        "
            }
        }
    }
    return $devs
}

set HCAS [concat $Device_IDs(CONNECTX) $Device_IDs(ConnectIB) $Device_IDs(CONNECTX4) $Device_IDs(CONNECTX4LX) $Device_IDs(CONNECTX5) $Device_IDs(CONNECTX6)  $Device_IDs(CONNECTX6DX)  $Device_IDs(CONNECTX6LX) $Device_IDs(CONNECTX7)  $Device_IDs(BLUEFIELD) $Device_IDs(BLUEFIELD2) $Device_IDs(BLUEFIELD3)]
set switch_devs  [concat $Device_IDs(IS4) $Device_IDs(SX) $Device_IDs(SwitchIB) $Device_IDs(Spectrum) $Device_IDs(SwitchIB2) $Device_IDs(Spectrum2) $Device_IDs(Spectrum3) $Device_IDs(Spectrum4) $Device_IDs(AmosGB) $Device_IDs(Quantum) $Device_IDs(Quantum2) ]

set hca_devs_str     [get_devs_id_str  "HCAs/NICs:" $HCAS]
set switch_devs_str  [get_devs_id_str  "Switches: " $switch_devs]

set G_VSD_LEN 208
set version "[format_revision mlxburn]"

set ToolUsage {[-h][-v] <-dev mst-device|-wrimage fw-image>
   <-fw mellanox-fw-file|-image fw-image|-img_dir img_direcory|-fw_dir fw_dir>
   [-conf fw-conf-file][-nofs][-nofs_img][-striped_image][-format BINARY|IMAGE][-dev_type device-type]
   [-exp_rom <exp_rom_file>][-gb_bin_file <gb_bin_file>][-exp_rom_dir <exp_rom_dir>][-force][-conf_dir <conf_dir>][-fwver]
   [-vpd][-vpd_rw][-vpd_prog_rw <rw-keywords-file>][-vpd_set_keyword <keyword-assignment>]
   [-set_pxe_en <(port1|port2)=(enable|disable)>] [-prof_file <profiles file>]
   [-query] [-conf_dir_list <dir1,dir2,...,dirn>]}

set ToolUsage "Usage: mlxburn $ToolUsage"

set ToolHelp {
NAME
       mlxburn

DESCRIPTION

       Burn or generate FW image for Mellanox devices.
       Please see the mlxburn man page (1) for more info.

OPTIONS
       -d| -dev <mst-dev>
              Burn the image using the given MST device.

       -fw <mellanox-fw-file>
              Specify Mellanox FW released Firmware File  to  use
              (file extension is .mlx)

       -image <fw-image-file>
              Do not  generate  image.  Use  the  given  fw  image
              instead.

       -img_dir <image directory>
              Do not generate image. Select the image to burn from
              the *.bin in the given directory.

       -conf <parameter-set-file>
              FW configuration file (.ini).  Needed  when
              generating  image  (not using -dev flag) or if con-
              figuration auto detection fails.

       -wrimage <fw-image-file>
              Write the image to the given file.

       -gb_bin_file <gb_bin_file>
              Integrate the given gearbox binary file to the FW
              image.

       -exp_rom <exp-rom-file>
              Integrate the given expansion rom file to the FW
              image.
              If the exp-rom-file is set to "AUTO", expansion
              rom file is auto detected from the files rom in the
              exp_rom_dir (see below).
              NOTE: Exp rom auto detection is done for devices that
              are already burned with an exp-rom image.
              If "-exp_rom AUTO" is specified for a device with no
              exp-rom, it would be burnt with no exp rom.
              To add exp-rom to a device, manually supply the exp rom
              file to use.

       -exp_rom_dir <exp_rom_dir>
              The directory in which to look for expansion rom file
              when "-exp_rom AUTO" is specified. By default, exp-rom
              files are searched in <fw file directory>/exp_rom/*

       -fwver
              When a device is given: Display current loaded firmware version (Deprecated).
              When a FW file is given (-fw flag): Display the file FW version.

       -query
              Query mode. Query the HCA or Switch device FW image.

       -vpd
              Display the read only section of the PCI VPD (Vital
              Product Data) of the given device.
              NOTE: VPD feature may not be supported on certain board types.

       -vpd_rw
              Display also the read/write section of the PCI VPD of the
              given device.

ADVANCED OPTIONS
       -nofs
              When specified, burn process will not be failsafe.

       -nofs_img
              When  specified,  generated image will not be fail-
              safe, and burn process will not be failsafe.

       -striped_image
              When specified, generated image will be in striped
              format, and will indicate that the image is in striped
              format when queried.

       -force
              None interactive mode. Assume "yes" for all user
              questions.

       -format <BINARY|IMAGE>
              Specify which image format to use. Can be specified
              only with the -wrimage flag.  Default is BINARY.

       -conf_dir <dir>
              When specified, the auto detected configuration files
              will be looked for in the given directory, instead of
              in the firmware file directory.
              Applicable for burn operation.

       -conf_dir_list <dir1,dir2,...,dirn>
              When specified, the auto detected configuration files
              will be looked for in the given directories, instead of
              in the firmware file directory.
              Applicable for burn operation.

       -fw_dir <dir>
              When specified, the auto detected fw files
              will be looked for in the given directory.
              Applicable for burn operation.

       -dev_type <mellanox-device-number>
              mlxburn  must know the device type in order to work
              properly.  Use this flag if device type auto-detec-
              tion fails.
              Supported Mellanox device types:Supported Mellanox device types:
              HCAs: [ 25408 / 25418 / 26418 / 26428 / 25448 / 26448 / 26468 / 26478 / 25458 / 26458 / 26438 / 26488 / 4099 / 
              4103 / 4113 / 4115 / 4117 / 4119 / 4121 / 4123 / 4125 / 4127 / 4129 / 41680 / 41681 / 41682 / 41684 / 41685 / 41686 ]
              Switches: [ 48436 / 48437 / 48438 / 51000 / 52000 / 52100 / 53000 / 53100 / 53104 / 53108 / 53120 / 54000 / 54002 ]
              Example: -dev_type 23108
    Spectrum4   {53120}
    AmosGB      {53108}

}

append ToolHelp $hca_devs_str $switch_devs_str

append  ToolHelp {

       -vpd_prog_rw <rw-keywords-file>
              Program the VPD-W tag (the writable section of the
              VPD) with the data given in the rw-keywords-file.
              File lines format: "KEYWORD = VALUE".
              In order to set binary data to a keyword, add ":BIN"
              to the keyword name. in this case, the data is a hexa-
              decimal string of even length. Example file:
                 V1 = MY-ASCII-KEYWORD
                 V2:BIN = 1234abcd

              White spaces before and after VALUE are trimmed.

       -vpd_set_keyword <keyword-assignment>
              Add or change a keyword value in the VPD-W tag (the
              writable section of the VPD) with the data given in
              the keyword-assignment string. The string format is
              identical to a line in the rw-keywords-file described
              above. Other keywords in the VPD-W tag are not affected
              by this operation.


       -vsd <string>
              Write this string, of up to 208 characters, to VSD section.

       In addition to the above flags, Mlxburn can also accept the following
       flags/options, which are passed to the underlying burning tool:

      -banks
      -use_image_ps
      -skip_is
      -mac
      -guid
      -sysguid
      -ndesc
      -bsn
      -use_image_guids
      -pe_i2c
      -se_i2c
      -is3_i2c
      -no
      -qq
      -uid
      -log
      -blank_guids
      -flash_params
      -allow_psid_change
      -no_flash_verify
      -use_image_rom
      -override_cache_replacement
      -ocr
      -ignore_dev_data
      -use_dev_rom
      -no_fw_ctrl

       See the flint tool documentation for HCA/4th gen switches/Bridge burning options.

PRODUCTION OPTIONS

       These options are only applicable for Connect-IB and ConnectX-4 and above Adapter Cards
       These options are relevant when generating an image for initial burn. The image contains
       the VPD and the GUIDs that are in a read-only area on flash.

       -vpd_r_file <vpd_r_file>
              Embed the given VPD Read-Only section in the generated image. The vpd_r_file should
              contain the vpd read only section and the first dword of the vpd write-able section.
              The file is in binary format, and its size must be a multiple of 4 bytes. Please
              refer to PCI base spec for VPD structure info.

       -base_guid <GUID>
              Set the given GUID as the image base GUID. The base GUID is used to derive GUIDs and MACs
              for the HCA ports. It is assumes that 16 GUIDs (base_guid to base_guid + 15) are reserved
              for the card.
              *On ConnectX4: only GUIDs will be derrived according to the HCA's configuration.

       -base_mac <MAC>
              Set the given MAC as the image base MAC. the base MAC is used to derrvie MACs for the HCA
              ports according to the device configuration (Connect-IB and ConnectX-4 and above Adapter Cards only).

INFO OPTIONS
       -h
              display a short help text

       -V [INFORM/WARNING/DEBUG]
              Set verbosity level. Default is WARNING.

       -v
              Print version info and exit.
}

set IS4_HW_ID          435
set CX_MAIN_ID         25408
set SX_HW_ID           581
set CX_HW_ID           400
set CX3_HW_ID          501
set CX3_PRO_HW_ID      503
set CIB_HW_ID          511
set CX4_HW_ID          521
set CX4LX_HW_ID        523
set CX5_HW_ID          525
set CX6_HW_ID          527
set CX6DX_HW_ID        530
set CX6LX_HW_ID        534
set CX7_HW_ID          536
set BF_HW_ID           529
set BF2_HW_ID          532
set BF3_HW_ID          540
set SWITCHIB_HW_ID     583
set SPECTRUM_HW_ID     585
set SWITCHIB2_HW_ID    587
set QUANTUM_HW_ID      589
set SPECTRUM2_HW_ID    590
set MENHIT_HW_ID       111
set SPECTRUM3_HW_ID    592
set AMOSGB_HW_ID       594
set SPECTRUM4_HW_ID    596
set QUANTUM2_HW_ID     599

proc is_is4 {dev} {
   global Device_IDs
   global IS4_HW_ID
   if {$dev == $IS4_HW_ID || [lsearch $Device_IDs(IS4) $dev] != -1} {
      return 1
   }
   return 0
}

proc is_sx {dev} {
   global Device_IDs
   global SX_HW_ID
   if {$dev == $SX_HW_ID || [lsearch $Device_IDs(SX) $dev] != -1} {
      return 1
   }
   return 0
}

proc is_switchib {dev} {
   global Device_IDs
   global SWITCHIB_HW_ID
   if {$dev == $SWITCHIB_HW_ID || [lsearch $Device_IDs(SwitchIB) $dev] != -1} {
      return 1
   }
   return 0
}

proc is_switchib2 {dev} {
   global Device_IDs
   global SWITCHIB2_HW_ID
   if {$dev == $SWITCHIB2_HW_ID || [lsearch $Device_IDs(SwitchIB2) $dev] != -1} {
      return 1
   }
   return 0
}

proc is_quantum {dev} {
   global Device_IDs
   global QUANTUM_HW_ID
   if {$dev == $QUANTUM_HW_ID || [lsearch $Device_IDs(Quantum) $dev] != -1} {
      return 1
   }
   return 0
}

proc is_quantum2 {dev} {
   global Device_IDs
   global QUANTUM2_HW_ID
   if {$dev == $QUANTUM2_HW_ID || [lsearch $Device_IDs(Quantum2) $dev] != -1} {
      return 1
   }
   return 0
}

proc is_spectrum {dev} {
   global Device_IDs
   global SPECTRUM_HW_ID
   if {$dev == $SPECTRUM_HW_ID || [lsearch $Device_IDs(Spectrum) $dev] != -1} {
      return 1
   }
   return 0
}

proc is_spectrum2 {dev} {
   global Device_IDs
   global SPECTRUM2_HW_ID
   if {$dev == $SPECTRUM2_HW_ID || [lsearch $Device_IDs(Spectrum2) $dev] != -1} {
      return 1
   }
   return 0
}

proc is_spectrum3 {dev} {
   global Device_IDs
   global SPECTRUM3_HW_ID
   if {$dev == $SPECTRUM3_HW_ID || [lsearch $Device_IDs(Spectrum3) $dev] != -1} {
      return 1
   }
   return 0
}

proc is_spectrum4 {dev} {
   global Device_IDs
   global SPECTRUM4_HW_ID
   if {$dev == $SPECTRUM4_HW_ID || [lsearch $Device_IDs(Spectrum4) $dev] != -1} {
      return 1
   }
   return 0
}

proc is_amg {dev} {
   global Device_IDs
   global AMOSGB_HW_ID
   if {$dev == $AMOSGB_HW_ID || [lsearch $Device_IDs(AmosGB) $dev] != -1} {
      return 1
   }
   return 0
}

proc is_connectx {dev} {
   global Device_IDs
   global CX_HW_ID
   global CX3_HW_ID
   global CX3_PRO_HW_ID
   global CX_MAIN_ID
   if {$dev == $CX_HW_ID || $dev == $CX_MAIN_ID || $dev == $CX3_HW_ID || $dev == $CX3_PRO_HW_ID || [lsearch $Device_IDs(CONNECTX) $dev] != -1} {
      return 1
   }
   return 0
}

proc is_connectib {dev} {
    global CIB_HW_ID
    global Device_IDs
    if {$dev == $CIB_HW_ID || [lsearch $Device_IDs(ConnectIB) $dev] != -1} {
        return 1
    }
    return 0
}

proc is_connectx4 {dev} {
    global CX4_HW_ID
    global Device_IDs
    if {$dev == $CX4_HW_ID || [lsearch $Device_IDs(CONNECTX4) $dev] != -1} {
        return 1
    }
    return 0
}

proc is_connectx4lx {dev} {
    global CX4LX_HW_ID
    global Device_IDs
    if {$dev == $CX4LX_HW_ID || [lsearch $Device_IDs(CONNECTX4LX) $dev] != -1} {
        return 1
    }
    return 0
}

proc is_connectx5 {dev} {
    global CX5_HW_ID
    global Device_IDs
    if {$dev == $CX5_HW_ID || [lsearch $Device_IDs(CONNECTX5) $dev] != -1} {
        return 1
    }
    return 0
}

proc is_connectx6 {dev} {
    global CX6_HW_ID
    global Device_IDs
    if {$dev == $CX6_HW_ID || [lsearch $Device_IDs(CONNECTX6) $dev] != -1} {
        return 1
    }
    return 0
}

proc is_connectx6dx {dev} {
    global CX6DX_HW_ID
    global Device_IDs
    if {$dev == $CX6DX_HW_ID || [lsearch $Device_IDs(CONNECTX6DX) $dev] != -1} {
        return 1
    }
    return 0
}

proc is_connectx6lx {dev} {
    global CX6LX_HW_ID
    global Device_IDs
    if {$dev == $CX6LX_HW_ID || [lsearch $Device_IDs(CONNECTX6LX) $dev] != -1} {
        return 1
    }
    return 0
}

proc is_connectx7 {dev} {
    global CX7_HW_ID
    global Device_IDs
    if {$dev == $CX7_HW_ID || [lsearch $Device_IDs(CONNECTX7) $dev] != -1} {
        return 1
    }
    return 0
}

proc is_bluefield {dev} {
    global BF_HW_ID
    global Device_IDs
    if {$dev == $BF_HW_ID || [lsearch $Device_IDs(BLUEFIELD) $dev] != -1} {
        return 1
    }
    return 0
}

proc is_menhit {dev} {
    global MENHIT_HW_ID
    global Device_IDs
    if {$dev == $MENHIT_HW_ID} {
        return 1
    }
    return 0
}

proc is_bluefield2 {dev} {
    global BF2_HW_ID
    global Device_IDs
    if {$dev == $BF2_HW_ID || [lsearch $Device_IDs(BLUEFIELD2) $dev] != -1} {
        return 1
    }
    return 0
}

proc is_bluefield3 {dev} {
    global BF3_HW_ID
    global Device_IDs
    if {$dev == $BF3_HW_ID || [lsearch $Device_IDs(BLUEFIELD3) $dev] != -1} {
        return 1
    }
    return 0
}

proc is_fs3_dev {dev} {
    if {[is_connectx4lx $dev] || [is_connectx5 $dev] || [is_connectx6 $dev] || [is_connectx6dx $dev] || [is_connectx6lx $dev] || [is_connectx7 $dev] || [is_bluefield $dev] || [is_bluefield2 $dev] || [is_bluefield3 $dev] || [is_connectx4 $dev] || [is_connectib $dev] || [is_switchib $dev] || [is_spectrum $dev] || [is_switchib2 $dev] || [is_quantum $dev] || [is_quantum2 $dev] || [is_spectrum2 $dev] || [is_spectrum3 $dev] || [is_spectrum4 $dev] || [is_amg $dev]} {
        return 1
    }
    return 0
}

proc is_flash {dev} {
    if {[is_connectx  $dev] ||
        [is_is4       $dev] ||
        [is_sx        $dev] ||
        [is_fs3_dev   $dev] ||
        [is_cable_dev $dev]} {

            return 1
    }
    return 0
}

proc is_cable_dev {dev} {
    if {[is_menhit  $dev]} {
            return 1
    }
    return 0
}

proc Usage {} {
   global ToolUsage
   puts $ToolUsage
}

proc Help {} {
   global ToolHelp

   Usage
   puts $ToolHelp
}

proc read_psid_from_dev {mst_dev burner} {
   set cmd "$burner -d $mst_dev q"

   set res [eval "exec -keepnewline $cmd"]

   # Flint query return format:
   # Board ID: <VSD>    (<PSID>)

   # Spark query format:
   # PSID    <PSID>
   if {[regexp -lineanchor {^Board ID:.+\((.+)\)} $res d1 psid]} {

   } elseif {[regexp -lineanchor {PSID:?\s*:?\s(\S+)} $res d1 psid] } {

   } else {
      set psid ""
   }

   g_puts "-D- Read PSID \"$psid\" from dev $mst_dev"

   return $psid
}

proc get_psid_from_ini {f} {

   set fh [::ini::open $f r]

   if       {[::ini::exists $fh  "PSID"    "PSID"]} {
      set psid [::ini::value $fh "PSID"    "PSID"]
   } elseif {[::ini::exists $fh  "ADAPTER" "PSID"]} {
      set psid [::ini::value $fh "ADAPTER" "PSID"]
   } elseif {[::ini::exists $fh  "image_info" "psid"]} {
      set psid [::ini::value $fh "image_info" "psid"]
   } else {
      error "Can't find psid assignment in file $f ."
   }

   ::ini::close $fh

   return $psid
}

#
# Read PSID from or .ini file
#

proc read_psid_from_conf {f} {
   set psid ""
   switch [file extension $f] {
      .ini -
      .INI {
         set psid [get_psid_from_ini $f]
      }
      default {
         error "Don't know how to read PSID for file with unsupported $f."
      }
   }

   return $psid

}


#
# get_matching_conf_file:
#
# Search in the directory of the FW file a ini file tha assigns the given PSID.
# An error is issued if no file is found, or if more than 1 file is found.
#
# Returns: The matching conf (ini) file.
#
set MORE_PSID_MSG "Defined in more than one file"
proc get_matching_conf_file {psid search_dir} {
    global MORE_PSID_MSG
   # get all conf file in the directory:

   set conf_file_in_path     [glob "$search_dir/*.ini" "$search_dir/*.INI" ]

   if {[llength $conf_file_in_path] == 0} {
      error "No FW configuration files found in $search_dir ."
   }

   set conf_file ""

   foreach f $conf_file_in_path {
      if {[catch {set curr_psid [read_psid_from_conf $f]} e]} {
         g_puts "-W- Can't extract PSID for file $f: $e ."
      } else {
         g_puts "-D- get_matching_conf_file: File $f --> PSID: $curr_psid"

         if {$curr_psid == $psid} {
            # Match! - Now check that there's no previous match.
            # Allow .ini file to override file with the same PSID

            if {$conf_file == ""} {
               set conf_file $f
            } else {
               if { $conf_file !=  $f } {
                  error "PSID \"$psid\" $MORE_PSID_MSG: $conf_file and $f"
               }
            }
         }
      }
   }

   if {$conf_file == ""} {
      error "No configuration file found for PSID \"$psid\" in $search_dir"
   }

   return $conf_file
}


proc remove_img_directives {img_file} {
   set tmp_file "$img_file.tmp"

   set cmd "egrep -v IMAGE|FW_VER|DEVICE_TYPE|DEVMAP $img_file > $tmp_file"

   if {[catch {eval "exec $cmd"} e]} {
      g_puts "-E- Image post processing failed: $e"
      cexit 1
   }

   file rename -force $tmp_file $img_file
}



proc cr_read {dev addr {i2c_addr ""}} {
   global errorCode errorInfo
   global use_mstflint

   # Try the hard coded mst path
   set mread  "/usr/mst/bin/mcra"

   if {![file executable $mread]} {
      # if not found , assume it's in the path
      set mread "mcra"
   }

   if {$use_mstflint} {
       set mread "mstmcra"
   }

   if {$i2c_addr != ""} {
       set i2c_addr  "-s $i2c_addr"
   }

   set cmd "$mread $i2c_addr $dev $addr"

    g_puts "-D- cr_read: Running $cmd"
   set e ""
   if {[catch {set reg [eval "exec $cmd"]} e]} {
         error "Can't access device $dev: $e"
   }

   g_puts "-D- cr read: $e . "
   return $reg
}

proc i2c_cr_read {dev addr i2c_addr i2c_addr_width} {
   global errorCode errorInfo

   # Try the hard coded mst path
   set i2c  "/usr/mst/bin/i2c"

   if {![file executable $i2c]} {
      # if not found , assume it's in the path
      set i2c "i2c"
   }

   set e ""

   g_puts "-D- $i2c -a $i2c_addr_width -d 4 $dev r $i2c_addr $addr"
   if {[catch {set devid [exec $i2c -a $i2c_addr_width -d 4 $dev r $i2c_addr $addr]} e]} {
      error "Can't access device $dev: $e"
   }

   g_puts "-D- i2c cr read: $devid . "

   return "0x$devid"
}

proc prepare_dev_info_list {dev_id rev} {
    set dev_list {}

    lappend dev_list    $dev_id
    lappend dev_list    $rev
    return $dev_list
}

proc get_dev_id {dev} {
    set hca_err ""

    # Check if HCA
    if {![catch {set hdev_id [cr_read $dev 0xf0014  ]} hca_err]} {
       set rev [format %#x [expr (($hdev_id & 0xff0000) >> 16)]]
       set hdev_id   [expr $hdev_id & 0xffff]
       g_puts "-D- Read HCA dev id: $hdev_id, rev: $rev"

       if {[is_flash $hdev_id]} {
           return [prepare_dev_info_list $hdev_id $rev]
       }
    }

    if {$hca_err != ""} {
        # If all 3 cr reads failed - Assume there's a general error with the device  -arbitrarily display the first error
        error $hca_err
    }

    error "read unknown device id for $dev"
}

proc get_fw_ver {mst_dev} {

   set dev_id [lindex [get_dev_id $mst_dev] 0]

    if {[is_flash $dev_id]} {
        if {[is_connectx $dev_id]} {
            set rev_base 0x1f064
        } elseif {[is_is4 $dev_id] || [is_sx $dev_id]} {
            set rev_base 0x60040
        } elseif {[is_fs3_dev $dev_id]} {
            error "-fwver is deprecated. to view Firmware version use -query instead."
        } else {
            set rev_base 0x82478
        }
        set maj     [cr_read $mst_dev $rev_base]
        set min_sub [cr_read $mst_dev [expr $rev_base + 4]]

        set maj [expr $maj >> 16]
        set min [expr $min_sub >> 16]
        set sub [expr $min_sub & 0xffff]
    } else {
        error "-fwver is not supported for device MT$dev_id"
    }

    return "$maj.$min.$sub"
}

# clean exit
proc cexit { { ret_val 0 } } {
   global files_to_delete
   g_puts "-D- Deleting: $files_to_delete "
   foreach f $files_to_delete {
      file delete $f
   }

   exit $ret_val
}

proc get_absolute_path { relative_dir } {
    global tcl_platform
    if { $tcl_platform(platform) != "unix" } {
        return $relative_dir
    }

    exec cd $relative_dir
    set absolute_dir [pwd]
    exec cd -
    return $absolute_dir
}

proc fix_file_path {file_path} {
    set out_file [file normalize $file_path]
    # set out_file [string map { \{ \\\{  \} \\\} } $out_file]
    return $out_file
}

# on linux if symbolic link followed by .. normalize go crazy
proc fix_normalize_bug {dir_name} {
    global tcl_platform
    if {$tcl_platform(platform) == "windows"} {
        return $dir_name
    }
    set splited_dir_name [split $dir_name "/"]
    set output ""
    foreach dir $splited_dir_name {
        if { [ string length $dir ] == 0 } {
            set output  "${output}/"
            continue
        } else {
            set output "${output}${dir}/"
        }
        set link ""
        while {![catch { set link [ file readlink $output ] } e]} {
            if {[string index $link 0 ] == "/"} {
                set output $link
            } else {
                set base_path [file dirname $output]
                set output "${base_path}/${link}/"
            }
        }
        set output [ file normalize $output ]
        set output "${output}/"
    }
    return $output
}

proc file_readable_fixed name {
    set rc [catch {open $name} fp]
    if {$rc==0} {close $fp}
    expr {$rc==0}
}

proc fix_dir_path {dir_name} {

    set dir_name [string trim $dir_name]
    if { $dir_name == "" } {
        return $dir_name
    }
    if { [file isfile $dir_name] } {
        g_puts "-E- The given dir \"$dir_name\" is not a directory"
        cexit 1
    }

    set tmp_dir_name $dir_name
    set dir_name [fix_normalize_bug $dir_name]
    set dir_name [file normalize $dir_name]

    if {[catch {set dir_name [glob -type d $dir_name]} e]} {
        if {[catch {set dir_name [file dirname $dir_name]} e]} {
            g_puts "-E- Could not extract dir from :\"$dir_name\""
            cexit 1
        }
    }

    if {[llength $dir_name] > 1} {
        g_puts "-E- Expected dir, got list of directories \"$tmp_dir_name\""
        cexit 1
    }
    catch {set dir_name [file link $dir_name]}

    return $dir_name
}

proc fix_list_dir_path {dir_name} {

    set dir_name [string trim $dir_name]
    if { [file isfile $dir_name] } {
        g_puts "-E- The given dir \"$dir_name\" is not a directory"
        cexit 1
    }

    set dir_name [fix_normalize_bug $dir_name]
    set dir_name [file normalize $dir_name]
    if {[catch {set dir_name [glob -type d $dir_name]} e]} {
        if {[catch {set dir_name [file dirname $dir_name]} e]} {
            g_puts "-E- Could not extract dir from :\"$dir_name\""
            cexit 1
        }
    }

    set new_list_dir ""
    foreach dir $dir_name {
        if {[catch {set new_list_dir [concat $new_list_dir [fix_dir_path $dir]]} e]} {
            set new_list_dir [concat $new_list_dir $dir]
        }
    }
    set dir_name $new_list_dir

    return $dir_name
}
######################################################################
#
# MAIN
#
######################################################################


#
# Get params
#
set fw_file               ""
set fw_dir                ""
set mst_dev               ""
set conf_file             ""
set conf_dir              "MLXBURN-AUTO"
set conf_dir_list         ""
set bin_file              ""
set img_dir               ""

set prof_file             ""
set hash_file             ""
set hash_dir              ""

set input_img             ""

set additional_burn_flags ""
set additional_mic_flags  ""
set must_burn_flags       ""
set format                ""
set img_args              ""
set burn_args             ""
set dev_type              ""
set g_dev_rev             ""
set g_dev_id              ""
set gb_bin_file           ""
set exp_rom               ""
set exp_rom_dir           ""
set user_data             ""
set vpd_r_file            ""
set base_guid             ""
set base_mac              ""
set nofs                  0
set nofs_img              0
set force                 0
set burn_cmd              "b"
set remove_directives     1
set show_fwver            0
set gen_tmp_img           ""

set show_vpd              0

set default_vpd_access "PCI"
set vpd_access            ""
set vpd_conf              ""
set vpd_check_rw          0
set vpd_prog_file         ""
set vpd_set_keyword       ""
set vpd_set_pxe           ""

set sw_sys_mode           0
set query_mode            0
set pattern               ""
set got_pattern           0
set got_range             0
set r_from                0x6c
set r_to                  0x6c
set list_only_mode        0
set use_ibspark           0
set use_mstflint          0


set files_to_delete {}

# package require getopt

#
#set ToolUsage {[-h] <-dev  mst-device|-wrimage  fw-image>  <-fw Mellanox-fw-release|-image fw-image>
#    [-conf fw-conf-file][-nofs][-format BINARY|IMAGE][-img_args args][-burn_args args][-dev_type device-type]}
#

#options
# Misc options are handled by mlxburn.
set misc_opts "h v help V: tmp_img: fwver nofs_img vpd_conf: d: dev: f: fw: fw_dir: conf: c: conf_dir: conf_dir_list: image: i: wrimage: dev_type: format: img_args: burn_args: org_img exp_rom: gb_bin_file: exp_rom_dir: prof_file: user_data: force sw_sys: s: pattern: p: list l range: r: query q inband ul img_dir: vpd_r_file: base_guid: base_mac:"
set vpd_opts  "vpd vpd_prog_rw: vpd_access: vpd_rw vpd_set_keyword: set_pxe_en:"
# burn options are sent to the burning tool
set common_opts    "striped_image blank_guids"
set mic_opts       "no_vsd_swap"
set burn_opts      "allow_psid_change nofs  byte_mode use_image_ps skip_is no qq use_image_rom use_image_guids no_flash_verify ocr override_cache_replacement ignore_dev_data use_dev_rom no_fw_ctrl"
set burn_opts_flag "banks: uid: log: uids: mac: macs: guid: guids: sysguid: vsd: ndesc: bsn: pe_i2c: pe: se_i2c: se: is3_i2c: flash_params:"
set all_opts       "$misc_opts $vpd_opts $burn_opts $burn_opts_flag $mic_opts $common_opts"

# for tclchecker warning masking:
set opt ""
set arg ""

array set args {}

while { [ set err [ getopt $argv $all_opts opt arg ]] } {
   if { $err < 0 } then {
      puts "-E- Wrong usage: $opt"
      Usage
      exit 2
   } else {
       g_puts "-D- additional_mic_flags = $additional_mic_flags"

      switch -exact -- $opt {
         h {Help; exit 0}
         help {Help; exit 0}
         v {puts $version; exit 0}
         V {
            set verbosemode "-V $arg"
            switch -- $arg {
               DEBUG {
                  set G_PUTS_DEBUG 1
                  set G_PUTS_WARNING 1
                  set G_PUTS_INFORM 1
               }
               WARNING {
                  set G_PUTS_WARNING 1
                  set G_PUTS_INFORM 1
               }
               INFORM {
                  set G_PUTS_WARNING 0
                  set G_PUTS_INFORM 1
               }
               default {
                  puts ""
                  puts "-E- Wrong verbose mode ($arg). Supported modes: DEBUG, WARNING, INFORM."
                  puts ""
                  exit 1
               }
            }
         }
         f -
         fw        {set fw_file    $arg}
         fw_dir    {set fw_dir     $arg}
         dev -
         d         {set mst_dev    $arg}
         c -
         conf      {set conf_file   $arg}
         conf_dir  {set conf_dir    $arg}
         conf_dir_list {set conf_dir_list [split $arg ','];}
         i -
         image     {set input_img  $arg}
         img_dir   {set img_dir    $arg}
         wrimage   {set bin_file   $arg}
         format    {set format     $arg}
         prof_file {set prof_file   $arg}
         img_args  {append additional_mic_flags  " $arg"}
         burn_args {append additional_burn_flags " $arg"}
         dev_type  {set dev_type   $arg}
         nofs      {set nofs       1}
         force     {set force      1}
         nofs_img  {set nofs       1; set nofs_img  1}
         org_img   {set remove_directives 0}
         gb_bin_file {set gb_bin_file $arg}
         exp_rom     {set exp_rom     $arg}
         exp_rom_dir {set exp_rom_dir $arg}
         user_data   {set user_data  $arg}
         vpd_r_file  {set vpd_r_file $arg}
         base_guid   {set base_guid  $arg}
         base_mac    {set base_mac  $arg}
         fwver       {set show_fwver 1}
         tmp_img     {set gen_tmp_img $arg}

         vpd         {set show_vpd   1}
         vpd_access  {set vpd_access $arg; set show_vpd   1}
         vpd_conf    {set vpd_conf   $arg; set show_vpd   1}
         vpd_rw      {set vpd_check_rw  1; set show_vpd   1}
         vpd_prog_rw {set vpd_check_rw  1; set show_vpd   1; set vpd_prog_file  $arg}
        vpd_set_keyword {set vpd_check_rw 1; set show_vpd   1; set vpd_set_keyword $arg}
	    set_pxe_en      {set vpd_check_rw 1; set show_vpd   1; set vpd_set_pxe $arg}


         # The mic flags
         no_vsd_swap  { append additional_mic_flags  " -$opt" }

         # The flags that are used by both of the mic and the burner
         blank_guids -
         striped_image { append additional_burn_flags " -$opt"; append additional_mic_flags  " -$opt"}
         vsd {  set vsd_len [string length $arg]
                if { ${vsd_len} > ${G_VSD_LEN}} {
                    g_puts "-E- size of vsd ($vsd_len) is too long , it can be up to ${G_VSD_LEN}"
                    exit 1
                }
                append additional_burn_flags " -$opt \"$arg\""
                global vsd_str
                set vsd_str $arg
                # append the correct mic flag after getting the image format
             }

         # The burner flags
         nofs -
         byte_mode -
         allow_psid_change -
         skip_is -
         no -
         qq -
         use_image_ps -
         use_image_rom -
         no_flash_verify -
         use_image_guids -
         uid -
         uids -
         log -
         banks -
         guid -
         guids -
         mac -
         macs -
         sysguid -
         ndesc -
         bsn -
         pe_i2c -
         pe -
         se_i2c -
         se -
         flash_params -
         is3_i2c { append additional_burn_flags " -$opt $arg" }

         # The flags that should be used in all the 'flint' commands
         ocr -
         ignore_dev_data - 
         use_dev_rom -
         no_fw_ctrl -
         override_cache_replacement  { append must_burn_flags " -$opt" }

         # The sys flags
         sw_sys -
         s {set sw_sys_mode 1; set sw_sys_name $arg}
         pattern -
         p {set got_pattern 1; set pattern $arg}
         list -
         l {
            set list_only_mode 1
         }
         range -
         r {
            set range [split $arg :];
            if {[llength $range] != 2} {
                puts "Invalid range: $arg , syntax is from:to"
                exit 1
            }
            set r_from [lindex $range 0];
            set r_to [lindex $range 1];
            set got_range 1;
         }
         query -
         q {set query_mode 1}

         inband {
            set use_ibspark 1
         }

         ul {
            set use_mstflint 1
         }

         default {

            puts "Wrong usage: $opt"
            puts $ToolUsage
            exit 1
         }
      }

      set args($opt) $arg
   }
}

set left_args [ lrange $argv $optind end ]
if {[llength $left_args]} {
   puts "-E- illegal parameter(s) used : $left_args"
   Usage
   exit 1
}

if {$tcl_platform(platform) == "windows"} {
     if { $use_mstflint } {
        g_puts "-E- -ul flag is not supported on Windows"
        exit 1
     }
}

if {$mst_dev != ""} {
    if { $tcl_platform(platform) == "unix" } {
        if {[catch {set user_id [eval "exec id -u"]} e]} {
            # Support for ESXi
            if { $tcl_platform(user) != "root" } {
                g_puts "-E- Failed get the user ID: $e."
            } else {
                set user_id 0
            }
        } else {
            if {$user_id != 0} {
                puts "-E- Only root can access the MST device by this application"
                exit 1
            }
        }
    }
}


#
# Check params:
#
if {$use_ibspark} {
    g_puts "-E- The \"-inband\" flag is deprecated. To perform inband burn, simply specify the inabnd device name after the \"-d\" flag."
    exit 1

}

if {($got_pattern || $list_only_mode) && !$sw_sys_mode} {
    g_puts "-E- -pattern and -range are allowed in switch system mode only, use -sw_sys flag."
    exit 1
}
if {$query_mode && ($fw_file != "" || $fw_dir != "")} {
    g_puts "-E- -fw/fw_dir flags are not allowed in this mode."
    exit 1
}
#Check if the user supplied flags for query without the query command
set quick_query [get_quick_query]
if {!$query_mode && $quick_query != ""} {
    g_puts "-E- wrong flag combination -qq, should be used with -query"
    exit 1
}
if {$sw_sys_mode} {

    if {!$got_pattern} {
        set pattern \\*
    }
    if {$mst_dev == ""} {
       g_puts "-E- -dev flag must be provided in switch system mode."
       Usage
       exit 1
    }
}

# If mst_dev is BDF (Bus-Device-Function)
# Normlize it 0000:1a:00:0 ==> 1a:00:0
set orig_mst_dev $mst_dev
if {$mst_dev != ""} {
    set bdfPattern {\w{4}:\w{2}:\w{2}.\w{1}}
    if {[regexp $bdfPattern $mst_dev]} {
        g_puts "-D- Removing domain from the PCI address if it's zeros ... $mst_dev"
        regsub {^0000:} $mst_dev {} mst_dev
    }
}

if        {$show_fwver} {
    if {$fw_file == "" && $mst_dev == ""} {
       g_puts "-E- Either -dev or -fw flags must be specified when -fwver flag is given"
       Usage
       exit 1
    }
    if {$fw_file != "" && $mst_dev != ""} {
       g_puts "-E- Only one of -dev or -fw flags can be specified when -fwver flag is given"
       Usage
       exit 1
    }
}


if        {$show_vpd} {
    if {$mst_dev == ""} {
       g_puts "-E- -dev must be specified for VPD operations."
       Usage
       exit 1
    } else {
       if {$vpd_set_keyword != ""} {
	  set op "set_keyword"
	  set tag_only 0
       } elseif {$vpd_set_pxe != ""} {
	  set op "set_pxe_en"
          set tag_only 0
       } elseif {$vpd_prog_file != ""} {
	  set op "program"
          set tag_only 1
       } else {
          set op "read"
          set tag_only 0
       }

       if { [catch { exec vmware -v } msg] } {
           # do nothing (not vmware)
       } else {
           # default vpd access in vmware is I2C since we dont have accss to PCI
           set default_vpd_access "I2C"
       }
       set in ""
       if {[catch {
          if {$vpd_access == ""} {
              set vpd_access $default_vpd_access
          } else {
              if {$op == "read"} {
                  error "vpd_access flag is not suppored in VPD read operations."
              }
          }
          if {$op == "read"} {
              cexit [read_vpd]
          } else {
              set os $tcl_platform(os)
              if {[string first "Linux" $os] != -1 ||
                  [string first "Windows" $os] != -1} {
              } else {
                  error "Write VPD is not supported in this platform"
              }
          }
          
          vpd_access_setup $orig_mst_dev $vpd_access $vpd_conf
          read_vpd_to_vars $tag_only
          if {$op == "program"} {
             write_vpd_rw $vpd_prog_file ""
          } elseif {$op == "set_keyword"} {
	     set in " in"
             write_vpd_rw "" $vpd_set_keyword
          } elseif {$op == "set_pxe_en"} {
             set in " in"
	     set_vpd_pxe $vpd_set_pxe
          } else {
	     g_puts "-E- Unknown Operation $op"
          }
       } e] } {
          g_puts "-E- Failed to $op$in VPD: $e"
	  g_puts "-D- errInfo=\"$errorInfo\" errCode=\"$errorCode\""
          exit 1
       }
       exit 0
    }
}

if       {($fw_file != "" || $input_img != "") && $list_only_mode} {
   g_puts "-E- -fw and -image flags cannot be specified in List mode (-list)."
   Usage
   exit 1
}

if       {$got_range && !$list_only_mode} {
    g_puts "-E- -range flag can only be specified in List mode, use the -list flag."
    Usage
    exit 1
}

if       {($fw_file == "" && $input_img == "" && $img_dir == "" && $fw_dir == "") && !$list_only_mode && !$query_mode && !$show_fwver} {
   g_puts "-E- Either a FW file, an image file, a FW directory or an image directory must be provided"
   Usage
   exit 1
   
} elseif {($fw_file != "" || $fw_dir != "") && ($input_img != "" || $img_dir != "")} {
   g_puts "-E- -fw/-fw_dir and -image/-img_dir flags are mutually exclusive."
   Usage
   exit 1
}

if {$input_img != "" && $img_dir != ""} {
    g_puts "-E- -image and -img_dir flags are mutually exclusive."
    exit 1
}

if {$fw_file != "" && $fw_dir != ""} {
    g_puts "-E- -fw and -fw_dir flags are mutually exclusive."
    exit 1
}

if {$conf_dir_list != "" && $conf_dir != "" && $conf_dir != "MLXBURN-AUTO"} {
    g_puts "-E- -conf_dir and -conf_dir_list flags are mutually exclusive."
    exit 1
}

if {$fw_file != ""} {
   g_puts "-D- conf_file = ($conf_file)"
   g_puts "-D- fw_file = ($fw_file)"
   set substring "/../dist/"
   set res [string first $substring $fw_file]
   if { $res  > 0 } {
	   set slink [string range $fw_file 0 $res-1]
	   set length [string length $fw_file]
	   set tail [string range $fw_file $res+9 $length]
	   set type  [file type $slink]
	   if { "$type" == "link" } {
		   set real_name  [file readlink $slink]
		   set fw_file "$real_name/$tail"
		   g_puts "-D- Final fw_file = $fw_file"
	   } 
   } elseif { ![file_readable_fixed $fw_file] } {
       g_puts "-E- FW file specified ($fw_file) not found."
       exit 1
   }
 }
   

if {$fw_dir != "" && ![file readable $fw_dir]} {
   g_puts "-E- FW Directory specified ($fw_dir) not found."
   exit 1
}

if {$fw_dir != "" && ![file isdirectory $fw_dir]} {
   g_puts "-E- FW Directory specified ($fw_dir) is not directory."
   exit 1
}

if       {$mst_dev == "" && $bin_file == "" && !$show_fwver} {
   g_puts "-E- Either MST device or output file must be provided."
   Usage
   exit 1
} elseif {$mst_dev != "" && $bin_file != ""} {
   g_puts "-E- -dev and -wrimage flags are mutually exclusive."
   Usage
   exit 1
}

if {$bin_file != "" && $fw_file == ""} {
   g_puts "-E- A fw file (xml/mlx) must be provided when generating image file."
   Usage
   exit 1
}

if {$mst_dev == "" && $conf_file == "" && !$show_fwver} {
   g_puts "-E- A fw configuration file (ini) must be provided when generating image file."
   Usage
   exit 1
}
if {$mst_dev == "" && ($exp_rom == "AUTO" || $exp_rom_dir != "")} {
   g_puts "-E- Cannot autodetect exprom from a directory when generating image file."
   exit 1
}

if {$input_img != "" && $mst_dev == ""} {
   g_puts "-E- Both input and output image specified - nothing to do ..."
   exit 1
}

if {$input_img != "" && $exp_rom != ""} {
    g_puts "-E- Expansion ROM can not be used with a binary image file. A FW file (given \
with the -fw flag) should be provided in order to embed the ROM in the FW image."
    exit 1
}

if {$exp_rom_dir != "" && $exp_rom != "AUTO"} {
	g_puts "-E- you should also use \"-exp_rom AUTO\", if you want to look for ROM in a specified directory"
	exit 1
}

#
# Get the burnt device type
# Get the device ID
if {$dev_type == ""} {

   # Getting device ID from the fw file name
   if {$fw_file != "" && $mst_dev == ""} {
      if {[regexp {^fw-(\d\d\d\d\d)} [file tail $fw_file] d1 dev_type]} {
         # hca dev type extracted
      } elseif {[regexp -nocase {^fw-ConnectX2} [file tail $fw_file]]} {
         set dev_type  "$CX_MAIN_ID"
         set g_dev_rev 0xb0
      } elseif {[regexp -nocase {^fw-ConnectX3Pro} [file tail $fw_file]]} {
         set dev_type  "$CX_MAIN_ID"
         set g_dev_id  "$CX3_PRO_HW_ID"
      } elseif {[regexp -nocase {^fw-ConnectX3} [file tail $fw_file]]} {
         set dev_type  "$CX_MAIN_ID"
         set g_dev_id  "$CX3_HW_ID"
      }  elseif {[regexp -nocase {^fw-is4} [file tail $fw_file]]} {
         set dev_type "$IS4_HW_ID"
      } elseif {[regexp -nocase {^fw-sx} [file tail $fw_file]]} {
         set dev_type "$SX_HW_ID"
      } elseif {[regexp -nocase {^fw-SwitchIB} [file tail $fw_file]]} {
         set dev_type "$SWITCHIB_HW_ID"
      } elseif {[regexp -nocase {^fw-SwitchIB-2} [file tail $fw_file]]} {
         set dev_type "$SWITCHIB2_HW_ID"
      } elseif {[regexp -nocase {^fw-Quantum} [file tail $fw_file]]} {
         set dev_type "$QUANTUM_HW_ID"
      } elseif {[regexp -nocase {^fw-Quantum-2} [file tail $fw_file]]} {
         set dev_type "$QUANTUM2_HW_ID"
      } elseif {[regexp -nocase {^fw-SwitchEN} [file tail $fw_file]]} {
         set dev_type "$SPECTRUM_HW_ID"
      } elseif {[regexp -nocase {^fw-Spectrum} [file tail $fw_file]]} {
         set dev_type "$SPECTRUM_HW_ID"
      } elseif {[regexp -nocase {^fw-Spectrum-2} [file tail $fw_file]]} {
         set dev_type "$SPECTRUM2_HW_ID"
      } elseif {[regexp -nocase {^fw-Spectrum-3} [file tail $fw_file]]} {
         set dev_type "$SPECTRUM3_HW_ID"
      } elseif {[regexp -nocase {^fw-Spectrum-4} [file tail $fw_file]]} {
         set dev_type "$SPECTRUM4_HW_ID"
      } elseif {[regexp -nocase {^fw-LinkXGearboxRetimer.mlx} [file tail $fw_file]]} {
         set dev_type "$AMOSGB_HW_ID"
      } elseif {[regexp -nocase {^fw-LinkXAbirGearboxRetimer.mlx} [file tail $fw_file]]} {
         set dev_type "$AMOSGB_HW_ID"
      } elseif {[regexp -nocase {^fw-ConnectIB} [file tail $fw_file]]} {
         set dev_type "$CIB_HW_ID"
      } elseif {[regexp -nocase {^fw-ConnectX4Lx} [file tail $fw_file]]} {
         set dev_type "$CX4LX_HW_ID"
      } elseif {[regexp -nocase {^fw-ConnectX4} [file tail $fw_file]]} {
         set dev_type "$CX4_HW_ID"
      } elseif {[regexp -nocase {^fw-ConnectX5} [file tail $fw_file]]} {
         set dev_type "$CX5_HW_ID"
      } elseif {[regexp -nocase {^fw-ConnectX6} [file tail $fw_file]]} {
         set dev_type "$CX6_HW_ID"
      } elseif {[regexp -nocase {^fw-ConnectX6Dx} [file tail $fw_file]]} {
         set dev_type "$CX6DX_HW_ID"
      } elseif {[regexp -nocase {^fw-ConnectX6Lx} [file tail $fw_file]]} {
         set dev_type "$CX6LX_HW_ID"
      } elseif {[regexp -nocase {^fw-BlueField} [file tail $fw_file]]} {
         set dev_type "$BF_HW_ID"
      } elseif {[regexp -nocase {^fw-Menhit} [file tail $fw_file]]} {
         set dev_type "$MENHIT_HW_ID"
      } elseif {[regexp -nocase {^fw-BlueField-2} [file tail $fw_file]]} {
         set dev_type "$BF2_HW_ID"
      } elseif {[regexp -nocase {^fw-BlueField-3} [file tail $fw_file]]} {
         set dev_type "$BF3_HW_ID"
      } elseif {[regexp -nocase {^fw-ConnectX7.mlx} [file tail $fw_file]]} {
         set dev_type "$CX7_HW_ID"
      } else {
         set file_ext [file extension $fw_file]
         if { !($file_ext == ".xml") && !($file_ext == ".mlx") && !($file_ext == ".BIN") } {
            g_puts "-E- Illegal FW file extension (\"$fw_file\"), Supported extensions are: .xml, .mlx"
         } else {
            g_puts "-E- Can't extract burnt device type from fw file $fw_file. Please specify device type explicitly. (E.G. -dev_type 27508)"
         }

         exit 1
      }

   # Getting the device ID from the HW by reding address 0xf0014 on the cr-space.
   } else {
        if {[regexp "mlnxsw-" $mst_dev]} {
            g_puts  "-W- Cr-space read is not supported over 'mlnxsw' device, assuming it's an SX device..."
            set dev_type  ${SX_HW_ID}
            set g_dev_rev 0
        } else {
            if {[catch {set dev_list [get_dev_id $mst_dev]} e]} {
                if {$e == "0xbad0cafe"} {
                    g_puts "-E- Can not auto detect device type: CR-space is locked."
                } else {
                    g_puts "-E- Can not auto detect device type: $e. Please check the given device."
                }
                exit 1
            }
            set dev_type  [lindex $dev_list 0]
            set g_dev_id  $dev_type
            set g_dev_rev [lindex $dev_list 1]
        }
   }
}
g_puts "-D- dev_type: $dev_type"

# Map given dev type to the types used by MIC
if {$dev_type != ""} {

    if {[is_connectx $dev_type]} {
        set dev_type $CX_MAIN_ID
    }

    if {[is_is4 $dev_type]} {
        set dev_type $IS4_HW_ID
    }

    if {[is_sx $dev_type]} {
        set dev_type $SX_HW_ID
    }

    if {[is_switchib $dev_type]} {
        set dev_type $SWITCHIB_HW_ID
    }

    if {[is_switchib2 $dev_type]} {
        set dev_type $SWITCHIB2_HW_ID
    }

    if {[is_quantum $dev_type]} {
        set dev_type $QUANTUM_HW_ID
    }

    if {[is_quantum2 $dev_type]} {
        set dev_type $QUANTUM2_HW_ID
    }

    if {[is_spectrum $dev_type]} {
        set dev_type $SPECTRUM_HW_ID
    }

    if {[is_spectrum2 $dev_type]} {
        set dev_type $SPECTRUM2_HW_ID
    }

    if {[is_spectrum3 $dev_type]} {
        set dev_type $SPECTRUM3_HW_ID
    }

    if {[is_spectrum4 $dev_type]} {
        set dev_type $SPECTRUM4_HW_ID
    }

    if {[is_amg $dev_type]} {
        set dev_type $AMOSGB_HW_ID
    }

    if {[is_connectib $dev_type]} {
        set dev_type $CIB_HW_ID
    }

    if {[is_connectx4 $dev_type]} {
        set dev_type $CX4_HW_ID
    }

    if {[is_connectx4lx $dev_type]} {
        set dev_type $CX4LX_HW_ID
    }
    
    if {[is_connectx5 $dev_type]} {
        set dev_type $CX5_HW_ID
    }
    
    if {[is_connectx6 $dev_type]} {
        set dev_type $CX6_HW_ID
    }

    if {[is_connectx6dx $dev_type]} {
        set dev_type $CX6DX_HW_ID
    }
    
    if {[is_connectx6lx $dev_type]} {
        set dev_type $CX6LX_HW_ID
    }

    if {[is_connectx7 $dev_type]} {
        set dev_type $CX7_HW_ID
    }

    if {[is_bluefield $dev_type]} {
        set dev_type $BF_HW_ID
    }
    if {[is_bluefield2 $dev_type]} {
        set dev_type $BF2_HW_ID
    }
    if {[is_bluefield3 $dev_type]} {
        set dev_type $BF3_HW_ID
    }

}

if {![is_flash $dev_type] && !$show_fwver} {
   g_puts "-E- Device type $dev_type is not a Mellanox device."
   exit 1
}

# set the correct mic vsd parameter if vsd flag was specified

if {([info exists vsd_str]) && ($vsd_str != "")} {
    if {[is_fs3_dev $dev_type]} {
        append additional_mic_flags " -image_info.vsd \"$vsd_str\""
    } else {
        append additional_mic_flags " -ADAPTER.adapter_vsd \"$vsd_str\""
    }
}

if {[is_flash $dev_type]} {
   set burner "flint"
   if {$use_mstflint} {
       set burner "mstflint"
   }
} else {
   set burner "spark"
}

#
# platform specific:

#
if { $tcl_platform(platform) == "windows" } {
   if {[info exist env(TEMP)]} {
      set tmp_dir [fix_dir_path $env(TEMP)]
   } else {
      set tmp_dir "c:/temp"
   }
   set t2a "t2a"

   # Normalize paths:
   set fw_file    [fix_file_path $fw_file   ]
   set bin_file   [fix_file_path $bin_file  ]
   set conf_file  [fix_file_path $conf_file ]
   set input_img  [fix_file_path $input_img ]

   set img_dir    [fix_dir_path $img_dir ]

   if { $exp_rom != "AUTO" } {
       set exp_rom    [fix_file_path $exp_rom   ]
   }
   set user_data  [fix_file_path $user_data ]
   if { $conf_dir != "MLXBURN-AUTO" } {
       set conf_dir   [fix_dir_path $conf_dir  ]
   }
   set fw_dir     [fix_dir_path $fw_dir    ]
   set exp_rom_dir    [fix_dir_path $exp_rom_dir   ]


} else {
   set tmp_dir "/tmp"
   set t2a "t2a"

    if { $conf_dir != "MLXBURN-AUTO" && $conf_dir != ""} {
	set conf_dir [fix_dir_path $conf_dir]
    }
    if { $fw_dir != "" } {
	set fw_dir [fix_dir_path $fw_dir]
    }
    
    if { $exp_rom_dir != "" } {
	set exp_rom_dir    [fix_dir_path $exp_rom_dir   ]
    }
}

g_puts "-D- tmp_dir = $tmp_dir"

if { $conf_dir_list != "" } {
    set new_conf_dir ""
    foreach dir $conf_dir_list {
	catch {set new_conf_dir [concat $new_conf_dir [fix_list_dir_path $dir] ]}
    }
    set conf_dir_list $new_conf_dir
}

if {!$sw_sys_mode} {
    if {$show_fwver} {
        if {$fw_file != ""} {
           set fwver [generate_image 1]
        } else {
           if {[catch {set fwver [get_fw_ver $mst_dev]} e]} {
               g_puts "-E- Failed to get FW version: $e"
               cexit 1
           }
        }
        g_puts "-I- FW Version: $fwver"
        cexit 0
    }

    if {$query_mode} {
        cexit [query_image_hca]
    }

    if {$img_dir != ""} {
        set bin_file [select_binary_file $mst_dev $img_dir]
        g_puts "-I- Using auto detected image file : $bin_file"
    } elseif {$input_img == ""} {
        set bin_file [generate_image]
    } else {
        set bin_file $input_img
    }
    
    if {$gen_tmp_img != ""} {
        g_puts "-I- Copy the generated image into $gen_tmp_img"
        if {[catch {file copy -force $bin_file $gen_tmp_img} cpyError]} {
            g_puts "-E- Failed to copy generated image: $cpyError"
            cexit 1
        }
        cexit 0
    }
    	
    if {$mst_dev == ""} {
       # Generate only - no burn
       g_puts "-I- Image generation completed successfully."
       cexit 0
    } else {
       burn_image $pattern
    }
} else {
    set status {}; set output {}

    #check if the system name provided exists in isw db.
    if {![g_exec "isw -d $mst_dev -s $sw_sys_name -n" status output]} {
       g_puts "-E- Failed routing switch system I2C bus: [lindex $status end]."
       cexit 1
    }
    set swSystemDesc [lindex $output 0]
    g_puts "-I- $swSystemDesc"

    set sharkMode   [regexp {MTS3600} $swSystemDesc]

    if {!$got_range} {
        if {$sharkMode} {
            set r_from 0x48
            set r_to   0x48
        }
    }

    #check if the pattern is legal.
    if {![g_exec "isw -d $mst_dev -s $sw_sys_name -p $pattern -l 0x6c" status output]} {
       g_puts "-E- Failed routing switch system I2C bus: [lindex $status end]."
       cexit 1
    }
    #check if the range is legal.
    if {![g_exec "isw -d $mst_dev -s $sw_sys_name -p $pattern -l $r_from $r_to" status output]} {
       g_puts "-E- Failed routing switch system I2C bus: [lindex $status end]."
       cexit 1
    }
    set target_boards [lsort -dictionary $output]
    if {$list_only_mode} {
        if {$sharkMode} {
            set devI2cAddr "48"
        } else {
            set devI2cAddr "6c"
        }
        if {[format %x $r_from] == $devI2cAddr && [format %x $r_to] == $devI2cAddr} {
            g_puts "-I- List of switch devices detected:"
            foreach slave $target_boards {
                g_puts "-I- [lindex $slave 0]"
            }
        } else {
            g_puts "-I- List only mode - I2C slaves detected:"
            foreach slave $target_boards {
                g_puts "-I- $slave"
            }
        }
        cexit 0
    }

    g_puts "-D- Pattern matches [llength $target_boards] devices."
    set header 1
    foreach slave $target_boards {
        set pat [lindex $slave 0]
        #g_puts "-I- $pat"
        if {![g_exec "isw -d $mst_dev -s $sw_sys_name -p $pat" status output]} {
           g_puts "-E- Failure reaching Switch device at $pat : [lindex $status end]"
           cexit 1
        }

        if {$show_fwver} {
            if {[catch {set fwver [get_fw_ver $mst_dev]} e]} {
                g_puts [format "-E- %-6s  Failed to get FW version: $e" $pat]
            } else {
                g_puts [format "-I- %-6s  $fwver" $pat]
            }
        } elseif {$query_mode} {
            query_image_switch $pat $header
            if {$header} {
                set header 0
            }
        } else {

            puts ""
            if {$pat != ""} {
               g_puts "-I- Burning switch device at $pat ..."
            }
            if {$input_img == ""} {
                set bin_file [generate_image]
                #set conf_file ""
            } else {
                set bin_file $input_img
            }
            burn_image $pat
        }
    }
}

cexit 0