#!/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

bfcfg_version=0.3
cfg_file=/etc/bf.cfg
log_file=/tmp/bfcfg.log
dump_mode=0

efivars=/sys/firmware/efi/efivars
efi_global_var_guid=8be4df61-93ca-11d2-aa0d-00e098032b8c
efi_vlan_var_guid=9e23d768-d2f3-4366-9fc3-3a7aba864374
rshim_efi_mac_sysfs=${efivars}/RshimMacAddr-${efi_global_var_guid}
mfg_sysfs_dir=/sys/bus/platform/drivers/mlx-bootctl
oob_mac_sysfs=${mfg_sysfs_dir}/oob_mac

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}
}

mfg_cfg()
{
  local tmp_file=/tmp/.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 oob_efi_mac_sysfs=${efivars}/OobMacAddr-${efi_global_var_guid}
  local mac_err opn_err mac

  if [ $dump_mode -eq 1 ]; then
    [ -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)"
    [ -e "${sku_sysfs}" ] && echo "mfg: MFG_SKU=$(cat ${sku_sysfs} 2>/dev/null)"
    [ -e "${modl_sysfs}" ] && echo "mfg: MFG_MODL=$(cat ${modl_sysfs} 2>/dev/null)"
    [ -e "${sn_sysfs}" ] && echo "mfg: MFG_SN=$(cat ${sn_sysfs} 2>/dev/null)"
    [ -e "${uuid_sysfs}" ] && echo "mfg: MFG_UUID=$(cat ${uuid_sysfs} 2>/dev/null)"
    return
  fi

  if [ -z "${MFG_OOB_MAC}" ] || [ -z "${MFG_OPN}" ] || [ -z "${MFG_SKU}" ] ||
     [ -z "${MFG_MODL}" ] || [ -z "${MFG_SN}" ] || [ -z "${MFG_UUID}" ]; 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"
    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

  mfg_lock
}

#
# Set 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_mode} -eq 1 ]; then
    value=$(hexdump -s "${offset}" -n 1 -e '/1 "%d" "\n"' "${tmp_file}")
    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
}

#
# 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_ENABLE_I2C0      36            1
#
sys_cfg()
{
  local tmp_file=/tmp/.bfcfg-sysfs-data
  local sys_cfg_sysfs=${efivars}/BfSysCfg-9c759c02-e5a3-45e4-acfc-c34a500628a6

  if [ $dump_mode -eq 0 ] && [ ! -e "${sys_cfg_sysfs}" ]; then
    log_msg "sys: failed to find the 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} "ENABLE_I2C0" 36 "${SYS_ENABLE_I2C0}"

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

  rm -f ${tmp_file}
}

misc_cfg()
{
  # Rshim MAC address.
  local mac
  local tmp_file=/tmp/.bfcfg-misc-data

  if [ $dump_mode -eq 1 ]; then
    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}"
  elif [ -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
}

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

  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 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

  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" ] && 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
  done
  echo "0x${p0mac}"
}

#
# 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>
#   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 vlan vlan_len disk_entry_idx
  local shell_entry disk_entries boot_order
  local tmp_dir=/tmp/.boot_cfg
  local tmp_file=${tmp_dir}/boot
  local tmp_vlan_file=${tmp_dir}/vlan
  local value tmp_entry old_boot_order

  [ $dump_mode -eq 1 ] && return

  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>
      ifname=$(echo "${entry}" | cut -d '-' -f 2)
      proto=$(echo "${entry}" | cut -d '-' -f 3)
      vlan=""
      vlan_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

      boot_cfg_add_header "${tmp_file}"

      case "${ifname}" in
      OOB)
        mac=$(cat ${oob_mac_sysfs} 2>/dev/null)
        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))
        else
          boot_cfg_add_len "${tmp_file}" $((101 + vlan_len))
        fi
        boot_cfg_add_name "${tmp_file}" "${entry}"
        ;;

      RSHIM)
        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))
        else
          boot_cfg_add_len "${tmp_file}" $((101 + vlan_len))
        fi
        boot_cfg_add_name "${tmp_file}" "${entry}"
        ;;

      NIC_P0|NIC_P1)
        mac=$(get_hca_p0_mac)
        if [ -z "${mac}" ] || [ ."${mac}" = ."N/A" ]; then
          log_msg "boot: failed to get MAC for ${entry}"
          continue
        fi
        if [ "${ifname}" = "NIC_P1" ]; then
          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))
        else
          boot_cfg_add_len "${tmp_file}" $((137 + vlan_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
      mac=$(echo "${mac}" | tr '[:lower:]' '[:upper:]')
      mac="${mac//:/}"
      eval "tmp=\${${ifname}_VLAN_SET}"
      if [ -z "${tmp}" ]; then
        eval "${ifname}_VLAN_SET=1"
        chattr -i "${efivars}/${mac}-${efi_vlan_var_guid}" 2>/dev/null
        rm -f "${efivars}/${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

      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

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

usage()
{
  echo "syntax: bfcfg [--help|-h] [--dump|-d] [--part-info|-p]"
}

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

# Parse the arguments.
options=$(getopt -n bfcfg -o dhp -l dump,help,part-info -- "$@")
eval set -- "$options"
while [ "$1" != -- ]; do
  case $1 in
    --dump|-d) dump_mode=1 ;;
    --help|-h) usage; exit 0 ;;
    --part-info|-p) part_info; exit 0 ;;
  esac
  shift
done
shift

# Start a new log file.
rm -f ${log_file} >/dev/null
log_msg "bfcfg (ver ${bfcfg_version})"
log_msg "$(date)"
log_msg

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

mfg_cfg
sys_cfg
misc_cfg
boot_cfg
sync
