#!/usr/bin/bash

# Copyright (c) 2020, Mellanox Technologies
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this
#    list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright notice,
#    this list of conditions and the following disclaimer in the documentation
#    and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# The views and conclusions contained in the software and documentation are those
# of the authors and should not be interpreted as representing official policies,
# either expressed or implied, of the FreeBSD Project.

# shellcheck disable=SC2059
true

${BFDBG}

# Temporary directory.
TMP_DIR=$(mktemp -d)
trap "rm -rf $TMP_DIR" EXIT
trap "rm -rf $TMP_DIR; exit 1" TERM INT

bfcfg_version=4.6
bfcfg_rc=0
cfg_file=/etc/bf.cfg
log_file=/tmp/bfcfg.log

# Config dump levels (the higher the number the more information is dumped).
# dump_level defaults to an empty string and is set based on bfcfg input arguments.
DUMP_LEVEL_NONE=0
DUMP_LEVEL_DEFAULT=1
DUMP_LEVEL_EXTRA=2
DUMP_LEVEL_MAX=${DUMP_LEVEL_EXTRA}
dump_level=

mlxmkcap=/lib/firmware/mellanox/boot/capsule/scripts/mlx-mkcap

bfcf_acpi_table=/sys/firmware/acpi/tables/BFCF
efivars=/sys/firmware/efi/efivars
efi_global_var_guid=8be4df61-93ca-11d2-aa0d-00e098032b8c
efi_bfcfg_var_guid=487ff588-fb71-4b19-9100-ebe067aa1af0
efi_vlan_var_guid=9e23d768-d2f3-4366-9fc3-3a7aba864374
efi_redfish_var_guid=ce3e5882-6770-4d5e-aa45-90f909da2446
efi_debug_var_guid=f3ce977e-c17f-493e-a3cd-5731d386e505
sys_cfg_sysfs=${efivars}/BfSysCfg-9c759c02-e5a3-45e4-acfc-c34a500628a6
redfish_cfg_sysfs=${efivars}/BfRedfish-${efi_redfish_var_guid}
redfish_state_cfg_sysfs=${efivars}/BfRedfishState-${efi_redfish_var_guid}
bmc_upgrade_reset_cfg_sysfs=${efivars}/BmcUpgradeReset-${efi_redfish_var_guid}
sb_state_sysfs=${efivars}/SecureBoot-${efi_global_var_guid}
sb_setup_mode_sysfs=${efivars}/SetupMode-${efi_global_var_guid}
rshim_efi_mac_sysfs=${efivars}/RshimMacAddr-${efi_global_var_guid}
pxe_dhcp_class_id_sysfs=${efivars}/DhcpClassId-${efi_global_var_guid}
bf_bundle_version_sysfs=${efivars}/BfBundleVer-${efi_bfcfg_var_guid}
sb_custom_mode_sysfs=${efivars}/BfCfgSbCustomMode-${efi_bfcfg_var_guid}
pass_settings_sysfs=${efivars}/BfCfgPassSettings-${efi_bfcfg_var_guid}
bf_modes_sysfs=${efivars}/BfCfgBfModes-${efi_bfcfg_var_guid}
boot_wdog_sysfs=${efivars}/BfBootWdog-120b4e59-904a-4fe2-9e03-54e9a29ffb22
osinfo_sysfs=${efivars}/BfOsInfo-${efi_debug_var_guid}
doca_info_sysfs=${efivars}/BfDocaInfo-${efi_debug_var_guid}
cap_atf_version_sysfs=${efivars}/BfCapAtfVer-${efi_debug_var_guid}
cap_uefi_version_sysfs=${efivars}/BfCapUefiVer-${efi_debug_var_guid}
bf_profile_root_key=${efivars}/BfProfileRootKey-f4d4a988-e8c5-11ef-8afc-0242c0a80103
bf_profile_ca_cert_nvidia=${efivars}/BfProfileCaCertNvidia-f4d4a988-e8c5-11ef-8afc-0242c0a80103
bf_profile_ca_cert_oem=${efivars}/BfProfileCaCertOem-f4d4a988-e8c5-11ef-8afc-0242c0a80103
bf_profile_config_sub_ca_cert_nvidia=${efivars}/BfProfileConfigSubCaCertNvidia-f4d4a988-e8c5-11ef-8afc-0242c0a80103
bf_profile_config_sub_ca_cert_oem=${efivars}/BfProfileConfigSubCaCertOem-f4d4a988-e8c5-11ef-8afc-0242c0a80103

mfg_sysfs_dir=/sys/bus/platform/devices/MLNXBF04:00/driver
if [ ! -e $mfg_sysfs_dir/oob_mac ]; then
  mfg_sysfs_dir=/sys/bus/platform/devices/MLNXBF04:00
fi
oob_mac_sysfs=${mfg_sysfs_dir}/oob_mac
large_icm_sysfs=${mfg_sysfs_dir}/large_icm

ECHO=${ECHO:-echo}

log_msg()
{
  echo "$@" >> ${log_file}
}

mfg_lock()
{
  log_msg "mfg: lock the partition"
  echo 1 > ${mfg_sysfs_dir}/mfg_lock 2>>${log_file}
}

delete_efi_var()
{
  local in_efivarfs=$1

  [ ${dump_level} -gt ${DUMP_LEVEL_NONE} ] && return

  if [ -e "${in_efivarfs}" ]; then
    log_msg "mfg: delete ${in_efivarfs}"
    chattr -i ${in_efivarfs}
    rm -f ${in_efivarfs}
  fi
}

mfg_reset_deps()
{
  # Cleanup configuration dependencies if needed
  if [ ${has_sys_cfg} -eq 0 ]; then
    # Delete SYS Config data
    delete_efi_var "${sys_cfg_sysfs}"
  fi
  if [ ${has_redfish_cfg} -eq 0 ]; then
    # Delete RDF Config
    delete_efi_var "${redfish_cfg_sysfs}"
  fi
}

mfg_sysfs_cfg()
{
  local tmp_file=${TMP_DIR}/.bfcfg-mfg-data
  local opn_sysfs=${mfg_sysfs_dir}/opn
  local sku_sysfs=${mfg_sysfs_dir}/sku
  local modl_sysfs=${mfg_sysfs_dir}/modl
  local sn_sysfs=${mfg_sysfs_dir}/sn
  local uuid_sysfs=${mfg_sysfs_dir}/uuid
  local rev_sysfs=${mfg_sysfs_dir}/rev
  local oob_efi_mac_sysfs=${efivars}/OobMacAddr-${efi_global_var_guid}
  local mac_err opn_err mac

  [ ${dump_level} -gt ${DUMP_LEVEL_NONE} ] && return

  if [ -z "${MFG_OOB_MAC}" ] || [ -z "${MFG_OPN}" ] || [ -z "${MFG_SKU}" ] ||
     [ -z "${MFG_MODL}" ] || [ -z "${MFG_SN}" ] || [ -z "${MFG_UUID}" ] ||
     [ -z "${MFG_REV}" ] ; then
    log_msg "mfg: skip mfg since one or more of the following are unspecified:"
    log_msg "MFG_OOB_MAC, MFG_OPN, MFG_SKU, MFG_MODL, MFG_SN, MFG_UUID, MFG_REV"
    return
  fi

  if [ -n "${MFG_OOB_MAC}" ] && [ -e "${oob_mac_sysfs}" ]; then
    log_msg "mfg: OOB_MAC=${MFG_OOB_MAC}"
    echo "${MFG_OOB_MAC}" > ${oob_mac_sysfs} 2>>${log_file}
    mac_err=$?

    if [ ${mac_err} -eq 0 ]; then
      log_msg "mfg: oob_mac written"

      # Update the MAC address in UEFI variable.
      # Somehow 'printf' into the sysfs file doesn't always work, probably
      # due to length check of each write. A temporary file is used here
      # to workaround such issue.
      mac="${MFG_OOB_MAC//:/\\x}"
      mac="\\x07\\x00\\x00\\x00\\x${mac}"
      printf "${mac}" > ${tmp_file}
      chattr -i ${oob_efi_mac_sysfs} 2>/dev/null
      cp ${tmp_file} ${oob_efi_mac_sysfs}
      rm -f ${tmp_file}
    else
      log_msg "mfg: mac_err=${mac_err}"
      return
    fi
  fi

  if [ -n "${MFG_OPN}" ] && [ -e "${opn_sysfs}" ]; then
    log_msg "mfg: OPN=${MFG_OPN}"
    echo "${MFG_OPN}" > ${opn_sysfs} 2>>${log_file}
    opn_err=$?

    if [ ${opn_err} -eq 0 ]; then
      log_msg "mfg: opn written"
    else
      log_msg "mfg: opn_err=${opn_err}"
      return
    fi
  fi

  if [ -n "${MFG_SKU}" ] && [ -e "${sku_sysfs}" ]; then
    log_msg "mfg: SKU=${MFG_SKU}"
    echo "${MFG_SKU}" > ${sku_sysfs} 2>>${log_file}
    sku_err=$?

    if [ ${sku_err} -eq 0 ]; then
      log_msg "mfg: sku written"
    else
      log_msg "mfg: sku_err=${sku_err}"
      return
    fi
  fi

  if [ -n "${MFG_MODL}" ] && [ -e "${modl_sysfs}" ]; then
    log_msg "mfg: MODL=${MFG_MODL}"
    echo "${MFG_MODL}" > ${modl_sysfs} 2>>${log_file}
    modl_err=$?

    if [ ${modl_err} -eq 0 ]; then
      log_msg "mfg: modl written"
    else
      log_msg "mfg: modl_err=${modl_err}"
      return
    fi
  fi

  if [ -n "${MFG_SN}" ] && [ -e "${sn_sysfs}" ]; then
    log_msg "mfg: SN=${MFG_SN}"
    echo "${MFG_SN}" > ${sn_sysfs} 2>>${log_file}
    sn_err=$?

    if [ ${sn_err} -eq 0 ]; then
      log_msg "mfg: sn written"
    else
      log_msg "mfg: sn_err=${sn_err}"
      return
    fi
  fi

  if [ -n "${MFG_UUID}" ] && [ -e "${uuid_sysfs}" ]; then
    log_msg "mfg: UUID=${MFG_UUID}"
    echo "${MFG_UUID}" > ${uuid_sysfs} 2>>${log_file}
    uuid_err=$?

    if [ ${uuid_err} -eq 0 ]; then
      log_msg "mfg: uuid written"
    else
      log_msg "mfg: uuid_err=${uuid_err}"
      return
    fi
  fi

  if [ -n "${MFG_REV}" ] && [ -e "${rev_sysfs}" ]; then
    log_msg "mfg: REV=${MFG_REV}"
    echo "${MFG_REV}" > ${rev_sysfs} 2>>${log_file}
    rev_err=$?

    if [ ${rev_err} -eq 0 ]; then
      log_msg "mfg: rev written"
    else
      log_msg "mfg: rev_err=${rev_err}"
      return
    fi
  fi

  mfg_lock
  mfg_sysfs_cfg_done=1
  mfg_reset_deps
}

mfg_cfg_set_bytes()
{
  local out_file=$1
  local name=$2
  local offset=$3
  local size=$4
  local value=$5

  [ ${dump_level} -gt ${DUMP_LEVEL_NONE} ] && return

  if [ -n "${value}" ] && [ -f "${out_file}" ]; then
    # Most of the fields are of type strings, thus check the number
    # of characters per string. Only exception for the OOB MAC.
    # The OOB MAC is stored in hex format; e.g., \xAA\xBB\xCC\xDD\xEE\xFF,
    # total of 24 charcters
    if [ ${#value} -gt ${size} ] && [ "${name}" != "MFG_OOB_MAC" ]; then
        log_msg "mfg: ${name} exceeding max length ${size}"
        return
    elif [ ${#value} -gt 24 ] && [ "${name}" == "MFG_OOB_MAC" ]; then
        log_msg "mfg: MFG_OOB_MAC exceeding max length 6 bytes"
        return
    else
      printf "${value}" | dd of="${out_file}" seek="${offset}" bs=1 count=${size} conv=notrunc 2> /dev/null
      err=$?
      if [ ${err} -eq 0 ]; then
        log_msg "mfg: ${name} written, value=${value}"
      else
        log_msg "mfg: modl_err=${err}"
        return
      fi
      has_cfg=1
    fi
  fi
}

copy_efi_var_data()
{
  local out_file=$1
  local in_efivarfs=$2
  local offset=$3
  local size=$4

  [ ${dump_level} -gt ${DUMP_LEVEL_NONE} ] && return

  if [ -e "${in_efivarfs}" ] && [ -f "${out_file}" ]; then
    chattr -i ${in_efivarfs}
    # Copy the EFI variable data, skip variable header (4 bytes)
    dd if="${in_efivarfs}" of="${out_file}" seek="${offset}" skip=4 bs=1 count=${size} conv=notrunc 2> /dev/null
    err=$?
    if [ ${err} -eq 0 ]; then
      log_msg "mfg: ${in_efivarfs} data written to ${out_file}"
    else
      log_msg "mfg: failed to write ${in_efivarfs} data to ${out_file}"
      log_msg "mfg: modl_err=${err}"
      return
    fi
  fi
}

mfg_cfg_set_bf_modes()
{
  local out_file=$1
  local name=$2
  local offset=$3
  local size=$4
  local value=$5
  local bin_value

  [ ${dump_level} -gt ${DUMP_LEVEL_NONE} ] && return

  if [ -z "${value}" ] || [ ! -f "${out_file}" ]; then
    return
  fi

  #
  # This must be consistent with the UEFI PMI library
  # implementation.
  #
  if [ "${name}" == "MFG_PLAT_MODE" ]; then
    if [ "${value^^}" == "DPU" ]; then
      bin_value='\x01'
    elif [ "${value^^}" == "NIC" ]; then
      bin_value='\x02'
    else
      log_msg "mfg: ${name}, unsupported value ${value}"
      return
    fi
    printf "${bin_value}" | dd of="${out_file}" seek="${offset}" bs=1 count=${size} conv=notrunc 2> /dev/null
    err=$?
    if [ ${err} -eq 0 ]; then
      log_msg "mfg: ${name}=${value}"
    else
      log_msg "mfg: modl_err=${err}"
      return
    fi
    has_cfg=1
  else
    log_msg "mfg: ${name} is not supported"
    return
  fi
}

mfg_cfg_to_bin()
{
  local bin_file=$1

  [ -f "${bin_file}" ] && rm -f ${bin_file}
  # Create empty MFG blob; it contains MFG data and MFG extended data
  # and occupies 16 pages of 256 bytes within the EEPROM
  #
  # Layout:
  #   MFG Data      (off:   0, len: 256 bytes)
  #   MFG Extension (off: 256, len:2048 bytes)
  #   SYS Config    (off:2304, len:  64 bytes)
  #   RDF Config    (off:2368, len:  32 bytes)
  #   BF Modes      (off:2400, len:   4 bytes)
  #   Reserved      (off:2404, len:1692 bytes)
  #
  dd if=/dev/zero of=${bin_file} count=16 bs=256 >&/dev/null
  has_cfg=0

  [ ! -f "${bin_file}" ] && return

  if [ -z "${MFG_OOB_MAC}" ] || [ -z "${MFG_OPN}" ] || [ -z "${MFG_SKU}" ] ||
     [ -z "${MFG_MODL}" ] || [ -z "${MFG_SN}" ] || [ -z "${MFG_UUID}" ] ||
     [ -z "${MFG_REV}" ] ; then
    log_msg "mfg: bin: skip mfg since one or more of the following are unspecified:"
    log_msg "MFG_OOB_MAC, MFG_OPN, MFG_SKU, MFG_MODL, MFG_SN, MFG_UUID, MFG_REV"

  elif [ ${mfg_sysfs_cfg_done} -eq 0 ]; then
    #
    # MFG Layout (1x 256B)
    #
    # OOB_MAC (off:  0, len: 8 bytes)
    # OPN     (off:  8, len:24 bytes)
    # SKU     (off: 32, len:24 bytes)
    # MODL    (off: 56, len:24 bytes)
    # SN      (off: 80, len:24 bytes)
    # UUID    (off:104, len:40 bytes)
    # REV     (off:144, len: 8 bytes)

    mfg_cfg_set_bytes ${bin_file} "MFG_OOB_MAC" 0 8 "\\x${MFG_OOB_MAC//:/\\x}"
    mfg_cfg_set_bytes ${bin_file} "MFG_OPN" 8 24 "${MFG_OPN}"
    mfg_cfg_set_bytes ${bin_file} "MFG_SKU" 32 24 "${MFG_SKU}"
    mfg_cfg_set_bytes ${bin_file} "MFG_MODL" 56 24 "${MFG_MODL}"
    mfg_cfg_set_bytes ${bin_file} "MFG_SN" 80 24 "${MFG_SN}"
    mfg_cfg_set_bytes ${bin_file} "MFG_UUID" 104 80 "${MFG_UUID}"
    mfg_cfg_set_bytes ${bin_file} "MFG_REV" 144 8 "${MFG_REV}"

    # Append MFG configuration dependenices, if needed.
    if [ ${has_cfg} -eq 1 ]; then
      if [ ${has_sys_cfg} -eq 1 ]; then
        # Copy SYS Config data
        copy_efi_var_data ${bin_file} "${sys_cfg_sysfs}" 2304 64
      fi
      if [ ${has_redfish_cfg} -eq 1 ]; then
        # Copy RDF Config
        copy_efi_var_data ${bin_file} "${redfish_cfg_sysfs}" 2368 32
      fi
    fi

  elif [ ${mfg_sysfs_cfg_done} -eq 1 ]; then
    log_msg "mfg: bin: skip mfg since configured via sysfs"
  fi

  if [ -z "${MFG_SYS_MFR}" ] && [ -z "${MFG_SYS_PDN}" ] && [ -z "${MFG_SYS_VER}" ] &&
     [ -z "${MFG_BSB_MFR}" ] && [ -z "${MFG_BSB_PDN}" ] && [ -z "${MFG_BSB_VER}" ] &&
     [ -z "${MFG_BSB_SN}" ] ; then
    log_msg "mfg: bin: skip mfg extension since none of the following are specified:"
    log_msg "MFG_SYS_MFR, MFG_SYS_PDN, MFG_SYS_VER, MFG_BSB_MFR, MFG_BSB_PDN, MFG_BSB_VER, MFG_BSB_SN"
  else
    #
    # MFG Extension Layout (8x 256B)
    #
    # SYS_MFR (off:  0 + 256, len:24 bytes)
    # SYS_PDN (off: 24 + 256, len:24 bytes)
    # SYS_VER (off: 48 + 256, len:512 bytes)
    # BSB_MFR (off:560 + 256, len:24 bytes)
    # BSB_PDN (off:584 + 256, len:24 bytes)
    # BSB_VER (off:608 + 256, len:24 bytes)
    # BSB_SN  (off:632 + 256, len:24 bytes)

    mfg_cfg_set_bytes ${bin_file} "MFG_SYS_MFR" 256 24 "${MFG_SYS_MFR}"
    mfg_cfg_set_bytes ${bin_file} "MFG_SYS_PDN" 280 24 "${MFG_SYS_PDN}"
    mfg_cfg_set_bytes ${bin_file} "MFG_SYS_VER" 304 512 "${MFG_SYS_VER}"
    mfg_cfg_set_bytes ${bin_file} "MFG_BSB_MFR" 816 24 "${MFG_BSB_MFR}"
    mfg_cfg_set_bytes ${bin_file} "MFG_BSB_PDN" 840 24 "${MFG_BSB_PDN}"
    mfg_cfg_set_bytes ${bin_file} "MFG_BSB_VER" 864 24 "${MFG_BSB_VER}"
    mfg_cfg_set_bytes ${bin_file} "MFG_BSB_SN" 888 24 "${MFG_BSB_SN}"
  fi

  if [ -n "${MFG_PLAT_MODE}" ]; then
    #
    # BF Modes (4 Bytes)
    #
    # SYS_IN_CPU_MODEL   (off: 2400, len:1 byte)
    # MFG_HOST_PRIV_MODE (off: 2401, len:1 byte)
    # MFG_PLAT_MODE      (off: 2402, len:1 byte)
    #
    # For now, support platform mode only. If needed enhance it
    # to support 'Internal CPU Model' and 'Host Privilege Level'.
    mfg_cfg_set_bf_modes ${bin_file} "MFG_PLAT_MODE" 2402 1 "${MFG_PLAT_MODE}"
  fi

  if [ ${has_cfg} -eq 0 ]; then
    log_msg "mfg: bin: configuration unspecified or incomplete. Skip binary generation."
    rm -f ${bin_file}
    return
  else
    log_msg "mfg: bin: configuration written to ${bin_file}"
  fi
}

mfg_cfg_bin_to_cap()
{
  local bin_file=$1
  local cap_file=$2

  mfg_cfg_to_bin ${bin_file}
  has_bin=0

  [ -f "${bin_file}" ] && has_bin=1

  if [ ${has_bin} -eq 0 ]; then
    log_msg "mfg: capsule: configuration blob not found. Skip capsule generation"
    return
  fi

  # Attempt creating the capsule with "mlx-mkcap" if python available
  if command -v python >/dev/null ; then
    ${mlxmkcap} --cfg-data ${bin_file} ${cap_file}
    if [ $? -eq 0 ] && [ -f "${cap_file}" ]; then
      log_msg "mfg: capsule: file created at ${cap_file}"
      return
    fi
  fi

  # BFB might be missing commands and modules needed to generate
  # the EFI capsule, thus push the bin file content to the payload
  # of the pregenerated capsule file.
  log_msg "mfg: Using template for capsule generation"
  dummy_cap=/lib/firmware/mellanox/boot/capsule/.efi_bfcfg.cap
  bin_size=$(stat -c%s ${bin_file})
  if [ ! -f "${dummy_cap}" ]; then
    log_msg "mfg: capsule: missing artifacts. Skip capsule generation"
    return
  fi
  cp -f ${dummy_cap} ${cap_file}
  # Skip the EFI capsule header; the header remains unchanged.
  # Re-write the payload starting from offset A8 (168).
  dd if="${bin_file}" of="${cap_file}" seek=168 bs=1 count=${bin_size} conv=notrunc 2> /dev/null
  if [ $? -eq 0 ]; then
    log_msg "mfg: capsule: payload updated"
    log_msg "mfg: capsule: file created at ${cap_file}"
  else
    log_msg "mfg: capsule: failed to update capsule payload"
    rm -f ${cap_file}
    return
  fi
}

mfg_cap_cfg()
{
  local bin_file=${TMP_DIR}/.bfcfg-mfg.bin
  local cap_file=${TMP_DIR}/.bfcfg-mfg.cap

  [ ${dump_level} -gt ${DUMP_LEVEL_NONE} ] && return

  mfg_cfg_bin_to_cap ${bin_file} ${cap_file}
  has_cap=0

  [ -f "${cap_file}" ] && has_cap=1

  if [ ${has_cap} -eq 0 ]; then
    log_msg "mfg: update: capsule file not found. Skip capsule update."
    return
  fi

  #
  # MFG capsule must be applied first, before secure boot capsule
  # otherwise the signature verification would fail.
  #
  # Note that capsules are sorted by name and processed in order.
  # Thus it is important to get the MFG capsule executed first and
  # the capsule file name starts with '.'; it is not expected that
  # the remaining capsule files generated outside of bfcfg command
  # would have the '.' prefix in the filename.
  #

  bfrec --capsule ${cap_file} 2>&1 > /dev/null
  if [ $? -eq 0 ]; then
    log_msg "mfg: update: MFG will be updated via capsule"
  else
    log_msg "mfg: update: failed to update MFG via capsule"
  fi

  rm -f ${bin_file} ${cap_file}
}

mfg_cfg()
{
  # Update the MFG fields with corresponding EFI variables
  local oob_mac=${efivars}/BfCfgOobMac-${efi_bfcfg_var_guid}
  local opn_sysfs=${efivars}/BfCfgOpn-${efi_bfcfg_var_guid}
  local sku_sysfs=${efivars}/BfCfgSku-${efi_bfcfg_var_guid}
  local modl_sysfs=${efivars}/BfCfgModl-${efi_bfcfg_var_guid}
  local sn_sysfs=${efivars}/BfCfgSn-${efi_bfcfg_var_guid}
  local uuid_sysfs=${efivars}/BfCfgUuid-${efi_bfcfg_var_guid}
  local rev_sysfs=${efivars}/BfCfgRev-${efi_bfcfg_var_guid}

  # The efi variables have a 4 byte attribute header we won't display.
  # If we fetch the data from the MFG, that header does not exist.
  local skip_bytes_mfg=4
  local skip_bytes_ext_mfg=4

  if [ -z "${oob_mac}" ] || [ -z "${opn_sysfs}" ] || [ -z "${sku_sysfs}" ] ||
     [ -z "${modl_sysfs}" ] || [ -z "${sn_sysfs}" ] || [ -z "${uuid_sysfs}" ] ||
     [ -z "${rev_sysfs}" ] ; then
    #
    # One or more EFI variables representing MFG fields are empty
    # Use the sysfs version of MFG fields
    #
    oob_mac=${mfg_sysfs_dir}/oob_mac
    opn_sysfs=${mfg_sysfs_dir}/opn
    sku_sysfs=${mfg_sysfs_dir}/sku
    modl_sysfs=${mfg_sysfs_dir}/modl
    sn_sysfs=${mfg_sysfs_dir}/sn
    uuid_sysfs=${mfg_sysfs_dir}/uuid
    rev_sysfs=${mfg_sysfs_dir}/rev
    skip_bytes_mfg=0
  fi

  # Use the EFI variable for all extended MFG fields
  local sys_mfr_sysfs=${efivars}/BfCfgSysMfr-${efi_bfcfg_var_guid}
  local sys_pdn_sysfs=${efivars}/BfCfgSysPdn-${efi_bfcfg_var_guid}
  local sys_ver_sysfs=${efivars}/BfCfgSysVer-${efi_bfcfg_var_guid}
  local bsb_mfr_sysfs=${efivars}/BfCfgBsbMfr-${efi_bfcfg_var_guid}
  local bsb_pdn_sysfs=${efivars}/BfCfgBsbPdn-${efi_bfcfg_var_guid}
  local bsb_ver_sysfs=${efivars}/BfCfgBsbVer-${efi_bfcfg_var_guid}
  local bsb_sn_sysfs=${efivars}/BfCfgBsbSn-${efi_bfcfg_var_guid}



  if [ ${dump_level} -gt ${DUMP_LEVEL_NONE} ]; then
    # W/A: always read the OOB MAC from kernel sysfs.
    # Note: The 'BfCfgOobMac' EFI variable might contain incorrect
    #       OOB MAC.
    [ -e "${oob_mac_sysfs}" ] && echo "mfg: MFG_OOB_MAC=$(cat ${oob_mac_sysfs} 2>/dev/null)"
    [ -e "${opn_sysfs}" ] && echo "mfg: MFG_OPN=$(cat ${opn_sysfs} 2>/dev/null | sed "s/^.\{$skip_bytes_mfg\}//")"
    [ -e "${sku_sysfs}" ] && echo "mfg: MFG_SKU=$(cat ${sku_sysfs} 2>/dev/null | sed "s/^.\{$skip_bytes_mfg\}//")"
    [ -e "${modl_sysfs}" ] && echo "mfg: MFG_MODL=$(cat ${modl_sysfs} 2>/dev/null  | sed "s/^.\{$skip_bytes_mfg\}//")"
    [ -e "${sn_sysfs}" ] && echo "mfg: MFG_SN=$(cat ${sn_sysfs} 2>/dev/null | sed "s/^.\{$skip_bytes_mfg\}//")"
    [ -e "${uuid_sysfs}" ] && echo "mfg: MFG_UUID=$(cat ${uuid_sysfs} 2>/dev/null | sed "s/^.\{$skip_bytes_mfg\}//")"
    [ -e "${rev_sysfs}" ] && echo "mfg: MFG_REV=$(cat ${rev_sysfs} 2>/dev/null | sed "s/^.\{$skip_bytes_mfg\}//")"
    [ -e "${sys_mfr_sysfs}" ] && echo "mfg: MFG_SYS_MFR=$(cat ${sys_mfr_sysfs} 2>/dev/null | sed "s/^.\{$skip_bytes_ext_mfg\}//")"
    [ -e "${sys_pdn_sysfs}" ] && echo "mfg: MFG_SYS_PDN=$(cat ${sys_pdn_sysfs} 2>/dev/null | sed "s/^.\{$skip_bytes_ext_mfg\}//")"
    [ -e "${sys_ver_sysfs}" ] && echo "mfg: MFG_SYS_VER=$(cat ${sys_ver_sysfs} 2>/dev/null | sed "s/^.\{$skip_bytes_ext_mfg\}//")"
    [ -e "${bsb_mfr_sysfs}" ] && echo "mfg: MFG_BSB_MFR=$(cat ${bsb_mfr_sysfs} 2>/dev/null | sed "s/^.\{$skip_bytes_ext_mfg\}//")"
    [ -e "${bsb_pdn_sysfs}" ] && echo "mfg: MFG_BSB_PDN=$(cat ${bsb_pdn_sysfs} 2>/dev/null | sed "s/^.\{$skip_bytes_ext_mfg\}//")"
    [ -e "${bsb_ver_sysfs}" ] && echo "mfg: MFG_BSB_VER=$(cat ${bsb_ver_sysfs} 2>/dev/null | sed "s/^.\{$skip_bytes_ext_mfg\}//")"
    [ -e "${bsb_sn_sysfs}" ] && echo "mfg: MFG_BSB_SN=$(cat ${bsb_sn_sysfs} 2>/dev/null | sed "s/^.\{$skip_bytes_ext_mfg\}//")"
    return
  fi

  mfg_sysfs_cfg_done=0
  mfg_sysfs_cfg
  mfg_cap_cfg
}

icm_cfg()
{
  if [ ${dump_level} -gt ${DUMP_LEVEL_NONE} ]; then
    [ -e "${large_icm_sysfs}" ] && echo "icm: LARGE_ICM_SIZE=$(cat ${large_icm_sysfs} 2>/dev/null)"
    return
  fi

  if [ -n "${LARGE_ICM_SIZE}" ] && [ -e "${large_icm_sysfs}" ]; then
    log_msg "icm: LARGE_ICM_SIZE=${LARGE_ICM_SIZE}"
    echo "${LARGE_ICM_SIZE}" > ${large_icm_sysfs} 2>>${log_file}
    large_icm_err=$?

    if [ ${large_icm_err} -eq 0 ]; then
      log_msg "icm: large_icm written"
    else
      log_msg "icm: large_icm_err=${large_icm_err}"
      return
    fi
  fi
}

#
# Set or dump a value at the specified offset in a file.
#
sys_cfg_one_byte()
{
  local tmp_file=$1
  local name=$2
  local offset=$3
  local value=$4
  local bin_value

  if [ ${dump_level} -gt ${DUMP_LEVEL_NONE} ]; then
    value=0
    if [ -e ${tmp_file} ]; then
      value=$(hexdump -s "${offset}" -n 1 -e '/1 "%d" "\n"' "${tmp_file}")
    fi
    echo "sys: ${name}=${value}"
  elif [ -n "${value}" ]; then
    bin_value=$(echo "${value}" | tr '[:lower:]' '[:upper:]')
    if [ ."$value" = ."TRUE" ]; then
      bin_value='\x01'
    else
      bin_value='\x00'
    fi
    printf "${bin_value}" | dd of="${tmp_file}" seek="${offset}" bs=1 count=1 conv=notrunc 2> /dev/null

    has_change=1
    log_msg "sys: ${name}=${value}"
  fi
}

#
# Dump a multi-byte value at the specified offset in a file.
#
sys_cfg_multi_byte()
{
  local tmp_file=$1
  local name=$2
  local offset=$3
  local num_bytes=$4
  local value=$5

  if [ ${dump_level} -eq ${DUMP_LEVEL_NONE} ]; then
    return
  fi

  value=0
  if [ -e ${tmp_file} ]; then
    value=$(hexdump -s "${offset}" -n "${num_bytes}" -e '"0x%X" "\n"' "${tmp_file}")
  fi
  echo "sys: ${name}=${value}"
}

#
# This function reads the BfModes EFI struct from a UEFI variable directly.
# Below are the offsets defined in UEFI which is 4(fixed header) plus the offset
# of the variable within the BfModes struct. These offsets are not supposed to
# change in order to be backward compatible with previous releases.
#   VARIABLE(Name)       OFFSET(Byte)  SIZE(Byte)
#   INTERNAL_CPU_MODEL        4            1
#   HOST_PRIV_LEVEL           5            1
#   NIC_MODE                  6            1
#
sysfs_dump_bf_modes()
{
  local tmp_file=${TMP_DIR}/.bfcfg-bfmodes-data
  local internal_cpu_model internal_cpu_model_str
  local host_priv_level host_priv_level_str
  local nic_mode nic_mode_str

  if [ ${dump_level} -eq ${DUMP_LEVEL_NONE} ]; then
    return
  fi

  if [ ! -e "${bf_modes_sysfs}" ]; then
    log_msg "misc: failed to find the ${bf_modes_sysfs} EFI variable"
    return
  fi

  # shellcheck disable=SC2216
  yes | cp -f ${bf_modes_sysfs} ${tmp_file}
  internal_cpu_model=$(hexdump -s 4 -n 1 -e '/1 "%d" "\n"' ${tmp_file})

  # shellcheck disable=SC2216
  yes | cp -f ${bf_modes_sysfs} ${tmp_file}
  host_priv_level=$(hexdump -s 5 -n 1 -e '/1 "%d" "\n"' ${tmp_file})

  # shellcheck disable=SC2216
  yes | cp -f ${bf_modes_sysfs} ${tmp_file}
  nic_mode=$(hexdump -s 6 -n 1 -e '/1 "%d" "\n"' ${tmp_file})

  if [ "${internal_cpu_model}" -eq 0 ]; then
    internal_cpu_model_str="SEPARATED"
  elif [ "${internal_cpu_model}" -eq 1 ]; then
    internal_cpu_model_str="EMBEDDED"
  else
    internal_cpu_model_str="UNKNOWN"
  fi

  if [ "${host_priv_level}" -eq 0 ]; then
    host_priv_level_str="PRIVILEGED"
  elif [ "${host_priv_level}" -eq 1 ]; then
    host_priv_level_str="RESTRICTED"
  else
    host_priv_level_str="UNKNOWN"
  fi

  if [ "${nic_mode}" -eq 0 ]; then
    nic_mode_str="DPU_MODE"
  elif [ "${nic_mode}" -eq 1 ]; then
    nic_mode_str="NIC_MODE"
  else
    nic_mode_str="UNKNOWN"
  fi

  echo "misc: INTERNAL_CPU_MODEL=${internal_cpu_model_str}"
  echo "misc: HOST_PRIV_LEVEL=${host_priv_level_str}"
  echo "misc: NIC_MODE=${nic_mode_str}"
}

sysfs_dump_pass_settings()
{
  local tmp_file=${TMP_DIR}/.bfcfg-pass-settings
  local default_pass_policy

  if [ ${dump_level} -eq ${DUMP_LEVEL_NONE} ]; then
    return
  fi

  if [ ! -e "${pass_settings_sysfs}" ]; then
    log_msg "misc: failed to find the ${pass_settings_sysfs} EFI variable"
    return
  fi

  # shellcheck disable=SC2216
  yes | cp -f ${pass_settings_sysfs} ${tmp_file}
  default_pass_policy=$(hexdump -s 4 -n 1 -e '/1 "%d" "\n"' ${tmp_file})
  echo "misc: UEFI_DEFAULT_PASS_POLICY_ENABLE=${default_pass_policy}"
}

#
# This function writes to the BfSysCfg UEFI variable directly.
# Below are the offsets defined in UEFI which is 4(fixed header) plus the offset
# of the variable within the BfSysCfg struct. These offsets are not supposed to
# change in order to be backward compatible with previous releases.
#   VARIABLE(Name)                 OFFSET(Byte)  SIZE(Byte)
#   SYS_ENABLE_SMMU                24             1
#   SYS_DISABLE_SPMI               25             1
#   SYS_ENABLE_2ND_EMMC            26             1
#   SYS_BOOT_PROTECT               27             1
#   SYS_ENABLE_SPCR                32             1
#   SYS_DISABLE_PCIE               33             1
#   SYS_ENABLE_OPTEE               34             1
#   SYS_DISABLE_TMFF               35             1
#   SYS_ENABLE_I2C0                36             1
#   SYS_DISABLE_FORCE_PXE_RETRY    37             1
#   SYS_ENABLE_BMC_FIELD_MODE      38             1
#   SYS_DISABLE_HEST               49             1
#   SYS_L3_CACHE_PART_LEVEL        50             1
#   SYS_LLC_READ_ALLOC             51             1
#   SYS_ENABLE_I2C3                52             1
#   SYS_ENABLE_FORCE_BOOT_RETRY    53             1
#   SYS_ENABLE_OEM_MFG_CONFIG      54             1
#   SYS_DISABLE_I2C1               55             1
#   SYS_DISABLE_WDOG_AFTER_BOOT    57             1
#   SYS_ENABLE_BMC_WAIT            59             1
#   SYS_DISABLE_CONFIG_AUTO_REBOOT 60             1
#   SYS_DISABLE_AUTO_BOOT_REFRESH  66             1
#   SYS_DISPLAY_BMC_NET_CONFIG     67             1
#
sys_cfg()
{
  local tmp_file=${TMP_DIR}/.bfcfg-sysfs-data

  has_sys_cfg=0

  if [ ! -e "${sys_cfg_sysfs}" ]; then
    log_msg "sys: failed to find the ${sys_cfg_sysfs} EFI variable"
    return
  fi

  # shellcheck disable=SC2216
  yes | cp -f ${sys_cfg_sysfs} ${tmp_file}
  has_change=0

  sys_cfg_one_byte ${tmp_file} "ENABLE_SMMU" 24 "${SYS_ENABLE_SMMU}"
  sys_cfg_one_byte ${tmp_file} "DISABLE_SPMI" 25 "${SYS_DISABLE_SPMI}"
  sys_cfg_one_byte ${tmp_file} "ENABLE_2ND_EMMC" 26 "${SYS_ENABLE_2ND_EMMC}"
  sys_cfg_one_byte ${tmp_file} "BOOT_PROTECT" 27 "${SYS_BOOT_PROTECT}"
  sys_cfg_one_byte ${tmp_file} "ENABLE_SPCR" 32 "${SYS_ENABLE_SPCR}"
  sys_cfg_one_byte ${tmp_file} "DISABLE_PCIE" 33 "${SYS_DISABLE_PCIE}"
  sys_cfg_one_byte ${tmp_file} "ENABLE_OPTEE" 34 "${SYS_ENABLE_OPTEE}"
  sys_cfg_one_byte ${tmp_file} "DISABLE_TMFF" 35 "${SYS_DISABLE_TMFF}"
  sys_cfg_one_byte ${tmp_file} "ENABLE_I2C0" 36 "${SYS_ENABLE_I2C0}"
  sys_cfg_one_byte ${tmp_file} "DISABLE_FORCE_PXE_RETRY" 37 "${SYS_DISABLE_FORCE_PXE_RETRY}"
  sys_cfg_one_byte ${tmp_file} "ENABLE_BMC_FIELD_MODE" 38 "${SYS_ENABLE_BMC_FIELD_MODE}"
  sys_cfg_multi_byte ${tmp_file} "LARGE_ICMC_SIZE" 39 4 "${SYS_LARGE_ICMC_SIZE}"
  sys_cfg_multi_byte ${tmp_file} "CE_THRESHOLD" 45 4 "${SYS_CE_THRESHOLD}"
  sys_cfg_one_byte ${tmp_file} "DISABLE_HEST" 49 "${SYS_DISABLE_HEST}"
  sys_cfg_one_byte ${tmp_file} "L3_CACHE_PARTITION_LEVEL" 50 "${SYS_L3_CACHE_PARTITION_LEVEL}"
  if [ ${dump_level} -gt ${DUMP_LEVEL_NONE} ]; then
    sys_cfg_one_byte ${tmp_file} "LLC_READ_ALLOC" 51 "${SYS_LLC_READ_ALLOC}" # only display the value, not set it
  fi
  sys_cfg_one_byte ${tmp_file} "ENABLE_I2C3" 52 "${SYS_ENABLE_I2C3}"
  sys_cfg_one_byte ${tmp_file} "ENABLE_FORCE_BOOT_RETRY" 53 "${SYS_ENABLE_FORCE_BOOT_RETRY}"
  sys_cfg_one_byte ${tmp_file} "ENABLE_OEM_MFG_CONFIG" 54 "${SYS_ENABLE_OEM_MFG_CONFIG}"
  sys_cfg_one_byte ${tmp_file} "DISABLE_I2C1" 55 "${SYS_DISABLE_I2C1}"
  sys_cfg_one_byte ${tmp_file} "DISABLE_WDOG_AFTER_BOOT" 57 "${SYS_DISABLE_WDOG_AFTER_BOOT}"
  sys_cfg_one_byte ${tmp_file} "ENABLE_BMC_WAIT" 59 "${SYS_ENABLE_BMC_WAIT}"
  sys_cfg_one_byte ${tmp_file} "DISABLE_CONFIG_AUTO_REBOOT" 60 "${SYS_DISABLE_CONFIG_AUTO_REBOOT}"
  sys_cfg_one_byte ${tmp_file} "DISABLE_AUTO_BOOT_REFRESH" 66 "${SYS_DISABLE_AUTO_BOOT_REFRESH}"
  sys_cfg_one_byte ${tmp_file} "DISPLAY_BMC_NET_CONFIG" 67 "${SYS_DISPLAY_BMC_NET_CONFIG}"

  if [ ${has_change} -eq 1 ]; then
    chattr -i ${sys_cfg_sysfs}
    cp ${tmp_file} ${sys_cfg_sysfs}
    sync
    has_sys_cfg=1
  fi

  rm -f ${tmp_file}
}

#
# This function reads the BfRedfishState UEFI variable directly.
# Below are the offsets defined in UEFI which is 4(fixed header) plus the offset
# of the variable within the BfRedfish struct. These offsets are not supposed to
# change in order to be backward compatible with previous releases.
#   VARIABLE(Name)       OFFSET(Byte)  SIZE(Byte)
#   CFG_SKIP_REDFISH        4            1
#
sys_redfish_cfg_dump()
{
  local tmp_file=${TMP_DIR}/.bfcfg-redfishfs-state-data

  if [ ${dump_level} -eq ${DUMP_LEVEL_NONE} ]; then
    return
  fi

  if [ ! -e "${redfish_state_cfg_sysfs}" ]; then
    log_msg "sys: failed to find the ${redfish_state_cfg_sysfs} EFI variable"
    return
  fi

  # shellcheck disable=SC2216
  yes | cp -f ${redfish_state_cfg_sysfs} ${tmp_file}
  has_change=0

  sys_cfg_one_byte ${tmp_file} "SKIP_REDFISH" 4 "${SYS_SKIP_REDFISH}"

  if [ ${has_change} -eq 1 ]; then
    log_msg "sys: changes to volatile ${redfish_state_cfg_sysfs} EFI variable will not take effect"
  fi
}

#
# This function writes to the BfRedfish UEFI variable directly.
# Below are the offsets defined in UEFI which is 4(fixed header) plus the offset
# of the variable within the BfRedfish struct. These offsets are not supposed to
# change in order to be backward compatible with previous releases.
#   VARIABLE(Name)       OFFSET(Byte)  SIZE(Byte)
#   CFG_ENABLE_REDFISH      4            1
#   CFG_RTCSYNC             5            1
#
sys_redfish_cfg()
{
  local tmp_file=${TMP_DIR}/.bfcfg-redfishfs-data

  if [ ${dump_level} -gt ${DUMP_LEVEL_NONE} ]; then
    sys_redfish_cfg_dump
  fi

  has_redfish_cfg=0

  if [ ! -e "${redfish_cfg_sysfs}" ]; then
    log_msg "sys: failed to find the ${redfish_cfg_sysfs} EFI variable"
    return
  fi

  # shellcheck disable=SC2216
  yes | cp -f ${redfish_cfg_sysfs} ${tmp_file}
  has_change=0

  sys_cfg_one_byte ${tmp_file} "ENABLE_REDFISH" 4 "${SYS_ENABLE_REDFISH}"
  sys_cfg_one_byte ${tmp_file} "RTCSYNC" 5 "${SYS_RTCSYNC}"

  if [ ${has_change} -eq 1 ]; then
    chattr -i ${redfish_cfg_sysfs}
    cp ${tmp_file} ${redfish_cfg_sysfs}
    sync
    has_redfish_cfg=1
  fi

  rm -f ${tmp_file}
}

#
# Dump the boot watchdog configuration.
# Below are the offsets defined in UEFI which is 4(fixed header) plus the offset
# of the variable within the BfBootWdog struct. These offsets are not supposed to
# change in order to be backward compatible with previous releases.
#   VARIABLE(Name)       OFFSET(Byte)  SIZE(Byte)
#   INTERVAL                  4            2
#   MODE                      6            1
#
sysfs_dump_boot_wdog()
{
  local current_mode current_interval current_mode_str
  local next_mode next_interval next_mode_str
  local tmp_file=${TMP_DIR}/.bfcfg-bootwdog-data

  if [ ${dump_level} -eq ${DUMP_LEVEL_NONE} ]; then
    return
  fi

  if [ ! -e "${boot_wdog_sysfs}" ]; then
    log_msg "misc: failed to find the ${boot_wdog_sysfs} EFI variable"
    return
  fi

  # shellcheck disable=SC2216
  yes | cp -f ${boot_wdog_sysfs} ${tmp_file}
  current_interval=$(hexdump -s 4 -n 2 -e '"%d" "\n"' "${tmp_file}")
  current_mode=$(hexdump -s 6 -n 1 -e '"%d" "\n"' "${tmp_file}")

  if [ "${current_mode}" -eq 0 ]; then
    current_mode_str="DISABLED"
  elif [ "${current_mode}" -eq 1 ]; then
    current_mode_str="STANDARD"
  elif [ "${current_mode}" -eq 2 ]; then
    current_mode_str="TIME_LIMIT"
  else
    current_mode_str="UNKNOWN"
  fi

  echo "misc: CURRENT_BOOT_WDOG_MODE=${current_mode_str}"
  echo "misc: CURRENT_BOOT_WDOG_INTERVAL=${current_interval}"

  # Watchdog settings that will take effect on next boot
  # can be confirmed with mlxbf-bootctl (may be different than
  # current boot config)
  if command -v mlxbf-bootctl 2>&1 > /dev/null; then
    next_mode=$(mlxbf-bootctl | grep "watchdog mode" | cut -d " " -f 4)
    next_interval=$(mlxbf-bootctl | grep "watchdog interval" | cut -d " " -f 4)
  fi

  if [ "${next_mode}" = "disabled" ]; then
    next_mode_str="DISABLED"
  elif [ "${next_mode}" = "standard" ]; then
    next_mode_str="STANDARD"
  elif [ "${next_mode}" = "time_limit" ]; then
    next_mode_str="TIME_LIMIT"
  else
    next_mode_str="UNKNOWN"
  fi

  if ! [ "${next_interval}" -eq "${next_interval}" ] 2>/dev/null; then
    next_interval="UNKNOWN"
  fi

  echo "misc: NEXT_BOOT_WDOG_MODE=${next_mode_str}"
  echo "misc: NEXT_BOOT_WDOG_INTERVAL=${next_interval}"
}

# Display profile root key and CA certificates info
misc_dump_profile_key_and_certs()
{
  local profile_key algo
  local tmp_file=${TMP_DIR}/.bfcfg-sysfs-data

  if [ ${dump_level} -eq ${DUMP_LEVEL_NONE} ]; then
    return
  fi

  if [ -e "${bf_profile_root_key}" ]; then
    echo "misc: PROFILE_ROOT_KEY_INSTALLED=1"

    if [ ${dump_level} -ge ${DUMP_LEVEL_EXTRA} ]; then
      cp -f ${bf_profile_root_key} ${tmp_file}
      algo=$(hexdump -s 8 -n 1 -e '/1 "%d" "\n"' ${tmp_file})
      algo=$(( $algo & 0xf ))
      if [ "$algo" = "0" ]; then
        profile_key=$(dd if=${bf_profile_root_key} skip=68 count=960 bs=1 2> /dev/null | xxd -p | tr -d '\n')
        echo "misc: PROFILE_ROOT_KEY_ALGO=RSA"
        echo "misc:"
        echo "  Modulus: $profile_key"
      elif [ "$algo" = "1" ]; then
        profile_key=$(dd if=${bf_profile_root_key} skip=68 count=960 bs=1 2> /dev/null | xxd -p | tr -d '\n')
        echo "misc: PROFILE_ROOT_KEY_ALGO=ECC"
        echo "misc:"
        echo "  XY: $profile_key"
      fi
    fi
  fi

  if [ -e "${bf_profile_ca_cert_nvidia}" ]; then
    echo "misc: PROFILE_CA_CERT_NVIDIA_INSTALLED=1"
    if [ ${dump_level} -ge ${DUMP_LEVEL_EXTRA} ]; then
      if command -v openssl &> /dev/null; then
        echo "misc:"
        echo "  Profile CA (NVIDIA):"
        dd if=${bf_profile_ca_cert_nvidia} skip=12 count=2040 bs=1 of=${tmp_file} 2> /dev/null
        openssl x509 -text -inform der -in ${tmp_file}
      else
        log_msg "misc: 'openssl' needs to be installed to dump certificates"
      fi
    fi
  fi

  if [ -e "${bf_profile_ca_cert_oem}" ]; then
    echo "misc: PROFILE_CA_CERT_OEM_INSTALLED=1"
    if [ ${dump_level} -ge ${DUMP_LEVEL_EXTRA} ]; then
      if command -v openssl &> /dev/null; then
        echo "misc:"
        echo "  Profile CA (OEM):"
        dd if=${bf_profile_ca_cert_oem} skip=12 count=2040 bs=1 of=${tmp_file} 2> /dev/null
        openssl x509 -text -inform der -in ${tmp_file}
      else
        log_msg "misc: 'openssl' needs to be installed to dump certificates"
      fi
    fi
  fi

  if [ -e "${bf_profile_config_sub_ca_cert_nvidia}" ]; then
    echo "misc: PROFILE_CONFIG_CA_CERT_NVIDIA_INSTALLED=1"
    if [ ${dump_level} -ge ${DUMP_LEVEL_EXTRA} ]; then
      if command -v openssl &> /dev/null; then
        echo "misc:"
        echo "  Profile Config CA (NVIDIA):"
        dd if=${bf_profile_config_sub_ca_cert_nvidia} skip=12 count=2040 bs=1 of=${tmp_file} 2> /dev/null
        openssl x509 -text -inform der -in ${tmp_file}
      else
        log_msg "misc: 'openssl' needs to be installed to dump certificates"
      fi
    fi
  fi

  if [ -e "${bf_profile_config_sub_ca_cert_oem}" ]; then
    echo "misc: PROFILE_CONFIG_CA_CERT_OEM_INSTALLED=1"
    if [ ${dump_level} -ge ${DUMP_LEVEL_EXTRA} ]; then
      if command -v openssl &> /dev/null; then
        echo "misc:"
        echo "  Profile Config CA (OEM):"
        dd if=${bf_profile_config_sub_ca_cert_oem} skip=12 count=2040 bs=1 of=${tmp_file} 2> /dev/null
        openssl x509 -text -inform der -in ${tmp_file}
      else
        log_msg "misc: 'openssl' needs to be installed to dump certificates"
      fi
    fi
  fi

  rm -f ${tmp_file}
  return
}

misc_cfg()
{
  local mac value
  local tmp_file=${TMP_DIR}/.bfcfg-misc-data

  if [ ${dump_level} -gt ${DUMP_LEVEL_NONE} ]; then
    sysfs_dump_bf_modes
    sysfs_dump_pass_settings
    sysfs_dump_boot_wdog

    # Rshim MAC address.
    mac=$(hexdump -v -e '/1 "%02x"' ${rshim_efi_mac_sysfs})
    mac="${mac:8:2}:${mac:10:2}:${mac:12:2}:${mac:14:2}:${mac:16:2}:${mac:18:2}"
    echo "misc: NET_RSHIM_MAC=${mac}"

    # PXE DHCP Class Identifier.
    value=""
    if [ -f "${pxe_dhcp_class_id_sysfs}" ]; then
      value=$(xxd -s 4 -p ${pxe_dhcp_class_id_sysfs} 2>/dev/null | xxd -r -p)
    fi
    echo "misc: PXE_DHCP_CLASS_ID=${value}"

    # BF bundle version.
    value=""
    if [ -f "${bf_bundle_version_sysfs}" ]; then
      value=$(xxd -s 4 -p ${bf_bundle_version_sysfs} 2>/dev/null | xxd -r -p)
    fi

    if [ -n "${value}" ]; then
      echo "misc: BF_BUNDLE_VERSION=${value}"
    fi

    # Display profile root key and CA certificates info
    misc_dump_profile_key_and_certs
  else
    # Rshim MAC address.
    if [ -n "${NET_RSHIM_MAC}" ]; then
      mac="${NET_RSHIM_MAC//:/\\x}"
      mac="\\x07\\x00\\x00\\x00\\x${mac}"
      printf "${mac}" > ${tmp_file}
      chattr -i ${rshim_efi_mac_sysfs}
      cp ${tmp_file} ${rshim_efi_mac_sysfs}
      rm -f ${tmp_file}
      log_msg "misc: NET_RSHIM_MAC=${NET_RSHIM_MAC}"
    fi

    # PXE DHCP Class Identifier.
    if [ -n "${PXE_DHCP_CLASS_ID}" ]; then
      value="\\x07\\x00\\x00\\x00${PXE_DHCP_CLASS_ID}"
      printf "${value}" > ${tmp_file}
      if [ -f "${pxe_dhcp_class_id_sysfs}" ]; then
        chattr -i ${pxe_dhcp_class_id_sysfs}
      fi
      cp ${tmp_file} ${pxe_dhcp_class_id_sysfs}
      rm -f ${tmp_file}
      log_msg "misc: PXE_DHCP_CLASS_ID=${PXE_DHCP_CLASS_ID}"
    fi

    # BF bundle version.
    if [ -n "${BF_BUNDLE_VERSION}" ]; then
      value="\\x07\\x00\\x00\\x00${BF_BUNDLE_VERSION}"
      printf "${value}" > ${tmp_file}
      if [ -f "${bf_bundle_version_sysfs}" ]; then
        chattr -i ${bf_bundle_version_sysfs}
      fi
      cp ${tmp_file} ${bf_bundle_version_sysfs}
      rm -f ${tmp_file}
      log_msg "misc: BF_BUNDLE_VERSION=${BF_BUNDLE_VERSION}"
    fi

    if [ ."${BMC_UPGRADE_RESET}" == ."1" ]; then
      printf "\\x07\\x00\\x00\\x00\\x01" > ${tmp_file}
      if [ -f "${bmc_upgrade_reset_cfg_sysfs}" ]; then
        chattr -i ${bmc_upgrade_reset_cfg_sysfs}
      fi
      cp ${tmp_file} ${bmc_upgrade_reset_cfg_sysfs}
      rm -f ${tmp_file}
      log_msg "misc: BMC_UPGRADE_RESET=${BMC_UPGRADE_RESET}"
    fi
  fi
}

#
# Parse the partition configuration and export $EFI_PART, $ROOT_PART and
# $PERSIST_PART
#
part_info()
{
  local disk part value

  # Source the configuration if exists.
  # shellcheck source=/dev/null
  [ -e "${cfg_file}" ] && . ${cfg_file}

  for disk in {0..15}; do
    eval "disk_name=\${DISK${disk}_NAME}"
    # shellcheck disable=SC2154
    [ -z "${disk_name}" ] && continue

    for part in {0..15}; do
      eval "value=\${DISK${disk}_PART${part}_MOUNT}"
      if [ ."${value}" = ."/" ]; then
        echo ROOT_PART="${disk_name}p${part}"
      fi

      eval "value=\${DISK${disk}_PART${part}_TYPE}"
      if [ ."${value}" = ."EFI" ]; then
        echo EFI_PART="${disk_name}p${part}"
      fi

      eval "value=\${DISK${disk}_PART${part}_PERSIST}"
      if [ -n "${value}" ]; then
        echo PERSIST_PART="${disk_name}p${part}"
      fi
    done
  done
}

#
# Add header into a boot entry
# $1: output file
#
boot_cfg_add_header()
{
  printf "\\x07\\x00\\x00\\x00\\x01\\x00\\x00\\x00" >> "$1"
}

#
# Add length into a boot entry
# $1: output file
# $2: length
#
boot_cfg_add_len()
{
  len=$2
  len=$(printf '%04x' "${len}")
  printf "\\x${len:2:2}\\x${len:0:2}" >> "$1"
}

#
# Add http into a boot entry
# $1: output file
#
boot_cfg_add_http()
{
  printf "\\x03\\x18\\x04\\x00" >> "$1"
}

#
# Add tail into a boot entry
# $1: output file
#
boot_cfg_add_tail()
{
  printf "\\x7f\\xff\\x04\\x00" >> "$1"
}

#
# Add name into a boot entry
# $1: output file
# $2: name
#
boot_cfg_add_name()
{
  local idx name=$2

  for ((idx=0; idx<${#name}; idx++)); do
    printf "${name:$idx:1}" >> "$1"
    printf "\\x00" >> "$1"
  done
  printf "\\x00\\x00" >> "$1"
}

#
# Add PCIe info into a boot entry
# $1: output file
# $2: port name
#
# Note: The quoting here is intentional, spaces need to be kept out
# shellcheck disable=SC2140
boot_cfg_add_pcie()
{
  local idx address dev_id func_id dev_path cnt

  # Get PCI path
  dev_path=$(lspci -t | head -n 1 | grep -o "[0-9][0-9]\.[0-9]*")
  if [ -z "${dev_path}" ]; then
    ${ECHO} "Failed to find device path"
    return
  fi

  # PciRoot
  printf "\\x02\\x01\\x0c\\x00\\xd0\\x41\\x03\\x0a\\x00\\x00\\x00\\x00" >> "$1"

  idx=1
  cnt=$(echo ${dev_path} | wc -w)
  for address in ${dev_path}; do
    # Type(1B)/SubType(1B)/Len(2B)
    printf "\\x01\\x01\\x06\\x00" >> "$1"

    # Get DevId and FuncId
    dev_id=$(echo ${address} | tr '.' ' ' | awk '{print $1}')
    func_id=$(echo ${address} | tr '.' ' ' | awk '{print $2}')

    # Adjust FuncId for P1
    if [ "$idx" -eq ${cnt} -a "$2" = "NIC_P1" ]; then
      func_id=$((func_id + 1))
    fi

    printf "\\x${func_id}\\x${dev_id}" >> "$1"
    idx=$((idx + 1))
  done
}

#
# Add Eth/MAC info into a boot entry
# $1: output file
# $2: MAC address (xx:xx:xx:xx:xx:xx)
#
boot_cfg_add_eth()
{
  local idx mac=$2

  printf "\\x03\\x0b\\x25\\x00" >> "$1"
  mac="\\x${mac//:/\\x}"
  printf "${mac}" >> "$1"
  for idx in {1..26}; do
    printf "\\x00" >> "$1"
  done
  printf "\\x01" >> "$1"
}

#
# Add VLAN info into a boot entry
# $1: output file
# $2: decimal VLAN id
#
boot_cfg_add_vlan()
{
  local vlan_id=$2

  vlan_id=$(printf '%04x' "${vlan_id}")
  printf "\\x03\\x14\\x06\\x00\\x${vlan_id:2:2}\\x${vlan_id:0:2}" >> "$1"
}

#
# Add IPv4 info into a boot entry
# $1: output file
#
boot_cfg_add_ipv4()
{
  local idx

  printf "\\x03\\x0c\\x1b\\x00" >> "$1"
  for idx in {1..23}; do
    printf "\\x00" >> "$1"
  done
}

#
# Add IPv6 info into a boot entry
# $1: output file
#
boot_cfg_add_ipv6()
{
  local idx

  printf "\\x03\\x0d\\x3c\\x00" >> "$1"
  for idx in {1..39}; do
    printf "\\x00" >> "$1"
  done
  printf "\\x40" >> "$1"
  for idx in {1..16}; do
    printf "\\x00" >> "$1"
  done
}

get_hca_p0_mac()
{
  local dev devmac devid p0mac devmac_oui num

  num=0
  base_mac=$(bfhcafw flint q 2>/dev/null | grep "^Base MAC" | awk '{print $3}')
  base_mac=$(echo $base_mac | cut -c1-6)
  p0mac="feffffffffff"
  for dev in /sys/class/net/*; do
    [ ! -f ${dev}/device/device ] && continue
    devid=$(cat ${dev}/device/device)
    [ ."${devid}" != ."0xa2d2" -a ."${devid}" != ."0xa2d6" -a ."${devid}" != ."0xa2dc" ] && continue
    devmac=$(cat ${dev}/address | sed 's/://g' | tr '[:upper:]' '[:lower:]')
    devmac_oui=$(echo ${devmac} | cut -c1-6)
    [ ."${base_mac}" != ."$devmac_oui" ] && continue
    if [ "${devmac}" \< "${p0mac}" ]; then
      p0mac=${devmac}
    fi
    num=$((num + 1))
  done
  echo "0x${p0mac}"
  echo "${num}"
}

boot_cfg_dump()
{
  local value tmp_entry boot_order boot_order_str boot_current timeout

  if [ ${dump_level} -eq ${DUMP_LEVEL_NONE} ]; then
    return
  fi

  boot_current=$(efibootmgr 2>/dev/null | grep BootCurrent | awk '{print $2}')
  timeout=$(efibootmgr 2>/dev/null | grep Timeout | awk '{print $2}')
  boot_order=$(efibootmgr 2>/dev/null | grep BootOrder | awk '{print $2}')
  boot_order_str=$(echo "${boot_order}" | fold -w 40 | sed -e 's/^/    /')

  echo "boot: BOOT_TIMEOUT_SEC=${timeout}"
  echo "boot: BOOT_CURRENT=${boot_current}"
  if [ ${dump_level} -ge ${DUMP_LEVEL_EXTRA} ]; then
    echo "boot:"
    echo "  BOOT_ORDER:"
    echo "${boot_order_str}"
    echo "  BOOT_OPTIONS:"
    echo "$(efibootmgr | tail +4 | sed -e 's/^/    /')"
  fi
}

boot_get_oob_mac()
{
  local mac ifname msb

  # Get MAC
  if [ "$bf_chip" = "BlueField-4" ]; then
    ifname=$(ls -d /sys/class/net/*/device/driver/module/drivers/pci:lan743x | cut -d '/' -f 5)
    mac=$(ethtool -e ${ifname} | grep 0x0000 | awk 'BEGIN {OFS=":"} {print $3, $4, $5, $6, $7, $8}' 2>/dev/null)
  else
    mac=$(cat ${oob_mac_sysfs} 2>/dev/null)
  fi

  # Check multicast bit
  if [ ."$mac" = ."00:00:00:00:00:00" ]; then
    mac=
  else
    msb="0x${mac:0:2}"
    msb=$((msb))
    if (($msb & 0x1)); then
      mac=
    fi
  fi

  echo $mac
}

#
# Boot Entry configuration
# Each entry BOOT<N> could have the following format:
#   PXE:
#     BOOT<N> = NET-<NIC_P0 | NIC_P1 | OOB | RSHIM>[.<vlan-id>]-<IPV4 | IPV6>[-HTTP]
#   UEFI Shell:
#     BOOT<N> = UEFI_SHELL
#   DISK: boot entries created during OS installation.
#     BOOT<N> = DISK
# Example:
#   BOOT0 = NET-NIC_P1-IPV4
#   BOOT1 = DISK
#
boot_cfg()
{
  local i tmp idx entry ifname proto mac upper_mac lower_mac vlan vlan_len disk_entry_idx
  local shell_entry disk_entries boot_order
  local tmp_dir=${TMP_DIR}/.boot_cfg
  local tmp_file=${tmp_dir}/boot
  local tmp_vlan_file=${tmp_dir}/vlan
  local value tmp_entry old_boot_order
  local l4proto l4proto_len
  local oob_mac_addr num oob_vlan_path

  if [ ${dump_level} -gt ${DUMP_LEVEL_NONE} ]; then
    boot_cfg_dump
    return
  fi

  rm -rf ${tmp_dir} 2>/dev/null
  mkdir -p ${tmp_dir}

  old_boot_order=$(efibootmgr 2>/dev/null | grep BootOrder | awk '{print $2}' | tr ',' ' ')

  # Check whether to preserve booting entries from disk.
  for i in {0..32}; do
    eval "entry=\${BOOT${i}}"
    entry=$(echo "${entry}" | tr '[:lower:]' '[:upper:]')
    [ -n "${entry}" ] && tmp=${entry}

    if [ "${entry}" = "DISK" ]; then
      for tmp_entry in ${old_boot_order}; do
        value=$(efibootmgr -v 2>/dev/null | grep "^Boot${tmp_entry}\*" | grep -w HD)
        if [ -n "${value}" ]; then
          disk_entries="${disk_entries} ${tmp_entry}"
        fi
      done
      break
    elif [ "${entry}" = "UEFI_SHELL" ]; then
      # Save the UEFI shell option
      shell_entry=$(efibootmgr 2>/dev/null | grep "EFI Internal Shell" | cut -c5-8)
      cp -f ${efivars}/Boot"${shell_entry}"* ${tmp_dir}/shell
    fi
  done

  # Don't continue if nothing configured.
  [ -z "${tmp}" ] && return

  # Save disk entries
  for i in ${disk_entries}; do
    cp ${efivars}/Boot"${i}"* ${tmp_dir}/
  done

  # Remove all existing entries
  rm -f ${efivars}/Boot00*

  # Scan it again to add boot entries
  idx=0
  for i in {0..32}; do
    eval "entry=\${BOOT${i}}"
    [ -z "${entry}" ] && continue
    rm -f ${tmp_file} 2>/dev/null
    entry=$(echo "${entry}" | tr '[:lower:]' '[:upper:]')

    # BOOT<N> = DISK
    if [ ."${entry}" = ."DISK" ]; then
      disk_entry_idx=64
      for j in $disk_entries; do
        tmp=$(printf '%04x' ${disk_entry_idx})
        cp "${tmp_dir}/Boot${j}-${efi_global_var_guid}" "${efivars}/Boot${tmp}-${efi_global_var_guid}"
        [ -n "${boot_order}" ] && boot_order="${boot_order},"
        boot_order="${boot_order}${tmp}"
        disk_entry_idx=$((disk_entry_idx + 1))
      done
      continue
    fi

    # BOOT<N> = UEFI_SHELL
    if [ ."${entry}" = ."UEFI_SHELL" ]; then
      if [ ! -e "${tmp_dir}/shell" ]; then
        log_msg "boot: UEFI shell entry not found"
        continue
      fi
      cp -f ${tmp_dir}/shell ${tmp_file}
    else
      # BOOT<N> = NET-<NIC_P0 | NIC_P1 | OOB | RSHIM>[.<vlan-id>]-<IPV4 | IPV6>[-HTTP]
      ifname=$(echo "${entry}" | cut -d '-' -f 2)
      proto=$(echo "${entry}" | cut -d '-' -f 3)
      vlan=""
      vlan_len=0
      l4proto=$(echo "${entry}" | cut -d '-' -f 4)
      l4proto_len=0
      if [[ "${ifname}" = *"."* ]]; then
        vlan=$(echo "${ifname}" | cut -d '.' -f 2)
        ifname=$(echo "${ifname}" | cut -d '.' -f 1)
        vlan_len=6
      fi
      if [ -z "${ifname}" ] || [ -z "${proto}" ]; then
        log_msg "boot: invalid format ${entry}"
        continue
      fi
      if [ "${proto}" != "IPV4" ] && [ "${proto}" != "IPV6" ]; then
        log_msg "boot: invalid format ${entry}, need IPV4 or IPV6"
        continue
      fi

      if [ "${l4proto}" = "HTTP" ]; then
        l4proto_len=4
      fi

      boot_cfg_add_header "${tmp_file}"

      case "${ifname}" in
      OOB)
        mac=$(boot_get_oob_mac)
        if [ -z "${mac}" ]; then
          log_msg "boot: failed to get MAC for ${entry}"
          continue
        fi
        if [ "${proto}" = "IPV4" ]; then
          boot_cfg_add_len "${tmp_file}" $((68 + vlan_len + l4proto_len))
        else
          boot_cfg_add_len "${tmp_file}" $((101 + vlan_len + l4proto_len))
        fi
        boot_cfg_add_name "${tmp_file}" "${entry}"
        oob_mac_addr=$(echo "${mac}" | tr '[:lower:]' '[:upper:]')
        oob_mac_addr="${oob_mac_addr//:/}"
        ;;

      RSHIM)
        [ "$bf_chip" = "BlueField-4" ] && continue
        mac=$(hexdump -v -e '/1 " %02x"' ${rshim_efi_mac_sysfs} 2>/dev/null)
        if [ -z "${mac}" ]; then
          log_msg "boot: failed to get MAC for ${entry}"
          continue
        fi
        mac="${mac// /:}"
        mac=${mac: -17}
        if [ "${proto}" = "IPV4" ]; then
          boot_cfg_add_len "${tmp_file}" $((68 + vlan_len + l4proto_len))
        else
          boot_cfg_add_len "${tmp_file}" $((101 + vlan_len + l4proto_len))
        fi
        boot_cfg_add_name "${tmp_file}" "${entry}"
        ;;

      NIC_P0|NIC_P1)
        {
          read -r mac
          read -r num
        } <<< "$(get_hca_p0_mac)"
        if [ -z "${mac}" ] || [ ."${mac}" = ."N/A" ] || [ ."${mac}" = ."0xfeffffffffff" ]; then
          log_msg "boot: failed to get MAC for ${entry}"
          bfcfg_rc=1
          continue
        fi
        if [ "${ifname}" = "NIC_P1" ]; then
          [ -z "${num}" ] && continue
          [ ${num} -lt 2 ] && continue
          mac=$((mac + 1))
        fi
        mac=$(printf '%012x' ${mac})
        # shellcheck disable=SC2116,SC2096,SC2086
        mac=$(echo ${mac:0:2}:${mac:2:2}:${mac:4:2}:${mac:6:2}:${mac:8:2}:${mac:10:2})

        if [ "${proto}" = "IPV4" ]; then
          boot_cfg_add_len "${tmp_file}" $((104 + vlan_len + l4proto_len))
        else
          boot_cfg_add_len "${tmp_file}" $((137 + vlan_len + l4proto_len))
        fi
        boot_cfg_add_name ${tmp_file} "${entry}"
        boot_cfg_add_pcie "${tmp_file}" "${ifname}"
        ;;

      *)
        continue
        ;;
      esac

      boot_cfg_add_eth "${tmp_file}" "${mac}"

      # Remove old VLAN configuration
      upper_mac=$(echo "${mac}" | tr '[:lower:]' '[:upper:]')
      upper_mac="${upper_mac//:/}"
      lower_mac="${mac//:/}"
      eval "tmp=\${${ifname}_VLAN_SET}"
      if [ -z "${tmp}" ]; then
        eval "${ifname}_VLAN_SET=1"
        chattr -i "${efivars}/${upper_mac}-${efi_vlan_var_guid}" 2>/dev/null
        chattr -i "${efivars}/${lower_mac}-${efi_vlan_var_guid}" 2>/dev/null
        # Remove efivar with upper case MAC address (valid) and
        # also the efivar with lower case MAC address. The efivar
        # with lower case MAC address was created erraneously in previous version
        # of this script and hence needs rectification.
        rm -f "${efivars}/${upper_mac}-${efi_vlan_var_guid}" 2>/dev/null
        rm -f "${efivars}/${lower_mac}-${efi_vlan_var_guid}" 2>/dev/null
      fi
      # Add new VLAN if specified
      if [ -n "${vlan}" ]; then
        tmp=$(printf '%04x' "${vlan}")
        if [ ! -e "${efivars}/${mac}-${efi_vlan_var_guid}" ]; then
          printf "\\x07\\x00\\x00\\x00\\x${tmp:2:2}\\x${tmp:0:2}" > \
            "${efivars}/${mac}-${efi_vlan_var_guid}"
        else
          chattr -i "${efivars}/${mac}-${efi_vlan_var_guid}"
          cp "${efivars}/${mac}-${efi_vlan_var_guid}" ${tmp_vlan_file}
          printf "\\x${tmp:2:2}\\x${tmp:0:2}" >> ${tmp_vlan_file}
          cp ${tmp_vlan_file} "${efivars}/${mac}-${efi_vlan_var_guid}"
        fi
        boot_cfg_add_vlan "${tmp_file}" "${vlan}"
      fi

      if [ "${proto}" = "IPV4" ]; then
        boot_cfg_add_ipv4 "${tmp_file}"
      else
        boot_cfg_add_ipv6 "${tmp_file}"
      fi

      if [ "${l4proto}" = "HTTP" ]; then
        boot_cfg_add_http "${tmp_file}"
      fi

      boot_cfg_add_tail "${tmp_file}"
    fi

    if [ -e "${tmp_file}" ]; then
      tmp=$(printf '%04x' ${idx})
      cp ${tmp_file} "${efivars}/Boot${tmp}-${efi_global_var_guid}"
      log_msg "boot: add Boot${tmp}(${entry})"
      [ -n "${boot_order}" ] && boot_order="${boot_order},"
      boot_order="${boot_order}${tmp}"
      idx=$((idx + 1))
    fi
  done

  if [ -n "$oob_mac_addr" ]; then
    oob_vlan_path=${efivars}/${oob_mac_addr}-${efi_vlan_var_guid}
    [ -e ${oob_vlan_path} ] && chattr -i ${oob_vlan_path}
    printf "\\x07\\x00\\x00\\x00\\xc8\\x0f" > ${oob_vlan_path}
  fi

  efibootmgr -o "${boot_order}" >/dev/null
  log_msg "boot: set boot order ${boot_order}"
  rm -rf ${tmp_dir} 2>/dev/null
}

# Dump secure boot config and related information
sb_cfg_dump()
{
  local secure_boot setup_mode setup_mode_str custom_mode custom_mode_str sb_enabled
  local tmp_file=${TMP_DIR}/.bfcfg-sysfs-data

  if [ ${dump_level} -eq ${DUMP_LEVEL_NONE} ]; then
    return
  fi

  if [ ! -e "${sb_state_sysfs}" ]; then
    log_msg "sb: failed to find the ${sb_state_sysfs} EFI variable"
    return
  fi

  if [ ! -e "${sb_setup_mode_sysfs}" ]; then
    log_msg "sb: failed to find the ${sb_setup_mode_sysfs} EFI variable"
    return
  fi

  # shellcheck disable=SC2216
  yes | cp -f ${sb_state_sysfs} ${tmp_file}
  secure_boot=$(hexdump -s 4 -n 1 -e '/1 "%d" "\n"' ${tmp_file})

  # shellcheck disable=SC2216
  yes | cp -f ${sb_setup_mode_sysfs} ${tmp_file}
  setup_mode=$(hexdump -s 4 -n 1 -e '/1 "%d" "\n"' ${tmp_file})

  if [ "${secure_boot}" -eq 1 ] && [ "${setup_mode}" -eq 0 ]; then
    sb_enabled=1
  else
    sb_enabled=0
  fi

  if [ "${setup_mode}" -eq 1 ]; then
    setup_mode_str="SETUP"
  else
    setup_mode_str="USER"
  fi

  echo "sb: SECURE_BOOT_ENABLED=${sb_enabled}"
  echo "sb: SETUP_MODE=${setup_mode_str}"

  if [ -e "${sb_custom_mode_sysfs}" ]; then
    # shellcheck disable=SC2216
    yes | cp -f ${sb_custom_mode_sysfs} ${tmp_file}
    custom_mode=$(hexdump -s 4 -n 1 -e '/1 "%d" "\n"' ${tmp_file})
    if [ "${custom_mode}" -eq 1 ]; then
      custom_mode_str="CUSTOM"
    else
      custom_mode_str="STANDARD"
    fi
    echo "sb: CUSTOM_MODE=${custom_mode_str}"
  fi

  if [ ${dump_level} -ge ${DUMP_LEVEL_EXTRA} ]; then
    if command -v mokutil &> /dev/null; then
      echo "sb:"
      echo "  Platform Key (PK):"
      echo "$(mokutil --pk | sed 's/^/    /')"
      echo "  Key Exchange Key (KEK):"
      echo "$(mokutil --kek | sed 's/^/    /')"
      echo "  Signatures Database (DB):"
      echo "$(mokutil --db | sed 's/^/    /')"
      echo "  Forbidden Signatures Database (DBX):"
      echo "$(mokutil --dbx | sed 's/^/    /')"
      echo "  Machine Owner Keys (MOK):"
      echo "$(mokutil --list-enrolled | sed 's/^/    /')"
    else
      log_msg "sb: 'mokutil' needs to be installed to dump certificates"
    fi
  fi

  rm -f ${tmp_file}
  return
}

# Enable UEFI Secure boot with default settings.
# UEFI password is reset to default.
sb_cfg()
{
  local sb_capsule_file=/lib/firmware/mellanox/boot/capsule/EnrollKeysCap

  if [ ${dump_level} -gt ${DUMP_LEVEL_NONE} ]; then
    sb_cfg_dump
    return
  fi

  [ "${UEFI_SECURE_BOOT}" != "TRUE" ] && return

  if [ ! -f "${sb_capsule_file}" ]; then
    log_msg "sb: failed to find the EFI capsule"
    return
  fi

  # Enable UEFI Secure boot
  bfrec --capsule ${sb_capsule_file} 2>&1 > /dev/null
  if [ $? -eq 0 ]; then
    log_msg "sb: UEFI Secure Boot will be enabled on next boot"
  else
    log_msg "sb: failed to enable UEFI Secure Boot"
  fi
}

#
# Configuration blob layout.
#
# Please refer to this page to lear more about the
# configuration binary format and various parameters.
# https://confluence.nvidia.com/pages/viewpage.action?spaceKey=SW&title=BlueField+Platform+Configuration
#
# Layout:
#   Legacy MFG configuration and dependencies (4096 bytes)
#       MFG Data      (off:   0, len: 256 bytes)
#       MFG Extension (off: 256, len:2048 bytes)
#       SYS Config    (off:2304, len:  64 bytes)
#       RDF Config    (off:2368, len:  32 bytes)
#       BF Modes      (off:2400, len:   4 bytes)
#       Reserved      (off:2404, len:1692 bytes)
#
#   Platform Config Header  (PCH) - (128 bytes)
#   Platform Config Payload (PCP) - Variable size.
#

#
# PCH fixed parameters
#
# hex: 42 5e 46 5e 5e 50 43 48
PCH_FP="B^F^^PCH"
PCH_HEADER_LEN=128
PCH_HEADER_TYPE=1
PCH_HEADER_VERSION=2
#
# PCP fixed parameters
#
PCP_TYPE_LEN=4
PCP_LENGTH_LEN=4
# This version is read by both platform firmware
# and BMC firmware. It is used to determine whether
# the given config encapsulated within the payload
# is supported. If any change in the PCP format or
# the layout this version must be bump-ed up.
# This verson number must be kept in sync with
# version defined in UEFI firmware.
PCP_VERSION=4
#
# PCP config type
#
PCP_TYPE_INVALID_CFG=0
PCP_TYPE_ARM_CFG=1
PCP_TYPE_BMC_CFG=2
PCP_TYPE_NIC_CFG=3
#
# PCP subtype
#
PCP_CFG_SUBTYPE_INVALID=0
#
# Subtype for PCP_TYPE_ARM_CFG
#
ARM_CFG_SUBTYPE_CTL=1
ARM_CFG_SUBTYPE_CTL_LEN=64
ARM_CFG_SUBTYPE_PWR=2
#ARM_CFG_SUBTYPE_PWR_LEN=
ARM_CFG_SUBTYPE_THERMAL=3
#ARM_CFG_SUBTYPE_THERMAL_LEN=
ARM_CFG_SUBTYPE_DDR=4
#ARM_CFG_SUBTYPE_DDR_LEN=
ARM_CFG_SUBTYPE_SYS=5
ARM_CFG_SUBTYPE_SYS_LEN=80
ARM_CFG_SUBTYPE_BOOT=6
ARM_CFG_SUBTYPE_BOOT_LEN=640
ARM_CFG_SUBTYPE_MISC=7
ARM_CFG_SUBTYPE_MISC_LEN=256
ARM_CFG_SUBTYPE_UEFI_SB=8
ARM_CFG_SUBTYPE_UEFI_SB_LEN=2056
#
# Subtype for PCP_TYPE_BMC_CFG
#
BMC_CFG_SUBTYPE_MISC=1
BMC_CFG_SUBTYPE_MISC_LEN=600
BMC_CFG_SUBTYPE_USER_DEFAULT=2
BMC_CFG_SUBTYPE_USER_DEFAULT_LEN=264
BMC_CFG_SUBTYPE_NTP=3
BMC_CFG_SUBTYPE_NTP_LEN=264

# Converts a value to bytes.
# Bytes are swapped after conversion.
# For example, val=128, len=4 -> \x80\x00\x00\x00
to_bytes()
{
  local val=$1
  local len=$2
  local bytes=$(printf "%0$(( $len * 2 ))X" "$val" | xxd -p -c 2 | sed '1!G;h;$!d' | tr -d '\n' | xxd -r -p | sed -e 's/[0-9A-F]\{2\}/&\\x/g' -e 's/\\x$//')

  printf "\\x$bytes"
}

# Converts bytes read from input file to integer.
# Bytes are swapped before conversion.
# For example, 0A -> 10
to_integer()
{
  local in_file=$1
  local offset=$2
  local size=$3
  local value=0

  value=$(dd if=${in_file} skip=${offset} count=${size} bs=1 2> /dev/null | xxd -p -c 1 | sed '1!G;h;$!d' | tr -d '\n')
  echo $(( 16#$value ))
}

arm_cfg_get_value()
{
  local out_file=$1
  local name=$2
  local offset=$3
  local size=$4
  local in_file=$5
  local value=0

  if [ -f "${in_file}" ]; then
    if [[ "$name" == "BOOT"* ]] \
      || [ "$name" = "PXE_DHCP_CLASS_ID" ] \
      || [ "$name" = "BF_BUNDLE_VERSION" ]; then
      value=$(dd if=${in_file} skip=${offset} count=${size} bs=1 2> /dev/null | tr -d '\0')
      if [[ "$name" == "BOOT"*"_DESC" ]] \
      || [[ "$name" == "BOOT"*"_ARGS" ]] \
      || [[ "$name" == "BOOT"*"_DEVPATH" ]]; then
        if [ -n "$value" ]; then echo "$name='$value'" >> ${out_file}; fi
      else
        if [ -n "$value" ]; then echo "$name=$value" >> ${out_file}; fi
    fi

    elif [ "${name}" = "SYS_ENABLE_SPCR" ]; then
      value=$(dd if=${in_file} skip=${offset} count=${size} bs=1 2> /dev/null | tr -d '\0')
      if [ "$value" = "0" ]; then echo "$name=FALSE" >> ${out_file}; fi
      if [ "$value" = "1" ]; then echo "$name=TRUE" >> ${out_file}; fi
      if [ "$value" = "2" ]; then echo "$name=TRUE" >> ${out_file}; fi
      # Print SYS_SPCR_PORT value
      if [ "$value" = "1" ]; then echo "SYS_SPCR_PORT=0" >> ${out_file}; fi
      if [ "$value" = "2" ]; then echo "SYS_SPCR_PORT=1" >> ${out_file}; fi

    elif [ "$name" = "SYS_CE_THRESHOLD" ]; then
      #value=$(dd if=${in_file} skip=${offset} count=${size} bs=1 2> /dev/null | tr -d '\0')
      #if [ "$value" != "FFFFFF" ]; then echo "$name=$value" >> ${out_file}; fi
      value=$(to_integer ${in_file} ${offset} $size)
      # FFFFFFFF is an invalid value.
      if [ "$value" -ne 4294967295 ]; then echo "$name=$value" >> ${out_file}; fi

    elif [ "$name" = "SYS_L3_CACHE_PARTITION_LEVEL" ]; then
      #value=$(dd if=${in_file} skip=${offset} count=${size} bs=1 2> /dev/null | tr -d '\0')
      #if [ "$value" != "FF" ]; then echo "$name=$value" >> ${out_file}; fi
      value=$(to_integer ${in_file} ${offset} $size)
      # FF is an invalid value.
      if [ "$value" -ne 255 ]; then echo "$name=$value" >> ${out_file}; fi

    elif [ "$name" = "CTL_UEFI_SECURE_BOOT_STATE" ]; then
      value=$(to_integer ${in_file} ${offset} $size)
      if [ "$value" = "1" ]; then echo "$name=ENABLED" >> ${out_file}; fi
      if [ "$value" = "2" ]; then echo "$name=DISABLED" >> ${out_file}; fi

    elif [[ "$name" == "SYS_"* ]] \
      || [[ "$name" == "CTL_"* ]] \
      || [ "$name" = "FACTORY_DEFAULT_DHCP_BEHAVIOR" ]; then
      value=$(dd if=${in_file} skip=${offset} count=${size} bs=1 2> /dev/null | tr -d '\0')
      if [ "$value" = "1" ]; then echo "$name=TRUE" >> ${out_file}; fi
      if [ "$value" = "0" ]; then echo "$name=FALSE" >> ${out_file}; fi

    elif [ "$name" = "MFG_PLAT_MODE" ]; then
      value=$(to_integer ${in_file} ${offset} $size)
      if [ $value -eq 1 ]; then echo "$name=DPU" >> ${out_file}; fi
      if [ $value -eq 2 ]; then echo "$name=NIC" >> ${out_file}; fi

    elif [ "$name" = "NET_RSHIM_MAC" ]; then
      value=$(dd if=${in_file} skip=${offset} count=${size} bs=1 2> /dev/null | xxd -p)
      mac=$(echo $value | sed -e 's/../&:/g' -e 's/:$//')
      [ "$mac" != "00:00:00:00:00:00" ] && echo "$name=$mac" >> ${out_file}

    elif [ "$name" = "NET_DHCP_IPV6_DUID" ]; then
      value=$(to_integer ${in_file} ${offset} $size)
      if [ $value -eq 1 ]; then echo "$name=UUID" >> ${out_file}; fi
      if [ $value -eq 2 ]; then echo "$name=LLT" >> ${out_file}; fi

    elif [[ "${name}" == "UEFI_SECUREBOOT_KEY"* ]]; then
      keyfile=
      value=$(to_integer ${in_file} $(( $offset + 1 )) 1)
      if [ "$value" = "1" ]; then echo "$name=PK" >> ${out_file}; keyfile=$(echo "$name" | sed 's/UEFI_SECUREBOOT_KEY/pk/'); fi
      if [ "$value" = "2" ]; then echo "$name=KEK" >> ${out_file}; keyfile=$(echo "$name" | sed 's/UEFI_SECUREBOOT_KEY/kek/'); fi
      if [ "$value" = "3" ]; then echo "$name=DB" >> ${out_file}; keyfile=$(echo "$name" | sed 's/UEFI_SECUREBOOT_KEY/db/'); fi
      if [ "$value" = "4" ]; then echo "$name=DBX" >> ${out_file}; keyfile=$(echo "$name" | sed 's/UEFI_SECUREBOOT_KEY/dbx/'); fi

      value=$(to_integer ${in_file} $(( $offset + 2)) 1)
      if [ "$value" = "1" ]; then echo "${name}_FORM=DER" >> ${out_file}; keyfile="./$keyfile.der"; fi
      if [ "$value" = "2" ]; then echo "${name}_FORM=HSH" >> ${out_file}; keyfile="./$keyfile.hsh"; fi

      local keyfilesize=$(to_integer ${in_file} $(( $offset + 3 )) 2)
      if [ -n "$keyfile" ] && [ $keyfilesize -ne 0 ] && [ $keyfilesize -le 2048 ]; then
        [ -f "$keyfile" ] && rm -f ${keyfile}
        dd if=${in_file} of=${keyfile} skip=$(( $offset + 5 )) count=${keyfilesize} bs=1 2> /dev/null
        echo "${name}_FILE=${keyfile}" >> ${out_file}
      else
        echo "** invalid UEFI Secure Boot config!"
        exit 1
      fi
    fi
  fi

  return
}

arm_sys_cfg_set_value()
{
  local out_file=$1
  local name=$2
  local offset=$3
  local size=$4
  local value=$5

  [ ! -f "${out_file}" ] && return

  if [ -n "${value}" ]; then

    if [ "$name" = "NET_RSHIM_MAC" ]; then
      printf "\\x${NET_RSHIM_MAC//:/\\x}" | dd of="${out_file}" seek="${offset}" bs=1 count=${size} conv=notrunc 2> /dev/null

    elif [ "$name" = "NET_DHCP_IPV6_DUID" ]; then
      if [ "${value^^}" == "UUID" ]; then printf "\x01" | dd of="${out_file}" seek="${offset}" bs=1 count=${size} conv=notrunc 2> /dev/null; fi
      if [ "${value^^}" == "LLT" ]; then printf "\x02" | dd of="${out_file}" seek="${offset}" bs=1 count=${size} conv=notrunc 2> /dev/null; fi

    elif [ "$name" = "MFG_PLAT_MODE" ]; then
      if [ "${value^^}" == "DPU" ]; then printf "\x01" | dd of="${out_file}" seek="${offset}" bs=1 count=${size} conv=notrunc 2> /dev/null; fi
      if [ "${value^^}" == "NIC" ]; then printf "\x02" | dd of="${out_file}" seek="${offset}" bs=1 count=${size} conv=notrunc 2> /dev/null; fi

    elif [ "$name" = "CTL_UEFI_SECURE_BOOT_STATE" ]; then
      if [ "${value^^}" == "ENABLED" ]; then printf "\x01" | dd of="${out_file}" seek="${offset}" bs=1 count=${size} conv=notrunc 2> /dev/null; fi
      if [ "${value^^}" == "DISABLED" ]; then printf "\x02" | dd of="${out_file}" seek="${offset}" bs=1 count=${size} conv=notrunc 2> /dev/null; fi

    elif [ "$name" = "SYS_CE_THRESHOLD" ] \
      || [ "$name" = "SYS_L3_CACHE_PARTITION_LEVEL" ]; then
      to_bytes ${value} ${size} |  dd of="${out_file}" seek="${offset}" bs=1 count=${size} conv=notrunc 2> /dev/null

    elif [ "${name}" = "SYS_ENABLE_SPCR" ]; then
      if [ "${value^^}" = "TRUE" ]; then
        if [ -n "${SYS_SPCR_PORT}" ]; then
          if [ "${SYS_SPCR_PORT}" = "1" ]; then printf "\x32" | dd of="${out_file}" seek="${offset}" bs=1 count=${size} conv=notrunc 2> /dev/null
          elif [ "${SYS_SPCR_PORT}" = "0" ]; then printf "\x31" | dd of="${out_file}" seek="${offset}" bs=1 count=${size} conv=notrunc 2> /dev/null
          # Ignore, if invalid value.
          else printf "\x00" | dd of="${out_file}" seek="${offset}" bs=1 count=${size} conv=notrunc 2> /dev/null
          fi
        else
          # Simply enable SPCR with default port number
          printf "\x31" | dd of="${out_file}" seek="${offset}" bs=1 count=${size} conv=notrunc 2> /dev/null
        fi
      elif [ "${value^^}" = "FALSE" ]; then
        printf "\x30" | dd of="${out_file}" seek="${offset}" bs=1 count=${size} conv=notrunc 2> /dev/null
      fi

    elif [ "$name" = "UEFI_SECUREBOOT_KEY" ]; then
      local keyoff=$offset

      local keytype_len=1
      eval "keytype=\$${name}${value}"
      keyoff=$(( $keyoff + 1 ))
      if [ "${keytype^^}" == "PK" ]; then printf "\x01" | dd of="${out_file}" seek="${keyoff}" bs=1 count=${keytype_len} conv=notrunc 2> /dev/null; fi
      if [ "${keytype^^}" == "KEK" ]; then printf "\x02" | dd of="${out_file}" seek="${keyoff}" bs=1 count=${keytype_len} conv=notrunc 2> /dev/null; fi
      if [ "${keytype^^}" == "DB" ]; then printf "\x03" | dd of="${out_file}" seek="${keyoff}" bs=1 count=${keytype_len} conv=notrunc 2> /dev/null; fi
      if [ "${keytype^^}" == "DBX" ]; then printf "\x04" | dd of="${out_file}" seek="${keyoff}" bs=1 count=${keytype_len} conv=notrunc 2> /dev/null; fi

      local keyform_len=1
      eval "keyform=\$${name}${value}_FORM"
      keyoff=$(( $keyoff + 1 ))
      if [ "${keyform^^}" == "DER" ]; then printf "\x01" | dd of="${out_file}" seek="${keyoff}" bs=1 count=${keyform_len} conv=notrunc 2> /dev/null; fi
      if [ "${keyform^^}" == "HSH" ]; then printf "\x02" | dd of="${out_file}" seek="${keyoff}" bs=1 count=${keyform_len} conv=notrunc 2> /dev/null; fi

      eval "keyfile=\$${name}${value}_FILE"
      keyoff=$(( $keyoff + 1 ))
      keyfilesize=$(stat -c%s "$keyfile")
      if [ $keyfilesize -lt 2048 ]; then
        # write the size of the file
        to_bytes ${keyfilesize} 2 |  dd of="${out_file}" seek="$(( $keyoff + 0 ))" bs=1 count=2 conv=notrunc 2> /dev/null
        # Copy the content of the file
        dd if="${keyfile}" of="${out_file}" seek="$(( $keyoff + 2 ))" bs=1 count=${keyfilesize} conv=notrunc 2> /dev/null
      else
        echo "** key file $keyfile, size $keyfilesize too big!"
        exit 1
      fi

    elif [ "${value^^}" = "TRUE" ]; then
      printf "\x31" | dd of="${out_file}" seek="${offset}" bs=1 count=${size} conv=notrunc 2> /dev/null

    elif [ "${value^^}" = "FALSE" ]; then
      printf "\x30" | dd of="${out_file}" seek="${offset}" bs=1 count=${size} conv=notrunc 2> /dev/null

    else
      printf "${value}" | dd of="${out_file}" seek="${offset}" bs=1 count=${size} conv=notrunc 2> /dev/null
    fi

  else
    # Write default values.
    if [ "$name" = "SYS_CE_THRESHOLD" ]; then
      #printf "FFFFFFFF" | dd of="${out_file}" seek="${offset}" bs=1 count=${size} conv=notrunc 2> /dev/null
      # Write FFFFFFFF to invalidate the parameter.
      to_bytes 4294967295 ${size} |  dd of="${out_file}" seek="${offset}" bs=1 count=${size} conv=notrunc 2> /dev/null
    elif [ "$name" = "SYS_L3_CACHE_PARTITION_LEVEL" ]; then
      #printf "FF" | dd of="${out_file}" seek="${offset}" bs=1 count=${size} conv=notrunc 2> /dev/null
      # Write FF to invalidate the parameter.
      to_bytes 255 ${size} |  dd of="${out_file}" seek="${offset}" bs=1 count=${size} conv=notrunc 2> /dev/null
    fi
  fi

  return
}

# Refer to this page to obtain info on configuration binary format.
# https://confluence.nvidia.com/pages/viewpage.action?spaceKey=SW&title=BlueField+Platform+Configuration
arm_cfg_to_bin()
{
  local out_file=$1

  local cfg_len=0
  local has_cfg_ctl=0
  local has_cfg_sys=0
  local has_cfg_boot=0
  local has_cfg_misc=0
  local has_cfg_uefi_secureboot=0

  if [ -n "${CTL_RESET_SYS}" ] \
    || [ -n "${CTL_DELETE_ALL_BOOT}" ] \
    || [ -n "${CTL_RESET_MISC}" ] \
    || [ -n "${CTL_DELETE_ALL_UEFI_SECURE_BOOT_KEYS}" ] \
    || [ -n "${CTL_UEFI_SECURE_BOOT_STATE}" ] \
    || [ -n "${CTL_DELETE_UEFI_PASSWORD}" ] \
    || [ -n "${CTL_SAVE_CONFIG_FILE}" ] \
    || [ -n "${CTL_RESET_LANG}" ]; then
    has_cfg_ctl=1
  fi

  [ "$has_cfg_ctl" -eq 1 ] && cfg_len=$(( $cfg_len + $ARM_CFG_SUBTYPE_CTL_LEN ))

  # Check system config
  if [ -n "${SYS_ENABLE_SMMU}" ] \
    || [ -n "${SYS_DISABLE_SPMI}" ] \
    || [ -n "${SYS_ENABLE_2ND_EMMC}" ] \
    || [ -n "${SYS_BOOT_PROTECT}" ] \
    || [ -n "${SYS_ENABLE_SPCR}" ] \
    || [ -n "${SYS_DISABLE_PCIE}" ] \
    || [ -n "${SYS_ENABLE_OPTEE}" ] \
    || [ -n "${SYS_DISABLE_TMFF}" ] \
    || [ -n "${SYS_ENABLE_I2C0}" ] \
    || [ -n "${SYS_DISABLE_FORCE_PXE_RETRY}" ] \
    || [ -n "${SYS_ENABLE_BMC_FIELD_MODE}" ] \
    || [ -n "${SYS_CE_THRESHOLD}" ] \
    || [ -n "${SYS_DISABLE_HEST}" ] \
    || [ -n "${SYS_L3_CACHE_PARTITION_LEVEL}" ] \
    || [ -n "${SYS_LLC_READ_ALLOC}" ] \
    || [ -n "${SYS_ENABLE_I2C3}" ] \
    || [ -n "${SYS_ENABLE_FORCE_BOOT_RETRY}" ] \
    || [ -n "${SYS_ENABLE_OEM_MFG_CONFIG}" ] \
    || [ -n "${SYS_DISABLE_I2C1}" ] \
    || [ -n "${SYS_ENABLE_BMC_WAIT}" ] \
    || [ -n "${SYS_DISABLE_CONFIG_AUTO_REBOOT}" ] \
    || [ -n "${SYS_DISABLE_AUTO_BOOT_REFRESH}" ] \
    || [ -n "${SYS_DISPLAY_BMC_NET_CONFIG}" ] \
    || [ -n "${SYS_ENABLE_REDFISH}" ] \
    || [ -n "${SYS_RTCSYNC}" ] \
    || [ -n "${MFG_PLAT_MODE}" ]; then
    has_cfg_sys=1
  fi

  [ "$has_cfg_sys" -eq 1 ] && cfg_len=$(( $cfg_len +  $ARM_CFG_SUBTYPE_SYS_LEN ))

  # Count the boot config if any
  boot_cnt=0
  for i in {0..32}; do
    eval "boot_id=\$BOOT${i}"
    eval "boot_img=\$BOOT${i}_DESC"
    eval "boot_dp=\$BOOT${i}_DEVPATH"
    eval "boot_args=\$BOOT${i}_ARGS"
    # Only image ID/Descriptor is mandatory.Boot image path, boot device path
    # and boot arguments are optional.
    if [ -n "$boot_id" ]; then
      boot_cnt=$(( $boot_cnt + 1 ))
      has_cfg_boot=1
    fi
  done

  [ "$has_cfg_boot" -eq 1 ] && cfg_len=$(( $cfg_len + $boot_cnt * $ARM_CFG_SUBTYPE_BOOT_LEN ))

  if [ -n "${NET_RSHIM_MAC}" ] \
    || [ -n "${FACTORY_DEFAULT_DHCP_BEHAVIOR}" ] \
    || [ -n "${PXE_DHCP_CLASS_ID}" ] \
    || [ -n "${BF_BUNDLE_VERSION}" ] \
    || [ -n "${NET_DHCP_IPV6_DUID}" ]; then
    has_cfg_misc=1
  fi

  [ "$has_cfg_misc" -eq 1 ] && cfg_len=$(( $cfg_len +  $ARM_CFG_SUBTYPE_MISC_LEN ))

  # Check UEFI Secure boot configuration.
  key_cnt=0
  for i in {0..32}; do
    eval "key_type=\$UEFI_SECUREBOOT_KEY${i}"
    eval "key_form=\$UEFI_SECUREBOOT_KEY${i}_FORM"
    eval "key_file=\$UEFI_SECUREBOOT_KEY${i}_FILE"
    if [ -n "$key_type" ] \
      && [ -n "$key_form" ] \
      && [ -f "$key_file" ]; then
      # All fields are mandatory
      key_cnt=$(( $key_cnt + 1 ))
      has_cfg_uefi_secureboot=1
    fi
  done

  [ "$has_cfg_uefi_secureboot" -eq 1 ] && cfg_len=$(( $cfg_len + $key_cnt * $ARM_CFG_SUBTYPE_UEFI_SB_LEN ))

  if [ "$has_cfg_sys" -eq 1 ] \
    || [ "$has_cfg_boot" -eq 1 ] \
    || [ "$has_cfg_misc" -eq 1 ] \
    || [ "$has_cfg_uefi_secureboot" -eq 1 ]; then
    cfg_len=$(( $cfg_len + $PCP_TYPE_LEN + $PCP_LENGTH_LEN ))
  else
    echo "no arm config found!"
    return
  fi

  #
  # Prepare the BMC Config record
  #
  dd if=/dev/zero of=${out_file} count=${cfg_len} bs=1 2> /dev/null

  local off=$(( $off + $PCP_TYPE_LEN + $PCP_LENGTH_LEN ))

  # Write control config to blob
  if [ "$has_cfg_ctl" -eq 1 ]; then
    arm_sys_cfg_set_value ${out_file} "CTL_RESET_SYS" $(( $off + 4 )) 1 "${CTL_RESET_SYS}"
    arm_sys_cfg_set_value ${out_file} "CTL_DELETE_ALL_BOOT" $(( $off + 5 )) 1 "${CTL_DELETE_ALL_BOOT}"
    arm_sys_cfg_set_value ${out_file} "CTL_RESET_MISC" $(( $off + 6 )) 1 "${CTL_RESET_MISC}"
    arm_sys_cfg_set_value ${out_file} "CTL_DELETE_ALL_UEFI_SECURE_BOOT_KEYS" $(( $off + 7 )) 1 "${CTL_DELETE_ALL_UEFI_SECURE_BOOT_KEYS}"
    arm_sys_cfg_set_value ${out_file} "CTL_UEFI_SECURE_BOOT_STATE" $(( $off + 8 )) 1 "${CTL_UEFI_SECURE_BOOT_STATE}"
    arm_sys_cfg_set_value ${out_file} "CTL_DELETE_UEFI_PASSWORD" $(( $off + 9 )) 1 "${CTL_DELETE_UEFI_PASSWORD}"
    arm_sys_cfg_set_value ${out_file} "CTL_SAVE_CONFIG_FILE" $(( $off + 10 )) 1 "${CTL_SAVE_CONFIG_FILE}"
    arm_sys_cfg_set_value ${out_file} "CTL_RESET_LANG" $(( $off + 11 )) 1 "${CTL_RESET_LANG}"
    # Add length and subtype.
    to_bytes "$(( $ARM_CFG_SUBTYPE_CTL_LEN - 3))" 2 | dd of="${out_file}" seek=$(( $off + 1 )) bs=1 count=2 conv=notrunc 2> /dev/null
    to_bytes "${ARM_CFG_SUBTYPE_CTL}" 1 | dd of="${out_file}" seek=$(( $off + 0 )) bs=1 count=1 conv=notrunc 2> /dev/null
    # Increment offset
    off=$(( $off +  $ARM_CFG_SUBTYPE_CTL_LEN))
  fi

  if [ "$has_cfg_sys" -eq 1 ]; then
    arm_sys_cfg_set_value ${out_file} "SYS_ENABLE_SMMU" $(( $off + 24 )) 1 "${SYS_ENABLE_SMMU}"
    arm_sys_cfg_set_value ${out_file} "SYS_DISABLE_SPMI" $(( $off + 25 )) 1 "${SYS_DISABLE_SPMI}"
    arm_sys_cfg_set_value ${out_file} "SYS_ENABLE_2ND_EMMC" $(( $off + 26 )) 1 "${SYS_ENABLE_2ND_EMMC}"
    arm_sys_cfg_set_value ${out_file} "SYS_BOOT_PROTECT" $(( $off + 27 )) 1 "${SYS_BOOT_PROTECT}"
    arm_sys_cfg_set_value ${out_file} "SYS_ENABLE_SPCR" $(( $off + 32 )) 1 "${SYS_ENABLE_SPCR}"
    arm_sys_cfg_set_value ${out_file} "SYS_DISABLE_PCIE" $(( $off + 33 )) 1 "${SYS_DISABLE_PCIE}"
    arm_sys_cfg_set_value ${out_file} "SYS_ENABLE_OPTEE" $(( $off + 34 )) 1 "${SYS_ENABLE_OPTEE}"
    arm_sys_cfg_set_value ${out_file} "SYS_DISABLE_TMFF" $(( $off + 35 )) 1 "${SYS_DISABLE_TMFF}"
    arm_sys_cfg_set_value ${out_file} "SYS_ENABLE_I2C0" $(( $off + 36 )) 1 "${SYS_ENABLE_I2C0}"
    arm_sys_cfg_set_value ${out_file} "SYS_DISABLE_FORCE_PXE_RETRY" $(( $off + 37 )) 1 "${SYS_DISABLE_FORCE_PXE_RETRY}"
    arm_sys_cfg_set_value ${out_file} "SYS_ENABLE_BMC_FIELD_MODE" $(( $off + 38 )) 1 "${SYS_ENABLE_BMC_FIELD_MODE}"
    arm_sys_cfg_set_value ${out_file} "SYS_CE_THRESHOLD" $(( $off + 45 )) 4 "${SYS_CE_THRESHOLD}"
    arm_sys_cfg_set_value ${out_file} "SYS_DISABLE_HEST" $(( $off + 49 )) 1 "${SYS_DISABLE_HEST}"
    arm_sys_cfg_set_value ${out_file} "SYS_L3_CACHE_PARTITION_LEVEL" $(( $off + 50 )) 1 "${SYS_L3_CACHE_PARTITION_LEVEL}"
    arm_sys_cfg_set_value ${out_file} "SYS_ENABLE_I2C3" $(( $off + 52 )) 1 "${SYS_ENABLE_I2C3}"
    arm_sys_cfg_set_value ${out_file} "SYS_ENABLE_FORCE_BOOT_RETRY" $(( $off + 53 )) 1 "${SYS_ENABLE_FORCE_BOOT_RETRY}"
    arm_sys_cfg_set_value ${out_file} "SYS_ENABLE_OEM_MFG_CONFIG" $(( $off + 54 )) 1 "${SYS_ENABLE_OEM_MFG_CONFIG}"
    arm_sys_cfg_set_value ${out_file} "SYS_DISABLE_I2C1" $(( $off + 55 )) 1 "${SYS_DISABLE_I2C1}"
    arm_sys_cfg_set_value ${out_file} "SYS_ENABLE_BMC_WAIT" $(( $off + 59 )) 1 "${SYS_ENABLE_BMC_WAIT}"
    arm_sys_cfg_set_value ${out_file} "SYS_DISABLE_CONFIG_AUTO_REBOOT" $(( $off + 60 )) 1 "${SYS_DISABLE_CONFIG_AUTO_REBOOT}"
    arm_sys_cfg_set_value ${out_file} "SYS_DISABLE_AUTO_BOOT_REFRESH" $(( $off + 66 )) 1 "${SYS_DISABLE_AUTO_BOOT_REFRESH}"
    arm_sys_cfg_set_value ${out_file} "SYS_DISPLAY_BMC_NET_CONFIG" $(( $off + 67 )) 1 "${SYS_DISPLAY_BMC_NET_CONFIG}"
    arm_sys_cfg_set_value ${out_file} "SYS_ENABLE_REDFISH" $(( $off + 68 )) 1 "${SYS_ENABLE_REDFISH}"
    arm_sys_cfg_set_value ${out_file} "SYS_RTCSYNC" $(( $off + 69 )) 1 "${SYS_RTCSYNC}"
    arm_sys_cfg_set_value ${out_file} "MFG_PLAT_MODE" $(( $off + 74 )) 1 "${MFG_PLAT_MODE}"
    # Add length and subtype.
    to_bytes "$(( $ARM_CFG_SUBTYPE_SYS_LEN - 3))" 2 | dd of="${out_file}" seek=$(( $off + 1 )) bs=1 count=2 conv=notrunc 2> /dev/null
    to_bytes "${ARM_CFG_SUBTYPE_SYS}" 1 | dd of="${out_file}" seek=$(( $off + 0 )) bs=1 count=1 conv=notrunc 2> /dev/null
    # Increment offset
    off=$(( $off +  $ARM_CFG_SUBTYPE_SYS_LEN))
  fi

  # Write boot config to blob
  if [ "$has_cfg_boot" -eq 1 ]; then
    for i in {0..32}; do
        eval "boot_id=\$BOOT${i}"
        eval "boot_img=\$BOOT${i}_DESC"
        eval "boot_dp=\$BOOT${i}_DEVPATH"
        eval "boot_args=\$BOOT${i}_ARGS"
        if [ -n "$boot_id" ]; then
          arm_sys_cfg_set_value ${out_file} "BOOT" $(( $off + 4 )) 60 "${boot_id}"
          arm_sys_cfg_set_value ${out_file} "BOOT_DESC" $(( $off + 64 )) 64 "${boot_img}"
          arm_sys_cfg_set_value ${out_file} "BOOT_DEVPATH" $(( $off + 128 )) 256 "${boot_dp}"
          arm_sys_cfg_set_value ${out_file} "BOOT_ARGS" $(( $off + 384 )) 256 "${boot_args}"
          # Add index, length and subtype
          to_bytes "${i}" 1 | dd of="${out_file}" seek=$(( $off + 3 )) bs=1 count=1 conv=notrunc 2> /dev/null
          to_bytes "$(( $ARM_CFG_SUBTYPE_BOOT_LEN - 3))" 2 | dd of="${out_file}" seek=$(( $off + 1 )) bs=1 count=2 conv=notrunc 2> /dev/null
          to_bytes "${ARM_CFG_SUBTYPE_BOOT}" 1 | dd of="${out_file}" seek=$(( $off + 0 )) bs=1 count=1 conv=notrunc 2> /dev/null
          # Increment offset
          off=$(( $off +  $ARM_CFG_SUBTYPE_BOOT_LEN))
        fi
    done
  fi

  # Write misc config to blob
  if [ "$has_cfg_misc" -eq 1 ]; then
    arm_sys_cfg_set_value ${out_file} "NET_RSHIM_MAC" $(( $off + 4 )) 6 "${NET_RSHIM_MAC}"
    arm_sys_cfg_set_value ${out_file} "NET_DHCP_IPV6_DUID" $(( $off + 10 )) 1 "${NET_DHCP_IPV6_DUID}"
    arm_sys_cfg_set_value ${out_file} "FACTORY_DEFAULT_DHCP_BEHAVIOR" $(( $off + 16 )) 1 "${FACTORY_DEFAULT_DHCP_BEHAVIOR}"
    arm_sys_cfg_set_value ${out_file} "PXE_DHCP_CLASS_ID" $(( $off + 17 )) 63 "${PXE_DHCP_CLASS_ID}"
    arm_sys_cfg_set_value ${out_file} "BF_BUNDLE_VERSION" $(( $off + 80 )) 128 "${BF_BUNDLE_VERSION}"
    # Add length and subtype.
    to_bytes "$(( $ARM_CFG_SUBTYPE_MISC_LEN - 3))" 2 | dd of="${out_file}" seek=$(( $off + 1 )) bs=1 count=2 conv=notrunc 2> /dev/null
    to_bytes "${ARM_CFG_SUBTYPE_MISC}" 1 | dd of="${out_file}" seek=$(( $off + 0 )) bs=1 count=1 conv=notrunc 2> /dev/null
    # Increment offset
    off=$(( $off +  $ARM_CFG_SUBTYPE_MISC_LEN))
  fi

  # Write UEFI secure boot config to blob
  if [ "$has_cfg_uefi_secureboot" -eq 1 ]; then
    for i in {0..32}; do
        eval "key_entry=\$UEFI_SECUREBOOT_KEY${i}"
        # All other fields are mandatory, previously checked.
        # no further checks here.
        if [ -n "$key_entry" ]; then
          arm_sys_cfg_set_value ${out_file} "UEFI_SECUREBOOT_KEY" $(( $off + 3 )) $(( $ARM_CFG_SUBTYPE_UEFI_SB_LEN - 3)) $i
          # Add length and subtype
          to_bytes "$(( $ARM_CFG_SUBTYPE_UEFI_SB_LEN - 3))" 2 | dd of="${out_file}" seek=$(( $off + 1 )) bs=1 count=2 conv=notrunc 2> /dev/null
          to_bytes "${ARM_CFG_SUBTYPE_UEFI_SB}" 1 | dd of="${out_file}" seek=$(( $off + 0 )) bs=1 count=1 conv=notrunc 2> /dev/null
          # Increment offset
          off=$(( $off +  $ARM_CFG_SUBTYPE_UEFI_SB_LEN))
        fi
    done
  fi

  # Push PCP Type and PCP Length for Arm config.
  to_bytes "$(( $cfg_len - $PCP_TYPE_LEN - $PCP_LENGTH_LEN ))" $PCP_LENGTH_LEN | dd of="${out_file}" seek=$(( 0 + $PCP_TYPE_LEN )) bs=1 count=$PCP_LENGTH_LEN conv=notrunc 2> /dev/null
  to_bytes "${PCP_TYPE_ARM_CFG}" $PCP_TYPE_LEN | dd of="${out_file}" seek=0 bs=1 count=$PCP_TYPE_LEN conv=notrunc 2> /dev/null

  return
}

bmc_cfg_get_value()
{
  local out_file=$1
  local name=$2
  local offset=$3
  local size=$4
  local in_file=$5
  local value=0

  if [ -f "${in_file}" ]; then
    if [[ "$name" == "BMC_USER_DEFAULT_USERNAME_"* ]] \
      || [[ "$name" == "BMC_USER_DEFAULT_PASSWORD_"* ]] \
      || [[ "$name" == "BMC_USER_DEFAULT_PRIVILEGE_"* ]] \
      || [ "$name" = "BMC_SEED" ] \
      || [ "$name" = "BMC_IPMI_ACCOUNTS" ] \
      || [[ "$name" == "BMC_NTP_"* ]]; then
      value=$(dd if=${in_file} skip=${offset} count=${size} bs=1 2> /dev/null | tr -d '\0')
      echo "$name='$value'" >> ${out_file}
    elif [ "$name" = "BMC_EXPIRE_PASSWORD_ON_FIRST_LOGIN" ] \
      || [ "$name" = "BMC_SOL_ENABLE" ] \
      || [ "$name" = "BMC_WEBUI_ENABLE" ] \
      || [ "$name" = "BMC_DISABLE_ROOT" ] \
      || [ "$name" = "BMC_SERIAL_REDIRECT_ENABLE" ]; then
      value=$(dd if=${in_file} skip=${offset} count=${size} bs=1 2> /dev/null | tr -d '\0')
      if [ "$value" = "1" ]; then echo "$name=yes" >> ${out_file}; fi
      if [ "$value" = "0" ]; then echo "$name=no" >> ${out_file}; fi
    elif [ "$name" = "BMC_PASSWORD_MINIMUM_LENGTH" ] \
      || [ "$name" = "BMC_LOCK_ACCOUNT_MAX_RETRY" ] \
      || [ "$name" = "BMC_SOL_CHARACTER_SEND_THRESHOLD" ] \
      || [ "$name" = "BMC_SOL_CHARACTER_ACCUMULATE_LEVEL" ]; then
      value=$(to_integer ${in_file} ${offset} $size)
      # FFFF is an invalid value.
      if [ "$value" -ne 65535 ]; then echo "$name=$value" >> ${out_file}; fi
    fi
  fi

  return
}

bmc_cfg_set_value()
{
  local out_file=$1
  local name=$2
  local offset=$3
  local size=$4
  local value=$5

  if [ -n "${value}" ] && [ -f "${out_file}" ]; then
    if [ "$name" = "BMC_USER_DEFAULT_USERNAME" ] \
      || [ "$name" = "BMC_USER_DEFAULT_PASSWORD" ] \
      || [ "$name" = "BMC_USER_DEFAULT_PRIVILEGE" ] \
      || [ "$name" = "BMC_SEED" ] \
      || [ "$name" = "BMC_IPMI_ACCOUNTS" ] \
      || [ "$name" = "BMC_NTP" ]; then
      printf "${value}" | dd of="${out_file}" seek="${offset}" bs=1 count=${size} conv=notrunc 2> /dev/null
    elif [ "${value^^}" = "YES" ]; then
      printf "\x31" | dd of="${out_file}" seek="${offset}" bs=1 count=${size} conv=notrunc 2> /dev/null
    elif [ "${value^^}" = "NO" ]; then
      printf "\x30" | dd of="${out_file}" seek="${offset}" bs=1 count=${size} conv=notrunc 2> /dev/null
    elif [ "${value}" -ge 0 ] && [ "${value}" -le 999 ]; then
      to_bytes "${value}" ${size} |  dd of="${out_file}" seek="${offset}" bs=1 count=${size} conv=notrunc 2> /dev/null
    fi
  elif [ -z "${value}" ] && [ -f "${out_file}" ]; then
    # Set default invalid values
    if [ "$name" = "BMC_PASSWORD_MINIMUM_LENGTH" ] \
      || [ "$name" = "BMC_LOCK_ACCOUNT_MAX_RETRY" ] \
      || [ "$name" = "BMC_SOL_CHARACTER_SEND_THRESHOLD" ] \
      || [ "$name" = "BMC_SOL_CHARACTER_ACCUMULATE_LEVEL" ]; then
      # Write FFFF to invalidate the parameter.
      to_bytes 65535 ${size} |  dd of="${out_file}" seek="${offset}" bs=1 count=${size} conv=notrunc 2> /dev/null
    fi
  fi

  return
}

# Refer to this page to obtain info on configuration binary format.
# https://confluence.nvidia.com/pages/viewpage.action?spaceKey=SW&title=BlueField+Platform+Configuration
bmc_cfg_to_bin()
{
  local out_file=$1

  local cfg_len=0
  local has_cfg_misc=0
  local has_cfg_user=0
  local has_cfg_ntp=0

  # Check common config
  if [ -n "${BMC_EXPIRE_PASSWORD_ON_FIRST_LOGIN}" ] \
    || [ -n "${BMC_LOCK_ACCOUNT_MAX_RETRY}" ] \
    || [ -n "${BMC_PASSWORD_MINIMUM_LENGTH}" ] \
    || [ -n "${BMC_SOL_ENABLE}" ] \
    || [ -n "${BMC_SOL_CHARACTER_SEND_THRESHOLD}" ] \
    || [ -n "${BMC_SOL_CHARACTER_ACCUMULATE_LEVEL}" ] \
    || [ -n "${BMC_DISABLE_ROOT}" ] \
    || [ -n "${BMC_SERIAL_REDIRECT_ENABLE}" ] \
    || [ -n "${BMC_WEBUI_ENABLE}" ] \
    || [ -n "${BMC_SEED}" ] \
    || [ -n "${BMC_IPMI_ACCOUNTS}" ]; then
    has_cfg_misc=1
  fi

  [ "$has_cfg_misc" -eq 1 ] && cfg_len=$(( $cfg_len +  $BMC_CFG_SUBTYPE_MISC_LEN ))

  # Count the user config if any
  user_cnt=0
  for i in {0..32}; do
    eval "name=\$BMC_USER_DEFAULT_USERNAME_${i}"
    eval "passwd=\$BMC_USER_DEFAULT_PASSWORD_${i}"
    eval "privilege=\$BMC_USER_DEFAULT_PRIVILEGE_${i}"
    if [ -n "$name" ] && [ -n "$passwd" ] && [ -n "$privilege" ]; then
        user_cnt=$(( $user_cnt + 1 ))
        has_cfg_user=1
    fi
  done

  [ "$has_cfg_user" -eq 1 ] && cfg_len=$(( $cfg_len + $user_cnt * $BMC_CFG_SUBTYPE_USER_DEFAULT_LEN ))

  # Count NTP config if any
  ntp_cnt=0
  for i in {0..32}; do
    eval "ntp=\$BMC_NTP_${i}"
    if [ -n "$ntp" ]; then
        ntp_cnt=$(( $ntp_cnt + 1 ))
        has_cfg_ntp=1
    fi
  done

  [ "$has_cfg_ntp" -eq 1 ] && cfg_len=$(( $cfg_len + $ntp_cnt * $BMC_CFG_SUBTYPE_NTP_LEN ))

  if [ "$has_cfg_misc" -eq 1 ] \
    || [ "$has_cfg_ntp" -eq 1 ] \
    || [ "$has_cfg_user" -eq 1 ]; then
    cfg_len=$(( $cfg_len + $PCP_TYPE_LEN + $PCP_LENGTH_LEN ))
  else
    echo "no bmc config found!"
    return
  fi

  # Prepare the BMC Config record
  dd if=/dev/zero of=${out_file} count=${cfg_len} bs=1 2> /dev/null

  local off=$(( $off + $PCP_TYPE_LEN + $PCP_LENGTH_LEN ))

  # Write common config to blob
  if [ "$has_cfg_misc" -eq 1 ]; then
    bmc_cfg_set_value ${out_file} "BMC_EXPIRE_PASSWORD_ON_FIRST_LOGIN" $(( $off + 3 )) 1 "${BMC_EXPIRE_PASSWORD_ON_FIRST_LOGIN}"
    bmc_cfg_set_value ${out_file} "BMC_PASSWORD_MINIMUM_LENGTH" $(( $off + 4 )) 2 "${BMC_PASSWORD_MINIMUM_LENGTH}"
    bmc_cfg_set_value ${out_file} "BMC_LOCK_ACCOUNT_MAX_RETRY" $(( $off + 6 )) 2 "${BMC_LOCK_ACCOUNT_MAX_RETRY}"
    bmc_cfg_set_value ${out_file} "BMC_SOL_ENABLE" $(( $off + 8 )) 1 "${BMC_SOL_ENABLE}"
    bmc_cfg_set_value ${out_file} "BMC_SOL_CHARACTER_SEND_THRESHOLD" $(( $off + 9 )) 2 "${BMC_SOL_CHARACTER_SEND_THRESHOLD}"
    bmc_cfg_set_value ${out_file} "BMC_SOL_CHARACTER_ACCUMULATE_LEVEL" $(( $off + 11 )) 2 "${BMC_SOL_CHARACTER_ACCUMULATE_LEVEL}"
    bmc_cfg_set_value ${out_file} "BMC_DISABLE_ROOT" $(( $off + 13 )) 1 "${BMC_DISABLE_ROOT}"
    bmc_cfg_set_value ${out_file} "BMC_SERIAL_REDIRECT_ENABLE" $(( $off + 14 )) 1 "${BMC_SERIAL_REDIRECT_ENABLE}"
    bmc_cfg_set_value ${out_file} "BMC_WEBUI_ENABLE" $(( $off + 16 )) 1 "${BMC_WEBUI_ENABLE}"
    bmc_cfg_set_value ${out_file} "BMC_SEED" $(( $off + 24 )) 64 "${BMC_SEED}"
    bmc_cfg_set_value ${out_file} "BMC_IPMI_ACCOUNTS" $(( $off + 88 )) 512 "${BMC_IPMI_ACCOUNTS}"
    # Add length and subtype.
    to_bytes "$(( $BMC_CFG_SUBTYPE_MISC_LEN - 3))" 2 | dd of="${out_file}" seek=$(( $off + 1 )) bs=1 count=2 conv=notrunc 2> /dev/null
    to_bytes "${BMC_CFG_SUBTYPE_MISC}" 1 | dd of="${out_file}" seek=$(( $off + 0 )) bs=1 count=1 conv=notrunc 2> /dev/null
    # Increment offset
    off=$(( $off +  $BMC_CFG_SUBTYPE_MISC_LEN))
  fi

  # Write user config to blob
  if [ "$has_cfg_user" -eq 1 ]; then
    for i in {0..32}; do
      eval "name=\$BMC_USER_DEFAULT_USERNAME_${i}"
      eval "passwd=\$BMC_USER_DEFAULT_PASSWORD_${i}"
      eval "privilege=\$BMC_USER_DEFAULT_PRIVILEGE_${i}"
      if [ -n "$name" ] && [ -n "$passwd" ] && [ -n "$privilege" ]; then
        bmc_cfg_set_value ${out_file} "BMC_USER_DEFAULT_PRIVILEGE" $(( $off + 8 )) 64 "${privilege}"
        bmc_cfg_set_value ${out_file} "BMC_USER_DEFAULT_USERNAME" $(( $off + 72 )) 64 "${name}"
        bmc_cfg_set_value ${out_file} "BMC_USER_DEFAULT_PASSWORD" $(( $off + 136 )) 128 "${passwd}"
        # Add indx, length and subtype
        to_bytes "${i}" 1 | dd of="${out_file}" seek=$(( $off + 3 )) bs=1 count=1 conv=notrunc 2> /dev/null
        to_bytes "$(( $BMC_CFG_SUBTYPE_USER_DEFAULT_LEN - 3))" 2 | dd of="${out_file}" seek=$(( $off + 1 )) bs=1 count=2 conv=notrunc 2> /dev/null
        to_bytes "${BMC_CFG_SUBTYPE_USER_DEFAULT}" 1 | dd of="${out_file}" seek=$(( $off + 0 )) bs=1 count=1 conv=notrunc 2> /dev/null
        # Increment offset
        off=$(( $off +  $BMC_CFG_SUBTYPE_USER_DEFAULT_LEN))
      fi
    done
  fi

  # Write ntp config to blob
  if [ "$has_cfg_ntp" -eq 1 ]; then
    for i in {0..32}; do
      eval "ntp=\$BMC_NTP_${i}"
      if [ -n "$ntp" ]; then
        bmc_cfg_set_value ${out_file} "BMC_NTP" $(( $off + 8 )) 256 "${ntp}"
        # Add indx, length and subtype
        to_bytes "${i}" 1 | dd of="${out_file}" seek=$(( $off + 7 )) bs=1 count=1 conv=notrunc 2> /dev/null
        to_bytes "$(( $BMC_CFG_SUBTYPE_NTP_LEN - 3))" 2 | dd of="${out_file}" seek=$(( $off + 1 )) bs=1 count=2 conv=notrunc 2> /dev/null
        to_bytes "${BMC_CFG_SUBTYPE_NTP}" 1 | dd of="${out_file}" seek=$(( $off + 0 )) bs=1 count=1 conv=notrunc 2> /dev/null
        # Increment offset
        off=$(( $off +  $BMC_CFG_SUBTYPE_NTP_LEN))
      fi
    done
  fi

  # Push PCP Type and PCP Length for BMC config.
  to_bytes "$(( $cfg_len - $PCP_TYPE_LEN - $PCP_LENGTH_LEN ))" $PCP_LENGTH_LEN | dd of="${out_file}" seek=$(( 0 + $PCP_TYPE_LEN )) bs=1 count=$PCP_LENGTH_LEN conv=notrunc 2> /dev/null
  to_bytes "${PCP_TYPE_BMC_CFG}" $PCP_TYPE_LEN | dd of="${out_file}" seek=0 bs=1 count=$PCP_TYPE_LEN conv=notrunc 2> /dev/null

  return
}

# Refer to this page to obtain info on configuration binary format.
# https://confluence.nvidia.com/pages/viewpage.action?spaceKey=SW&title=BlueField+Platform+Configuration
cfg_push_pch()
{
  local out_file=$1
  local data_len=$2

  #
  # PCH format (length 128 bytes):
  #
  # FP             (off:  0, len:  8 bytes)
  # HdrVersion     (off:  8, len:  4 bytes)
  # HdrLen         (off: 12, len:  4 bytes)
  # HdrType        (off: 16, len:  4 bytes)
  # Reserved       (off: 20, len:  4 bytes)
  # Length         (off: 24, len:  4 bytes)
  # Version        (off: 28, len:  4 bytes)
  # Flags          (off: 32, len:  4 bytes)
  # Owner          (off: 36, len:  1 bytes)
  # Reserved       (off: 37, len:  3 bytes)
  # UUID/OPN       (off: 40, len: 32 bytes)
  # DataPtr        (off: 72, len:  4 bytes)
  # AuthDataPtr    (off: 76, len:  4 bytes)
  # DataVersion    (off: 80, len:  4 bytes)
  # DataTimestamp  (off: 84, len:  4 bytes)
  # Reserved       (off: 88, len: 40 bytes)
  #

  local hdr_off=0
  local length=$data_len
  local version=$PCP_VERSION
  local payload_data_off=$PCH_HEADER_LEN
  local auth_data_off=0

  [ -s "${out_file}" ] && rm -f ${out_file}

  # Create empty header file.
  dd if=/dev/zero of=${out_file} count=${PCH_HEADER_LEN} bs=1 2> /dev/null

  # Populate header
  to_bytes "${PCH_HEADER_VERSION}" 4 | dd of="${out_file}" seek="$(( $hdr_off + 8 ))" bs=1 count=4 conv=notrunc 2> /dev/null
  to_bytes "${PCH_HEADER_LEN}" 4 | dd of="${out_file}" seek="$(( $hdr_off + 12 ))" bs=1 count=4 conv=notrunc 2> /dev/null
  to_bytes "${PCH_HEADER_TYPE}" 4 | dd of="${out_file}" seek="$(( $hdr_off + 16 ))" bs=1 count=4 conv=notrunc 2> /dev/null
  to_bytes "${length}" 4 | dd of="${out_file}" seek="$(( $hdr_off + 24 ))" bs=1 count=4 conv=notrunc 2> /dev/null
  to_bytes "${version}" 4 | dd of="${out_file}" seek="$(( $hdr_off + 28 ))" bs=1 count=4 conv=notrunc 2> /dev/null
  to_bytes "${payload_data_off}" 4 | dd of="${out_file}" seek="$(( $hdr_off + 72 ))" bs=1 count=4 conv=notrunc 2> /dev/null
  to_bytes "${auth_data_off}" 4 | dd of="${out_file}" seek="$(( $hdr_off + 76 ))" bs=1 count=4 conv=notrunc 2> /dev/null

  # Write configuration data version if specified.
  if [ -n "${CONFIG_VERSION}" ]; then
    to_bytes "${CONFIG_VERSION}" 4 | dd of="${out_file}" seek="$(( $hdr_off + 80 ))" bs=1 count=4 conv=notrunc 2> /dev/null
  fi

  # Write configuration data timestamp if specified.
  if [ -n "${CONFIG_DATE}" ]; then
    local timestamp_off=$(( $hdr_off + 84 ))
    # Parse YYYY.MM.DD format and convert to timestamp
    local year=$(echo "$CONFIG_DATE" | cut -d'.' -f1)
    local month=$(echo "$CONFIG_DATE" | cut -d'.' -f2)
    local day=$(echo "$CONFIG_DATE" | cut -d'.' -f3)
    # Push date to the header.
    to_bytes "${year}" 2 | dd of="${out_file}" seek="$(( $timestamp_off + 0 ))" bs=1 count=2 conv=notrunc 2> /dev/null
    to_bytes "${month}" 1 | dd of="${out_file}" seek="$(( $timestamp_off + 2 ))" bs=1 count=1 conv=notrunc 2> /dev/null
    to_bytes "${day}" 1 | dd of="${out_file}" seek="$(( $timestamp_off + 3 ))" bs=1 count=1 conv=notrunc 2> /dev/null
  fi

  # Write finger print
  local fp=$(printf ${PCH_FP} | xxd -p | xxd -r -p | sed -e 's/[0-9A-F]\{2\}/&\\x/g' -e 's/\\x$//')
  printf "${fp}" | dd of="${out_file}" seek="$(( $hdr_off + 0 ))" bs=1 count=8 conv=notrunc 2> /dev/null

  return
}

# Convert input configuration to a binary file.
#
# When 'bfcfg' cannot be executed from Arm operating
# system, an alternative is to run the command on external
# host to convert the config to a binary file. This binary
# file can be used to create a capsule that is appended to
# a BFB file and applied from external host.
cfg2bin()
{
  local out_file=$(basename $1).bin
  local cfg_file=$1

  local mfg_cfg_bin="${TMP_DIR}/.mfg_cfg.cfg"
  local pch_cfg_bin="${TMP_DIR}/.pch_cfg.bin"
  local bmc_cfg_bin="${TMP_DIR}/.bmc_cfg.bin"
  local arm_cfg_bin="${TMP_DIR}/.arm_cfg.bin"
  local pcp_cfg_bin="$mfg_cfg_bin"
  local cfg_len=0

  if ! command -v xxd > /dev/null; then
    echo "error: $0 requires the xxd command, please install it first"
    exit 1
  fi

  # Source the configuration if exists.
  if [ ! -s "${cfg_file}" ]; then
    echo "error: invalid configuration '${cfg_file}'."
    exit 1
  fi

  # Cleanup temp files.
  rm -f $mfg_cfg_bin $pch_cfg_bin $arm_cfg_bin $bmc_cfg_bin
  [ -f "${out_file}" ] && rm -f ${out_file}

  # Reset variables
  mfg_sysfs_cfg_done=0
  has_redfish_cfg=0
  has_sys_cfg=0

  # Source the configuration if exists.
  # shellcheck source=/dev/null
  . ${cfg_file}

  start_log_file

  # Export config to bin
  [ "$skip_mfg" -eq 0 ] && mfg_cfg_to_bin ${mfg_cfg_bin}
  arm_cfg_to_bin ${arm_cfg_bin}
  bmc_cfg_to_bin ${bmc_cfg_bin}
  has_bin=0

  if [ ! -s "${mfg_cfg_bin}" ] \
    && [ ! -s "${arm_cfg_bin}" ] \
    && [ ! -s "${bmc_cfg_bin}" ]; then
      echo "** error: failed to write configuration."
      exit 1
  fi

  # Generate the 4KB MFG binary (legacy), if needed.
  # This aims to conserve backward compatibility with old firmware.
  if [ ! -f "${mfg_cfg_bin}" ]; then
    # Create empty header file.
    dd if=/dev/zero of=${mfg_cfg_bin} count=4096 bs=1 2> /dev/null
  fi

  if [ -f "${arm_cfg_bin}" ] \
    || [ -f "${bmc_cfg_bin}" ] ; then
    # Add PCH header filename to the list.
    pcp_cfg_bin="$pcp_cfg_bin $pch_cfg_bin"

    if [ -f "${arm_cfg_bin}" ]; then
      cfg_len=$(( $cfg_len + $(stat -c%s "$arm_cfg_bin") ))
      pcp_cfg_bin="$pcp_cfg_bin $arm_cfg_bin"
    fi

    if [ -f "${bmc_cfg_bin}" ]; then
      cfg_len=$(( $cfg_len + $(stat -c%s "$bmc_cfg_bin") ))
      pcp_cfg_bin="$pcp_cfg_bin $bmc_cfg_bin"
    fi

    # Push header after the legacy blob
    cfg_push_pch ${pch_cfg_bin} ${cfg_len}
  fi

  # Generate binary file
  cat $pcp_cfg_bin > $out_file

  # Cleanup temp files.
  rm -f $mfg_cfg_bin $pch_cfg_bin $arm_cfg_bin $bmc_cfg_bin

  echo ""
  echo "$0: configuration blob written to ${out_file}"

  return
}

mfg_cfg_get_value()
{
  local out_file=$1
  local name=$2
  local offset=$3
  local size=$4
  local in_file=$5
  local value=0

  if [ -f "${in_file}" ]; then
    if [ "$name" = "MFG_OOB_MAC" ]; then
      value=$(dd if=${in_file} skip=${offset} count=${size} bs=1 2> /dev/null | xxd -p)
      if [ "$value" != "000000000000" ]; then
        mac=$(echo $value | sed -e 's/../&:/g' -e 's/:$//')
        echo "$name=$mac" >> ${out_file}
      fi

    else
      #value=$(tail -c+${offset} ${in_file} | head -c${size})
      value=$(dd if=${in_file} skip=${offset} count=${size} bs=1 2> /dev/null | tr -d '\0')
      if [ "$name" = "MFG_SYS_MFR" ] \
        || [ "$name" = "MFG_SYS_PDN" ] \
        || [ "$name" = "MFG_BSB_MFR" ] \
        || [ "$name" = "MFG_BSB_PDN" ]; then
        if [ -n "$value" ]; then echo "$name=\"$value\"" >> ${out_file}; fi
      else
        if [ -n "$value" ]; then echo "$name=$value" >> ${out_file}; fi
      fi
    fi

  fi

  return
}

# Refer to this page to obtain info on configuration binary format.
# https://confluence.nvidia.com/pages/viewpage.action?spaceKey=SW&title=BlueField+Platform+Configuration
mfg_to_txt()
{
  local in_file=$1
  local off=$2
  local out_file=$3

  [ ! -s "${in_file}" ] && return

  local in_file_len=$(stat -c%s "$in_file")
  [ $in_file_len -lt 4096 ] && return

  mfg_cfg_get_value ${out_file} "MFG_OOB_MAC" "$(( $off + 0 ))" 6 ${in_file}
  mfg_cfg_get_value ${out_file} "MFG_OPN" "$(( $off + 8 ))" 24 ${in_file}
  mfg_cfg_get_value ${out_file} "MFG_SKU" "$(( $off + 32 ))" 24 ${in_file}
  mfg_cfg_get_value ${out_file} "MFG_MODL" "$(( $off + 56 ))" 24 ${in_file}
  mfg_cfg_get_value ${out_file} "MFG_SN" "$(( $off + 80 ))" 24 ${in_file}
  mfg_cfg_get_value ${out_file} "MFG_UUID" "$(( $off + 104 ))" 40 ${in_file}
  mfg_cfg_get_value ${out_file} "MFG_REV" "$(( $off + 144 ))" 8 ${in_file}

  mfg_cfg_get_value ${out_file} "MFG_SYS_MFR" "$(( $off + 256 ))" 24 ${in_file}
  mfg_cfg_get_value ${out_file} "MFG_SYS_PDN" "$(( $off + 280 ))" 24 ${in_file}
  mfg_cfg_get_value ${out_file} "MFG_SYS_VER" "$(( $off + 304 ))" 512 ${in_file}

  mfg_cfg_get_value ${out_file} "MFG_BSB_MFR" "$(( $off + 816 ))" 24 ${in_file}
  mfg_cfg_get_value ${out_file} "MFG_BSB_PDN" "$(( $off + 840 ))" 24 ${in_file}
  mfg_cfg_get_value ${out_file} "MFG_BSB_VER" "$(( $off + 864 ))" 24 ${in_file}
  mfg_cfg_get_value ${out_file} "MFG_BSB_SN" "$(( $off + 888 ))" 24 ${in_file}

  echo "" >> ${out_file}

  return
}

# Refer to this page to obtain info on configuration binary format.
# https://confluence.nvidia.com/pages/viewpage.action?spaceKey=SW&title=BlueField+Platform+Configuration
pch_to_txt()
{
  local in_file=$1
  local off=$2
  local out_file=$3

  [ ! -s "${in_file}" ] && return

  local fp=$(dd if=${in_file} skip=$(( $off + 0 )) count=8 bs=1 2>/dev/null | tr -d '\0')
  if [ "$fp" != "$PCH_FP" ]; then
    echo "** error: cannot find valid config!"
    exit 1
  fi

  # Add comment
  cat <<EOF > ${out_file}
#
# This file is auto-generated by '$0 -b' command line.
#

EOF

  local version=$(to_integer ${in_file} $(( $off + 28 )) 4)
  echo "VERSION=$version" >> ${out_file}
  echo "" >> ${out_file}

  # Read configuration data version if present.
  local data_version=$(to_integer ${in_file} $(( $off + 80 )) 4)
  if [ "$data_version" != "0" ]; then
    echo "CONFIG_VERSION=$data_version" >> ${out_file}
  fi
  # Read configuration data timestamp if present.
  local year=$(to_integer ${in_file} $(( $off + 84 + 0)) 2)
  local month=$(to_integer ${in_file} $(( $off + 84 + 2 )) 1)
  local day=$(to_integer ${in_file} $(( $off + 84 + 3 )) 1)
  local data_timestamp="${year}.${month}.${day}"
  if [ "$year" != "0" ] && [ "$month" != "0" ] && [ "$day" != "0" ]; then
    echo "CONFIG_DATE=$data_timestamp" >> ${out_file}
  fi

  if [ "$data_version" != "0" ] || [ "$data_timestamp" != "0" ]; then
    echo "" >> ${out_file}
  fi

}

# Receives the platform config binary, parse it and dump it to text.
#
# Refer to this page to obtain info on configuration binary format.
# https://confluence.nvidia.com/pages/viewpage.action?spaceKey=SW&title=BlueField+Platform+Configuration
pcp_cfg_to_txt()
{
  local in_file=$1
  local out_file=$2

  local cfg_len=0
  local type=0
  local length=0
  local subtype=0
  local limit=0
  local key_idx=0

  [ ! -s "${out_file}" ] && rm -f ${out_file}
  [ ! -s "${in_file}" ] && return

  in_file_len=$(stat -c%s "$in_file")

  local off=0

  # Skip MFG sections
  off=$(( $off + 4096 ))

  # Print header info
  pch_to_txt ${in_file} $off ${out_file}

  # Print MFG data
  mfg_to_txt ${in_file} 0 ${out_file}

  # Skip header
  off=$(( $off + $PCH_HEADER_LEN ))

  while : ; do
    # Read type
    type=$(to_integer ${in_file} $(( $off + 0 )) $PCP_TYPE_LEN)
    length=$(to_integer ${in_file} $(( $off + $PCP_TYPE_LEN )) $PCP_LENGTH_LEN)

    off=$(( $off + $PCP_TYPE_LEN + $PCP_LENGTH_LEN ))
    limit=$(( $length + $off ))

    if [ $type -eq "$PCP_TYPE_ARM_CFG" ]; then
      while : ; do
        # Read subtype and length
        subtype=$(to_integer ${in_file} $(( $off + 0 )) 1)

        if [ $subtype -eq "$ARM_CFG_SUBTYPE_CTL" ]; then
          arm_cfg_get_value ${out_file} "CTL_RESET_SYS" "$(( $off + 4 ))" 1 ${in_file}
          arm_cfg_get_value ${out_file} "CTL_DELETE_ALL_BOOT" "$(( $off + 5 ))" 1 ${in_file}
          arm_cfg_get_value ${out_file} "CTL_RESET_MISC" "$(( $off + 6 ))" 1 ${in_file}
          arm_cfg_get_value ${out_file} "CTL_DELETE_ALL_UEFI_SECURE_BOOT_KEYS" "$(( $off + 7 ))" 1 ${in_file}
          arm_cfg_get_value ${out_file} "CTL_UEFI_SECURE_BOOT_STATE" "$(( $off + 8 ))" 1 ${in_file}
          arm_cfg_get_value ${out_file} "CTL_DELETE_UEFI_PASSWORD" "$(( $off + 9 ))" 1 ${in_file}
          arm_cfg_get_value ${out_file} "CTL_SAVE_CONFIG_FILE" "$(( $off + 10 ))" 1 ${in_file}
          arm_cfg_get_value ${out_file} "CTL_RESET_LANG" "$(( $off + 11 ))" 1 ${in_file}
          off=$(( $off +  $ARM_CFG_SUBTYPE_CTL_LEN))

        elif [ $subtype -eq "$ARM_CFG_SUBTYPE_SYS" ]; then
          arm_cfg_get_value ${out_file} "SYS_ENABLE_SMMU" "$(( $off + 24 ))" 1 ${in_file}
          arm_cfg_get_value ${out_file} "SYS_DISABLE_SPMI" "$(( $off + 25 ))" 1 ${in_file}
          arm_cfg_get_value ${out_file} "SYS_ENABLE_2ND_EMMC" "$(( $off + 26 ))" 1 ${in_file}
          arm_cfg_get_value ${out_file} "SYS_BOOT_PROTECT" "$(( $off + 27 ))" 1 ${in_file}
          arm_cfg_get_value ${out_file} "SYS_ENABLE_SPCR" "$(( $off + 32 ))" 1 ${in_file}
          arm_cfg_get_value ${out_file} "SYS_DISABLE_PCIE" "$(( $off + 33 ))" 1 ${in_file}
          arm_cfg_get_value ${out_file} "SYS_ENABLE_OPTEE" "$(( $off + 34 ))" 1 ${in_file}
          arm_cfg_get_value ${out_file} "SYS_DISABLE_TMFF" "$(( $off + 35 ))" 1 ${in_file}
          arm_cfg_get_value ${out_file} "SYS_ENABLE_I2C0" "$(( $off + 36 ))" 1 ${in_file}
          arm_cfg_get_value ${out_file} "SYS_DISABLE_FORCE_PXE_RETRY" "$(( $off + 37 ))" 1 ${in_file}
          arm_cfg_get_value ${out_file} "SYS_ENABLE_BMC_FIELD_MODE" "$(( $off + 38 ))" 1 ${in_file}
          arm_cfg_get_value ${out_file} "SYS_CE_THRESHOLD" "$(( $off + 45 ))" 4 ${in_file}
          arm_cfg_get_value ${out_file} "SYS_DISABLE_HEST" "$(( $off + 49 ))" 1 ${in_file}
          arm_cfg_get_value ${out_file} "SYS_L3_CACHE_PARTITION_LEVEL" "$(( $off + 50 ))" 1 ${in_file}
          arm_cfg_get_value ${out_file} "SYS_LLC_READ_ALLOC" "$(( $off + 51 ))" 1 ${in_file}
          arm_cfg_get_value ${out_file} "SYS_ENABLE_I2C3" "$(( $off + 52 ))" 1 ${in_file}
          arm_cfg_get_value ${out_file} "SYS_ENABLE_FORCE_BOOT_RETRY" "$(( $off + 53 ))" 1 ${in_file}
          arm_cfg_get_value ${out_file} "SYS_ENABLE_OEM_MFG_CONFIG" "$(( $off + 54 ))" 1 ${in_file}
          arm_cfg_get_value ${out_file} "SYS_DISABLE_I2C1" "$(( $off + 55 ))" 1 ${in_file}
          arm_cfg_get_value ${out_file} "SYS_ENABLE_BMC_WAIT" "$(( $off + 59 ))" 1 ${in_file}
          arm_cfg_get_value ${out_file} "SYS_DISABLE_CONFIG_AUTO_REBOOT" "$(( $off + 60 ))" 1 ${in_file}
          arm_cfg_get_value ${out_file} "SYS_DISABLE_AUTO_BOOT_REFRESH" "$(( $off + 66 ))" 1 ${in_file}
          arm_cfg_get_value ${out_file} "SYS_DISPLAY_BMC_NET_CONFIG" "$(( $off + 67 ))" 1 ${in_file}
          arm_cfg_get_value ${out_file} "SYS_ENABLE_REDFISH" "$(( $off + 68 ))" 1 ${in_file}
          arm_cfg_get_value ${out_file} "SYS_RTCSYNC" "$(( $off + 69 ))" 1 ${in_file}
          arm_cfg_get_value ${out_file} "MFG_PLAT_MODE" "$(( $off + 74 ))" 1 ${in_file}
          off=$(( $off +  $ARM_CFG_SUBTYPE_SYS_LEN))

        elif [ $subtype -eq "$ARM_CFG_SUBTYPE_BOOT" ]; then
          idx=$(to_integer ${in_file} $(( $off + 3 )) 1)
          arm_cfg_get_value ${out_file} "BOOT${idx}" "$(( $off + 4 ))" 60 ${in_file}
          arm_cfg_get_value ${out_file} "BOOT${idx}_DESC" "$(( $off + 64 ))" 64 ${in_file}
          arm_cfg_get_value ${out_file} "BOOT${idx}_DEVPATH" "$(( $off + 128 ))" 256 ${in_file}
          arm_cfg_get_value ${out_file} "BOOT${idx}_ARGS" "$(( $off + 384 ))" 256 ${in_file}
          off=$(( $off +  $ARM_CFG_SUBTYPE_BOOT_LEN))

        elif [ $subtype -eq "$ARM_CFG_SUBTYPE_MISC" ]; then
          arm_cfg_get_value ${out_file} "NET_RSHIM_MAC" "$(( $off + 4 ))" 6 ${in_file}
          arm_cfg_get_value ${out_file} "NET_DHCP_IPV6_DUID" $(( $off + 10 )) 1 "${in_file}"
          arm_cfg_get_value ${out_file} "FACTORY_DEFAULT_DHCP_BEHAVIOR" "$(( $off + 16 ))" 1 ${in_file}
          arm_cfg_get_value ${out_file} "PXE_DHCP_CLASS_ID" "$(( $off + 17 ))" 63 ${in_file}
          arm_cfg_get_value ${out_file} "BF_BUNDLE_VERSION" "$(( $off + 80 ))" 128 ${in_file}
          off=$(( $off +  $ARM_CFG_SUBTYPE_MISC_LEN))

        elif [ $subtype -eq "$ARM_CFG_SUBTYPE_UEFI_SB" ]; then
          arm_cfg_get_value ${out_file} "UEFI_SECUREBOOT_KEY${key_idx}" "$(( $off + 3 ))" $(( $ARM_CFG_SUBTYPE_UEFI_SB_LEN - 3 )) ${in_file}
          off=$(( $off +  $ARM_CFG_SUBTYPE_UEFI_SB_LEN))
          key_idx=$(( $key_idx + 1 ))

        fi

        [[ $off -ge $limit ]] && break
      done

    elif [ $type -eq "$PCP_TYPE_BMC_CFG" ]; then
      while : ; do
        # Read subtype and length
        subtype=$(to_integer ${in_file} $(( $off + 0 )) 1)

        if [ $subtype -eq "$BMC_CFG_SUBTYPE_MISC" ]; then
          bmc_cfg_get_value ${out_file} "BMC_EXPIRE_PASSWORD_ON_FIRST_LOGIN" "$(( $off + 3 ))" 1 ${in_file}
          bmc_cfg_get_value ${out_file} "BMC_PASSWORD_MINIMUM_LENGTH" "$(( $off + 4 ))" 2 ${in_file}
          bmc_cfg_get_value ${out_file} "BMC_LOCK_ACCOUNT_MAX_RETRY" "$(( $off + 6 ))" 2 ${in_file}
          bmc_cfg_get_value ${out_file} "BMC_SOL_ENABLE" "$(( $off + 8 ))" 1 ${in_file}
          bmc_cfg_get_value ${out_file} "BMC_SOL_CHARACTER_SEND_THRESHOLD" "$(( $off + 9 ))" 2 ${in_file}
          bmc_cfg_get_value ${out_file} "BMC_SOL_CHARACTER_ACCUMULATE_LEVEL" "$(( $off + 11 ))" 2 ${in_file}
          bmc_cfg_get_value ${out_file} "BMC_DISABLE_ROOT" "$(( $off + 13 ))" 1 ${in_file}
          bmc_cfg_get_value ${out_file} "BMC_SERIAL_REDIRECT_ENABLE" "$(( $off + 14 ))" 1 ${in_file}
          bmc_cfg_get_value ${out_file} "BMC_WEBUI_ENABLE" "$(( $off + 16 ))" 1 ${in_file}
          bmc_cfg_get_value ${out_file} "BMC_SEED" "$(( $off + 24 ))" 64 ${in_file}
          bmc_cfg_get_value ${out_file} "BMC_IPMI_ACCOUNTS" "$(( $off + 88 ))" 512 ${in_file}
          off=$(( $off + $BMC_CFG_SUBTYPE_MISC_LEN))

        elif [ $subtype -eq "$BMC_CFG_SUBTYPE_USER_DEFAULT" ]; then
          idx=$(to_integer ${in_file} $(( $off + 3 )) 1)
          bmc_cfg_get_value ${out_file} "BMC_USER_DEFAULT_PRIVILEGE_${idx}" "$(( $off + 8 ))" 64 ${in_file}
          bmc_cfg_get_value ${out_file} "BMC_USER_DEFAULT_USERNAME_${idx}" "$(( $off + 72 ))" 64 ${in_file}
          bmc_cfg_get_value ${out_file} "BMC_USER_DEFAULT_PASSWORD_${idx}" "$(( $off + 136 ))" 128 ${in_file}
          off=$(( $off +  $BMC_CFG_SUBTYPE_USER_DEFAULT_LEN))

        elif [ $subtype -eq "$BMC_CFG_SUBTYPE_NTP" ]; then
          idx=$(to_integer ${in_file} $(( $off + 7 )) 1)
          bmc_cfg_get_value ${out_file} "BMC_NTP_${idx}" "$(( $off + 8 ))" 256 ${in_file}
          off=$(( $off + $BMC_CFG_SUBTYPE_NTP_LEN))

        fi

        [[ $off -ge $limit ]] && break
      done

    else
        # Type nt supported, thus skip!
        off=$(( $off + $length ))

    fi

    [[ $off -ge $in_file_len ]] && break
  done

  return
}

#
# Fixed parameters for BFB and capsule files.
# MUST be consistent with the definition of BFB header
# format and UEFI capsule header format.
#
# The BFB header magic number "BF^B^S" (0x13026642)
BFB_MAGIC="42660213"
# The EFI Capsule GUID
CAP_GUID="edd5cb6d2de8444cbda17194199ad92a"
CAP_CERT_CONST="4d535331100000000100000001000000"

# This function implements a very baic parsing of the capsule file.
parse_cap()
{
  incap=$1
  outbin=$2

  # Check UEFI capsule GUID
  local capguid=$(dd if=${incap} skip=0 count=16 bs=1 2> /dev/null | xxd -p)
  [ "$capguid" != "$CAP_GUID" ] && return

  log_msg "bin2cfg: CAP file detected."

  echo "Parsing EFI Capsule $incap..."

  local incap_len=$(stat -c%s "$incap")
  local off=0

  #
  # This routine assumes the capsule contain a single payload
  # item. Also the capsule doesn't embed any driver.
  #

  # The UEFI Capsule header is 8-byte aligned, thus extra padding
  # bumps up its size to 32 bytes instead of 28 bytes.
  local uefi_cap_hdr_len=32
  # typedef struct {
  #   ///
  #   /// A GUID that defines the contents of a capsule.
  #   ///
  #   EFI_GUID          CapsuleGuid;
  #   ///
  #   /// The size of the capsule header. This may be larger than the size of
  #   /// the EFI_CAPSULE_HEADER since CapsuleGuid may imply
  #   /// extended header entries
  #   ///
  #   UINT32            HeaderSize;
  #   ///
  #   /// Bit-mapped list describing the capsule attributes. The Flag values
  #   /// of 0x0000 - 0xFFFF are defined by CapsuleGuid. Flag values
  #   /// of 0x10000 - 0xFFFFFFFF are defined by this specification
  #   ///
  #   UINT32            Flags;
  #   ///
  #   /// Size in bytes of the capsule.
  #   ///
  #   UINT32            CapsuleImageSize;
  # } EFI_CAPSULE_HEADER;
  local capfile_len=$(to_integer ${incap} $(( $off + 24 )) 4)
  # Quick check on the capsule length.
  if [ $capfile_len -ne $incap_len ]; then
    echo "** error: invalid capsule file size"
    exit 1
  fi

  off=$(( $off + $uefi_cap_hdr_len ))

  local fmp_cap_hdr_len=16
  # typedef struct {
  #   UINT32 Version;
  #
  #   ///
  #   /// The number of drivers included in the capsule and the number of corresponding
  #   /// offsets stored in ItemOffsetList array.
  #   ///
  #   UINT16 EmbeddedDriverCount;
  #
  #   ///
  #   /// The number of payload items included in the capsule and the number of
  #   /// corresponding offsets stored in the ItemOffsetList array.
  #   ///
  #   UINT16 PayloadItemCount;
  #
  #   ///
  #   /// Variable length array of dimension [EmbeddedDriverCount + PayloadItemCount]
  #   /// containing offsets of each of the drivers and payload items contained within the capsule
  #   ///
  #   // UINT64 ItemOffsetList[];
  # } EFI_FIRMWARE_MANAGEMENT_CAPSULE_HEADER;
  off=$(( $off + $fmp_cap_hdr_len ))

  local fmp_cap_img_hdr_v2_len=40
  local fmp_cap_img_hdr_v3_len=48
  # typedef struct {
  #   UINT32   Version;
  #
  #   ///
  #   /// Used to identify device firmware targeted by this update. This guid is matched by
  #   /// system firmware against ImageTypeId field within a EFI_FIRMWARE_IMAGE_DESCRIPTOR
  #   ///
  #   EFI_GUID UpdateImageTypeId;
  #
  #   ///
  #   /// Passed as ImageIndex in call to EFI_FIRMWARE_MANAGEMENT_PROTOCOL.SetImage ()
  #   ///
  #   UINT8    UpdateImageIndex;
  #   UINT8    reserved_bytes[3];
  #
  #   ///
  #   /// Size of the binary update image which immediately follows this structure
  #   ///
  #   UINT32   UpdateImageSize;
  #
  #   ///
  #   /// Size of the VendorCode bytes which optionally immediately follow binary update image in the capsule
  #   ///
  #   UINT32   UpdateVendorCodeSize;
  #
  #   ///
  #   /// The HardwareInstance to target with this update. If value is zero it means match all
  #   /// HardwareInstances. This field allows update software to target only a single device in
  #   /// cases where there are more than one device with the same ImageTypeId GUID.
  #   /// This header is outside the signed data of the Authentication Info structure and
  #   /// therefore can be modified without changing the Auth data.
  #   ///
  #   UINT64   UpdateHardwareInstance;
  #
  #   ///
  #   /// Bits which indicate authentication and depex information for the image that follows this structure.
  #   /// It is used if EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER_INIT_VERSION is 0x00000003.
  #   ///
  #   UINT64   ImageCapsuleSupport
  # } EFI_FIRMWARE_MANAGEMENT_CAPSULE_IMAGE_HEADER;
  local fmp_ver=$(to_integer ${incap} $(( $off + 0 )) 4)
  if [ $fmp_ver -lt 3 ]; then
    off=$(( $off + $fmp_cap_img_hdr_v2_len ))
  else
    off=$(( $off + $fmp_cap_img_hdr_v3_len ))
  fi

  local fmp_auth_hdr_len=32
  # ///
  # /// Image Attribute -Authentication Required
  # ///
  # typedef struct {
  #   ///
  #   /// It is included in the signature of AuthInfo. It is used to ensure freshness/no replay.
  #   /// It is incremented during each firmware image operation.
  #   ///
  #   UINT64                                  MonotonicCount;
  #   ///
  #   /// Provides the authorization for the firmware image operations. It is a signature across
  #   /// the image data and the Monotonic Count value. Caller uses the private key that is
  #   /// associated with a public key that has been provisioned via the key exchange.
  #   /// Because this is defined as a signature, WIN_CERTIFICATE_UEFI_GUID.CertType must
  #   /// be EFI_CERT_TYPE_PKCS7_GUID.
  #   ///
  #   WIN_CERTIFICATE_UEFI_GUID               AuthInfo;
  # } EFI_FIRMWARE_IMAGE_AUTHENTICATION;
  #
  # ///
  # /// Certificate which encapsulates a GUID-specific digital signature
  # ///
  # typedef struct {
  #   ///
  #   /// This is the standard WIN_CERTIFICATE header, where
  #   /// wCertificateType is set to WIN_CERT_TYPE_EFI_GUID.
  #   ///
  #   WIN_CERTIFICATE   Hdr;
  #   ///
  #   /// This is the unique id which determines the
  #   /// format of the CertData. .
  #   ///
  #   EFI_GUID          CertType;
  #   ///
  #   /// The following is the certificate data. The format of
  #   /// the data is determined by the CertType.
  #   /// If CertType is EFI_CERT_TYPE_RSA2048_SHA256_GUID,
  #   /// the CertData will be EFI_CERT_BLOCK_RSA_2048_SHA256 structure.
  #   ///
  #   UINT8            CertData[1];
  # } WIN_CERTIFICATE_UEFI_GUID;
  #
  # ///
  # /// The WIN_CERTIFICATE structure is part of the PE/COFF specification.
  # ///
  # typedef struct {
  #   ///
  #   /// The length of the entire certificate,
  #   /// including the length of the header, in bytes.
  #   ///
  #   UINT32  dwLength;
  #   ///
  #   /// The revision level of the WIN_CERTIFICATE
  #   /// structure. The current revision level is 0x0200.
  #   ///
  #   UINT16  wRevision;
  #   ///
  #   /// The certificate type. See WIN_CERT_TYPE_xxx for the UEFI
  #   /// certificate types. The UEFI specification reserves the range of
  #   /// certificate type values from 0x0EF0 to 0x0EFF.
  #   ///
  #   UINT16  wCertificateType;
  #   ///
  #   /// The following is the actual certificate. The format of
  #   /// the certificate depends on wCertificateType.
  #   ///
  #   /// UINT8 bCertificate[ANYSIZE_ARRAY];
  #   ///
  # } WIN_CERTIFICATE;
  #
  # #define WIN_CERT_TYPE_EFI_GUID         0x0EF1
  #
  # ///
  # /// This identifies a signature containing a DER-encoded PKCS #7 version 1.5 [RFC2315]
  # /// SignedData value.
  # ///
  # #define EFI_CERT_TYPE_PKCS7_GUID \
  #   { \
  #     0x4aafd29d, 0x68df, 0x49ee, {0x8a, 0xa9, 0x34, 0x7d, 0x37, 0x56, 0x65, 0xa7} \
  #   }
  off=$(( $off + $fmp_auth_hdr_len ))

  # Constant data 'MSS1*' is appended to the authentication data.
  local certdata=$(dd if=${incap} skip=$(( $off + 0 )) count=16 bs=1 2> /dev/null | xxd -p)
  if [ "$certdata" != "$CAP_CERT_CONST" ]; then
    # There must be a certificate, thus skip it.
    # The certificate is expected in DER format.
    # The certificate size is encoded over two bytes following the sequence
    # byte (0x30) and the first byte of the DER size (0x82 XX XX).
    local cert_len=$(dd if=${incap} skip=$(( $off + 2 )) count=2 bs=1 2> /dev/null | xxd -p)
    cert_len=$(echo $(( 16#$cert_len )))
    if [ $cert_len -gt $capfile_len ]; then
      echo "** error: invalid capsule auth data"
      exit 1
    fi
    off=$(( $off + 4 + $cert_len ))
  fi

  certdata=$(dd if=${incap} skip=$(( $off + 0 )) count=16 bs=1 2> /dev/null | xxd -p)
  if [ "$certdata" != "$CAP_CERT_CONST" ]; then
    echo "** error: invalid capsule header"
    exit 1
  fi

  # skip certdata
  off=$(( $off + 16 ))

  local payload_hdr_len=32
  # ///
  # /// Payload Header (32 bytes)
  # ///
  # typedef struct {
  #   UINT32   Type;             // Image Type ID
  #   UINT32   Length;           // Payload Length
  #   UINT8    PrivateData[24];  // PrivateData
  # };
  #
  off=$(( $off + 32 ))

  # Extract capsule payload.
  dd if=${incap} of=${outbin} bs=1 count=$(( $capfile_len - $off )) skip=$off 2> /dev/null

  return
}

parse_bfb()
{
  local inbfb=$1
  local outbin=$2
  local tmp_cap_file="${TMP_DIR}/.bf.cfg.cap.tmp"

  local bfb_hdr_len=24
  #
  # The Capsule file parameters within the BFB header.
  #
  # Fields (swapped)
  #  Minor             : 2 (4 bits)
  #  Major             : 1 (4 bits)
  #  Next image version: X (4 bits), where X in {0, 1, 2}
  #  Reserved          : 0 (4 bits)
  #  Header length     : 3 (4 bits)
  #  Curr image version: 0 (4 bits)
  #  Image Identifier  : 52 (8 bits)
  #
  # Only a single capsule file is expected within the
  # BFB file and thus the Next image version should be
  # X=0.
  #
  local image_id_0=21003034
  local image_id_1=21103034
  local image_id_2=21203034

  # Check magic number
  local magic=$(dd if=${inbfb} skip=0 count=4 bs=1 2>/dev/null | xxd -p)
  [ "$magic" != "$BFB_MAGIC" ] && return

  log_msg "bin2cfg: BFB file detected."

  echo "Parsing BFB $inbfb..."

  local inbfb_len=$(stat -c%s "$inbfb")
  local off=0

  while : ; do

    [[ $off -ge $inbfb_len ]] && break;

    magic=$(dd if=${inbfb} skip=$(( $off + 0 )) count=4 bs=1 2>/dev/null | xxd -p)
    local id=$(dd if=${inbfb} skip=$(( $off + 4 )) count=4 bs=1 2>/dev/null | xxd -p)

    # Read length and reverse byte order to obtain the length in hex.
    # The 'sed' command can be replaced with commands such as
    # 'tac' or 'nl | sort -nr | cut -f 2-'.
    local len=$(to_integer ${inbfb} $(( $off + 8 )) 4)
    # Pad the length if needed.
    # Round-up to next greater multiple of 8
    local len_pad=$(( ($len + 7) & (-8) ))
    # Skip BFB header
    off=$(( $off +  $bfb_hdr_len ))

    #
    # Check if the magic number was previously found
    # and the next word matches the capsule image
    # identifier.
    #

    if [ "$magic" != "$BFB_MAGIC" ]; then
      off=$(( $off + $len_pad ))
      continue
    fi

    if [ "$id" != "$image_id_0" ] \
        && [ "$id" != "$image_id_1" ] \
        && [ "$id" != "$image_id_2" ]; then
      off=$(( $off + $len_pad ))
      continue
    fi

    # If we reach here, it means that the capsule file is found.
    # Extract it and parse it.
    dd if=${inbfb} of=${tmp_cap_file} bs=1 count=$len skip=$off 2> /dev/null
    parse_cap ${tmp_cap_file} ${outbin}
    # Cleanup temp file
    rm -f "${tmp_cap_file}"
    break

  done

  return
}

# IMPORTANT NOTE: This function can be executed from OpenBMC,
# thus it is important to keep commands and subroutines portable
# to various shell/bash environment.
bin2cfg()
{
  local out_file=$(basename $1).cfg
  local bin_file=$1
  local tmp_cfg_file="${TMP_DIR}/.bf.cfg.tmp"
  local tmp_bin_file="${TMP_DIR}/.bf.bin.tmp"

  if ! command -v xxd > /dev/null; then
    echo "error: $0 requires the xxd command, please install it first"
    exit 1
  fi

  # Source the configuration if exists.
  if [ ! -s "${bin_file}" ]; then
    echo "error: invalid binary '${bin_file}'."
    exit 1
  fi

  # Cleanup temp file
  [ -f "${tmp_cfg_file}" ] && rm -f "${tmp_cfg_file}"
  [ -f "${tmp_bin_file}" ] && rm -f "${tmp_bin_file}"

  #
  # Check binary - supported binaries are BFB, CAP and CFG.
  #
  parse_bfb ${bin_file} ${tmp_bin_file}
  if [ ! -s "${tmp_bin_file}" ]; then
    parse_cap ${bin_file} ${tmp_bin_file}
  fi

  if [ -s "${tmp_bin_file}" ]; then
    pcp_cfg_to_txt ${tmp_bin_file} ${tmp_cfg_file}
  else
    pcp_cfg_to_txt ${bin_file} ${tmp_cfg_file}
  fi

  if [ -s "${tmp_cfg_file}" ]; then
    mv ${tmp_cfg_file} ${out_file}
    rm -f ${tmp_cfg_file} ${tmp_bin_file}
    echo ""
    echo "$0: configuration written to ${out_file}"
  else
    echo "** error: failed to read configuration."
    exit 1
  fi

  return
}

start_log_file()
{
  rm -f ${log_file} >/dev/null
  log_msg "bfcfg (ver ${bfcfg_version})"
  log_msg "$(date)"
  log_msg
}

get_osarg_len() {
  local i=0
  local len=0
  local bits=8
  local osarg_len_offset=45
  local osarg_len_byte_size=4

  # Length of OsArgs is present in the ACPI table at a fixed offset.
  # Length information is UINT32 and hence spread over 4-bytes.
  # Read each byte and shift by 8X times and add them together.

  tail -c+$osarg_len_offset ${bfcf_acpi_table} | hexdump -v -e '/1 "%u\n"' | while read Char; do
    shiftval=$(expr $bits \* $i)
    newval=$(($Char<<$shiftval))
    len=$(expr $len + $newval)
    i=$(expr $i + 1)
    if [ $i -eq $osarg_len_byte_size ]; then
      echo $len
      break
    fi
  done
}

print_osarg()
{
  if [ ! -e ${bfcf_acpi_table} ]; then
    return
  fi

  local osarg_len=$(get_osarg_len)
  #
  # ACPI table headers are fixed, so OsArgs always starts from
  # a fixed offset.
  #
  local osarg_offset=49

  if [[ ${osarg_len} -ne 0 ]]; then
    tail -c+$osarg_offset ${bfcf_acpi_table} | head -c$osarg_len | hexdump -v -e '/1 "%c\n"' | while read Char; do
      printf "%c" $Char
    done
    printf "\n"
  fi
}

#
# Pass some OS info to UEFI variable.
#
set_osinfo()
{
  local log_buf log_buf_len

  log_buf=0x`cat /proc/kallsyms | grep -w log_buf | awk '{print $1}'`
  [ "$log_buf" == "0x" ] && return
  log_buf=$(printf '%016x' "${log_buf}")
  log_buf_len=0x`cat /proc/kallsyms | grep -w log_buf_len | awk '{print $1}'`
  [ "$log_buf_len" == "0x" ] && return
  log_buf_len=$(printf '%016x' "${log_buf_len}")

  printf "\\x06\\x00\\x00\\x00" > ${TMP_DIR}/os_info
  printf "\\x${log_buf:14:2}\\x${log_buf:12:2}" >> ${TMP_DIR}/os_info
  printf "\\x${log_buf:10:2}\\x${log_buf:8:2}" >> ${TMP_DIR}/os_info
  printf "\\x${log_buf:6:2}\\x${log_buf:4:2}" >> ${TMP_DIR}/os_info
  printf "\\x${log_buf:2:2}\\x${log_buf:0:2}" >> ${TMP_DIR}/os_info
  printf "\\x${log_buf_len:14:2}\\x${log_buf_len:12:2}" >> ${TMP_DIR}/os_info
  printf "\\x${log_buf_len:10:2}\\x${log_buf_len:8:2}" >> ${TMP_DIR}/os_info
  printf "\\x${log_buf_len:6:2}\\x${log_buf_len:4:2}" >> ${TMP_DIR}/os_info
  printf "\\x${log_buf_len:2:2}\\x${log_buf_len:0:2}" >> ${TMP_DIR}/os_info

  cp ${TMP_DIR}/os_info "${osinfo_sysfs}" 2>/dev/null
}

#
# Pass version info from bf-info command to UEFI variable.
#
set_doca_info()
{
  local doca_info

  # Check if bf-info command exists
  if ! command -v bf-info >/dev/null 2>&1; then
    # Since bf-info is part of bf-release package,
    # it is not critical to fail if the command is not found.
    echo "warning: bf-info command not found"
    return
  fi

  # Get DOCA version info from bf-info command in JSON format.
  doca_info=$(bf-info --json 2>/dev/null)
  if [ -z "${doca_info}" ]; then
    echo "error: failed to get version info from bf-info command"
    exit 1
  fi

  # Create the EFI variable data with header
  printf "\\x06\\x00\\x00\\x00" > ${TMP_DIR}/doca_info
  echo -n "${doca_info}" >> ${TMP_DIR}/doca_info

  # Write to EFI variable
  cp ${TMP_DIR}/doca_info "${doca_info_sysfs}" 2>/dev/null
}

#
# Pass the ATF version from capsule to UEFI variable.
#
set_cap_atf_version()
{
  printf "\\x06\\x00\\x00\\x00" > ${TMP_DIR}/cap_atf_version
  echo -n "$1" >> ${TMP_DIR}/cap_atf_version

  cp ${TMP_DIR}/cap_atf_version "${cap_atf_version_sysfs}" 2>/dev/null
}

#
# Pass the UEFI version from capsule to UEFI variable.
#
set_cap_uefi_version()
{
  printf "\\x06\\x00\\x00\\x00" > ${TMP_DIR}/cap_uefi_version
  echo -n "$1" >> ${TMP_DIR}/cap_uefi_version

  cp ${TMP_DIR}/cap_uefi_version "${cap_uefi_version_sysfs}" 2>/dev/null
}

usage()
{
  echo "syntax: bfcfg [--help|-h] [--version|-v]"
  echo "              [--bin2cfg|-b <bin_file> [--skip-mfg]]"
  echo "              [--cfg2bin|-c <cfg_file>]"
  echo "              [--dump|-d] [--dump-level|-l <${DUMP_LEVEL_NONE}-${DUMP_LEVEL_MAX}>]"
  echo "              [--dump-osarg|-o]"
  echo "              [--cfg|-f <cfg_file>]"
  echo "              [--hscv|-s]"
  echo "              [--osinfo|-i]"
  echo "              [--docainfo]"
  echo "              [--capatfver <pending_version>]"
  echo "              [--capuefiver <pending_version>]"
  echo "              [--part-info|-p]"
}

bf_cfg()
{
  dump_mode=0
  has_cfg2bin=0
  has_bin2cfg=0
  infile=
  skip_mfg=0

  # Parse the arguments.
  options=$(getopt -n bfcfg -o dil:ohvpc:b:f:s -l dump,dump-level:,dump-osarg,help,part-info,osinfo,docainfo,cfg2bin:,bin2cfg:cfg:,skip-mfg,version,hscv,capatfver,capuefiver -- "$@")
  eval set -- "$options"
  while [ "$1" != -- ]; do
    case $1 in
      --dump|-d) dump_mode=1;;
      --dump-level|-l) dump_level=$2 ;;
      --dump-osarg|-o) print_osarg; exit 0;;
      --help|-h) usage; exit 0 ;;
      --osinfo|-i) set_osinfo; exit 0 ;;
      --docainfo) set_doca_info; exit 0 ;;
      --part-info|-p) part_info; exit 0 ;;
      --skip-mfg) skip_mfg=1 ;;
      --cfg2bin|-c) has_cfg2bin=1; infile=$2 ;;
      --bin2cfg|-b) has_bin2cfg=1; infile=$2 ;;
      --cfg|-f) cfg_file=$1 ;;
      --capatfver) set_cap_atf_version $3; exit 0 ;;
      --capuefiver) set_cap_uefi_version $3; exit 0 ;;
      --version|-v) echo "$0 version '$bfcfg_version'"; exit 0 ;;
      --hscv|-s) echo "$0 highest supported config version: $PCP_VERSION"; exit 0 ;;
    esac
    shift
  done
  shift

  # Ensure dump_level is set correctly based on input dump_mode and dump_level.
  if [ ${dump_mode} -eq 0 ]; then
    dump_level=${DUMP_LEVEL_NONE}
  elif [ -z "${dump_level}" ]; then
    dump_level=${DUMP_LEVEL_DEFAULT}
  elif ! [[ "${dump_level}" =~ ^[${DUMP_LEVEL_NONE}-${DUMP_LEVEL_MAX}]$ ]]; then
    echo "Invalid dump level." 2>>${log_file}
    exit 0
  fi

  if [ "$has_cfg2bin" -eq 1 ]; then
    cfg2bin $infile
    exit 0
  elif [ "$has_bin2cfg" -eq 1 ]; then
    bin2cfg $infile
    exit 0
  fi

  # Source the configuration if exists.
  # shellcheck source=/dev/null
  [ -e "${cfg_file}" ] && . ${cfg_file}

  # Start a new log file.
  start_log_file

  # Mount the efi variables.
  test "$(ls -A ${efivars})" || mount -t efivarfs none ${efivars}

  icm_cfg
  if [ ${dump_level} -gt ${DUMP_LEVEL_NONE} ]; then
    # Backward compatibility; dump mfg_cfg prior
    # to sys_cfg and sys_redfish_cfg
    mfg_cfg
    sys_cfg
    sys_redfish_cfg
  else
    # It is important to run this configuration
    # in this specific order, because sys_cfg data
    # and sys_redfish_cfg data might be appended
    # to the capsule file created to program mfg_cfg
    sys_cfg
    sys_redfish_cfg
    mfg_cfg
  fi
  misc_cfg
  boot_cfg
  sb_cfg
}

bf4_usage()
{
  if [ "$bf_chip" = "BlueField-4" ]; then
    echo "syntax: bfcfg [--help|-h] [--version|-v]"
    echo "              [--dump|-d] [--dump-level|-l <${DUMP_LEVEL_NONE}-${DUMP_LEVEL_MAX}>]"
  fi
}

bf4_mfg_sysfs_cfg()
{
  local ifname mac signature

  ifname=$(ls -d /sys/class/net/*/device/driver/module/drivers/pci:lan743x | cut -d '/' -f 5)
  if [ -z "${ifname}" ]; then
    log_msg "mfg: oob interface not found"
    return
  fi

  if [ ${dump_level} -gt ${DUMP_LEVEL_NONE} ]; then
    # Check valid signature 'aa'.
    mac=$(ethtool -e ${ifname} | grep 0x0000 | awk '{print $2 ":" $3 ":" $4 ":" $5 ":" $6 ":" $7 ":" $8}')
    if [ ."${mac:0:2}" = ."aa" ]; then
      mac="${mac:3}"
    else
      mac=""
    fi
    echo "MFG_OOB_MAC=${mac}"
    return
  fi

  [ -z "${MFG_OOB_MAC}" ] && return

  # Clear the valid signature is zero mac is specified.
  if [ "${MFG_OOB_MAC}" = "00:00:00:00:00:00" ]; then
    signature="ff"
    MFG_OOB_MAC="ff:ff:ff:ff:ff:ff"
  else
    signature="aa"
  fi

  IFS=":" read -r -a BYTES <<< "${MFG_OOB_MAC}"
  printf "\x${signature}\x${BYTES[0]}\x${BYTES[1]}\x${BYTES[2]}\x${BYTES[3]}\x${BYTES[4]}\x${BYTES[5]}" > ${TMP_DIR}/oobmac
  ethtool -E ${ifname} magic 0x74A5 offset 0 length 7 < ${TMP_DIR}/oobmac
}

#
# Only support mfg_sysfs_cfg() for now.
#
bf4_cfg()
{
  dump_mode=0

  # Parse the arguments.
  options=$(getopt -n bfcfgl: -o dhvl -l dump,dump-level:,help,version -- "$@")
  eval set -- "$options"
  while [ "$1" != -- ]; do
    case $1 in
      --dump|-d) dump_mode=1;;
      --dump-level|-l) dump_level=$2 ;;
      --help|-h) bf4_usage; exit 0 ;;
      --version|-v) echo "$0 version '$bfcfg_version'"; exit 0 ;;
    esac
    shift
  done
  shift

  # Ensure dump_level is set correctly based on input dump_mode and dump_level.
  if [ ${dump_mode} -eq 0 ]; then
    dump_level=${DUMP_LEVEL_NONE}
  elif [ -z "${dump_level}" ]; then
    dump_level=${DUMP_LEVEL_DEFAULT}
  elif ! [[ "${dump_level}" =~ ^[${DUMP_LEVEL_NONE}-${DUMP_LEVEL_MAX}]$ ]]; then
    echo "Invalid dump level." 2>>${log_file}
    exit 0
  fi

  # Source the configuration if exists.
  # shellcheck source=/dev/null
  [ -e "${cfg_file}" ] && . ${cfg_file}

  bf4_mfg_sysfs_cfg
}

bf_chip=$(bffamily --chip)

if [ "$bf_chip" = "BlueField-4" ]; then
  bf4_cfg $*
else
  bf_cfg $*
fi

sync
exit $bfcfg_rc
