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