BMC_IP=${BMC_IP:-"192.168.240.1"}
BMC_PORT=${BMC_PORT:-"443"}
BMC_USER=${BMC_USER:-"root"}
DEFAULT_BMC_PASSWORD="0penBmc"
TMP_BMC_PASSWORD="Nvidia_12345!"
RESET_BMC_PASSWORD=0
BMC_PASSWORD=${BMC_PASSWORD:-""}
BMC_SSH_USER=${BMC_SSH_USER:-"$BMC_USER"}
BMC_SSH_PASSWORD=${BMC_SSH_PASSWORD:-"$BMC_PASSWORD"}
NEW_BMC_PASSWORD=${NEW_BMC_PASSWORD:-""}
UEFI_PASSWORD=${UEFI_PASSWORD:-""}
NEW_UEFI_PASSWORD=${NEW_UEFI_PASSWORD:-""}
OOB_IP=${OOB_IP:-"192.168.240.2"}
OOB_NETPREFIX=${OOB_NETPREFIX:-"29"}
BMC_IP_TIMEOUT=${BMC_IP_TIMEOUT:-60}
BMC_TASK_TIMEOUT=${BMC_TASK_TIMEOUT:-"1800"}
UPDATE_BMC_FW=${UPDATE_BMC_FW:-"yes"}
BMC_REBOOT=${BMC_REBOOT:-"no"}
CEC_REBOOT=${CEC_REBOOT:-"no"}
BMC_UPGRADE_RESET=${BMC_UPGRADE_RESET:-"yes"}
FIELD_MODE_SET=0
UPDATE_CEC_FW=${UPDATE_CEC_FW:-"yes"}
BMC_INSTALLED_VERSION=""
BMC_MIN_MULTIPART_VERSION="24.04"
CEC_MIN_RESET_VERSION="00.02.0180.0000"
UPDATE_DPU_GOLDEN_IMAGE=${UPDATE_DPU_GOLDEN_IMAGE:-"yes"}
UPDATE_NIC_FW_GOLDEN_IMAGE=${UPDATE_NIC_FW_GOLDEN_IMAGE:-"yes"}
RESET_BMC_RSHIM_LOG=${RESET_BMC_RSHIM_LOG:-"yes"}
# The PLDM format UUID in hexadecimal (no dashes, little-endian order).
PLDM_UUID="f018878ccb7d49439800a02f059aca02"

bmc_pref=""
if (lspci -n -d 15b3: | grep -wq 'a2dc'); then
        bmc_pref="bf3"
elif (lspci -n -d 15b3: | grep -wq 'a2d6'); then
        bmc_pref="bf2"
fi

NIC_FW_GI_PATH=${NIC_FW_GI_PATH:-"/lib/firmware/mellanox/bmc"}
DPU_GI_PATH=${DPU_GI_PATH:-"/lib/firmware/mellanox/bmc"}
BMC_PATH=${BMC_PATH:-"/lib/firmware/mellanox/bmc"}
CEC_PATH=${CEC_PATH:-"/lib/firmware/mellanox/cec"}

FORCE_BMC_FW_INSTALL=${FORCE_BMC_FW_INSTALL:-"no"}
FORCE_CEC_INSTALL=${FORCE_CEC_INSTALL:-"no"}
BMC_TMP_DIR=${BMC_TMP_DIR:-"/tmp"}

CHROOT_DIR=${CHROOT_DIR:-"/mnt"}

BMC_CREDENTIALS="'{\"username\":\"$BMC_USER\", \"password\":\"${BMC_PASSWORD}\"}'"
BMC_LINK_UP="no"
BMC_FIRMWARE_UPDATED="no"
BMC_FIRMWARE_STAGED="no"
export BMC_TOKEN=""
export task_id=""
export task_state=""
export task_status=""

SSH="ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR"
SCP="scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR"

mlx_mkcap="/usr/lib/firmware/mellanox/boot/capsule/scripts/mlx-mkcap"

reset_bmc_rshim_log()
{
	ilog "Resetting BMC Rshim log"
	is_bmc_rshim=$(sshpass -p $BMC_SSH_PASSWORD $SSH ${BMC_SSH_USER}@${BMC_IP} 'test -e /dev/rshim0/misc && echo "yes" || echo "no"')
	if [ "$is_bmc_rshim" == "no" ]; then
		ilog "BMC Rshim is not found. Skipping BMC Rshim log reset..."
		return
	fi
	ilog "$(sshpass -p $BMC_SSH_PASSWORD $SSH ${BMC_SSH_USER}@${BMC_IP} 'echo "CLEAR_ON_READ 1" > /dev/rshim0/misc')"
	log "BMC Rshim log reset started"
	# Wait for the misc to flush
	sleep 10
	ilog "$(sshpass -p $BMC_SSH_PASSWORD $SSH ${BMC_SSH_USER}@${BMC_IP} 'cat /dev/rshim0/misc')"
	ilog "$(sshpass -p $BMC_SSH_PASSWORD $SSH ${BMC_SSH_USER}@${BMC_IP} 'echo "CLEAR_ON_READ 0" > /dev/rshim0/misc')"
	log "BMC Rshim log reset completed"
}

skip_bmc()
{
	skip_status=1
	if [ ! -z "$1" ]; then
		skip_status=$1
	fi
	log "WARN Skipping BMC components upgrade."
	RC=$((RC+$skip_status))
	UPDATE_BMC_FW="no"
	UPDATE_CEC_FW="no"
	UPDATE_DPU_GOLDEN_IMAGE="no"
	UPDATE_NIC_FW_GOLDEN_IMAGE="no"
	if [ $skip_status -ne 0 ]; then
		update_progress bmc_fw 1
		update_progress cec_fw 1
		update_progress dpu_golden_image 1
		update_progress nic_firmware_golden_image 1
	fi
}

wait_for_bmc_ip()
{
    SECONDS=0
    while ! (ping -c 3 $BMC_IP > /dev/null 2>&1)
    do
        sleep 10
        if [ $SECONDS -gt $BMC_IP_TIMEOUT ]; then
            if ! (ping -c 3 $BMC_IP > /dev/null 2>&1); then
                rlog "ERR Failed to access BMC"
                ilog "- ERROR: Failed to access $BMC_IP after $SECONDS sec."
                RC=$((RC+1))
            fi
        fi
    done
    sleep 60
}

create_vlan()
{
	if [ "$BMC_LINK_UP" == "yes" ]; then
		return 0
	fi

	if [ "$(get_field_mode)" == "01" ]; then
		set_field_mode '00'
		FIELD_MODE_SET=1
		bmc_reboot_from_dpu
	fi

	ilog "Creating VLAN 4040"
	if [ ! -d "/sys/bus/platform/drivers/mlxbf_gige" ]; then
		ilog "- ERROR: mlxbf_gige driver is not loaded"
		RC=$((RC+1))
		return 1
	fi
	OOB_IF=$(ls -1 "/sys/bus/platform/drivers/mlxbf_gige/MLNXBF17:00/net")
	ilog "Configuring VLAN id 4040 on ${OOB_IF}. This operation may take up to $BMC_IP_TIMEOUT seconds"
	SECONDS=0
	while ! (ping -c 3 $BMC_IP > /dev/null 2>&1); do
		if [ $SECONDS -gt $BMC_IP_TIMEOUT ]; then
			rlog "ERR Failed to create VLAN."
			ilog "- ERROR: Failed to create VLAN interface after $SECONDS sec. All the BMC related operations will be skipped."
			skip_bmc
			return 1
		fi
		ilog "Bringing up ${OOB_IF} interface..."
		ilog "$(ip link set dev ${OOB_IF} up 2>&1)"
		while [ "$(cat /sys/class/net/${OOB_IF}/operstate 2> /dev/null)" != "up" ]; do
			ilog "${OOB_IF} status is $(cat /sys/class/net/${OOB_IF}/operstate 2>&1) after $SECONDS sec."
			if [ $SECONDS -gt $BMC_IP_TIMEOUT ]; then
				rlog "ERR Failed to bring UP ${OOB_IF}."
				ilog "- ERROR: Failed to bring UP ${OOB_IF} interface after $SECONDS sec. All the BMC related operations will be skipped."
				skip_bmc
				return 1
			fi
			sleep 1
		done
		ilog "${OOB_IF} interface is UP."
		ilog "$(ip link show ${OOB_IF} 2>&1)"
		if [ ! -d /sys/class/net/vlan4040 ]; then
			ilog "Bringing up VLAN4040 interface..."
			ilog "$(ip link add link ${OOB_IF} name vlan4040 type vlan id 4040 2>&1)"
			ilog "$(ip link set dev vlan4040 up 2>&1)"
		fi
		while [ "$(cat /sys/class/net/vlan4040/operstate 2> /dev/null)" != "up" ]; do
			ilog "VLAN4040 status is $(cat /sys/class/net/vlan4040/operstate 2>&1) after $SECONDS sec."
			if [ $SECONDS -gt $BMC_IP_TIMEOUT ]; then
				rlog "ERR Failed to bring UP vlan4040"
				ilog "- ERROR: Failed to bring UP vlan4040 interface after $SECONDS sec. All the BMC related operations will be skipped."
				skip_bmc
				return 1
			fi
			sleep 1
		done
		ilog "VLAN4040 interface is UP."
		ilog "Running: dhclient -e METRIC=1025 -e IF_METRIC=1025 vlan4040"
		output=$(dhclient -e METRIC=1025 -e IF_METRIC=1025 vlan4040 2>&1)
		rc=$?
		ilog "$output"
		sleep 5
		ilog "$(ip addr show vlan4040 2>&1)"
		if (ping -c 3 $BMC_IP > /dev/null 2>&1); then
			break
		fi
		if [ $rc -ne 0 ]; then
			ilog "dhclient failed"
			ilog "Configuring static IP: ${OOB_IP}/${OOB_NETPREFIX} for vlan4040"
			ip addr add ${OOB_IP}/${OOB_NETPREFIX} brd + dev vlan4040
			ilog "$(ip addr show vlan4040 2>&1)"
		fi
	done
	BMC_LINK_UP="yes"
	return 0
}

prepare_sshpass_environment()
{
        if [ ! -f /dev/pts/ptmx ]; then
                echo "none  /dev/pts  devpts  defaults 0 0" >> /etc/fstab
                mount /dev/pts
        fi
        if [ ! -f /etc/passwd ]; then
                echo "root:x:0:0:root:/root:/bin/bash" >> /etc/passwd
        fi
}

get_field_mode()
{
    mode=$(ipmitool raw 0x32 0x68 2> /dev/null | tr -d ' ')
    return $mode
}

set_field_mode()
{
    hvalue=$1

    ilog "Setting Field Mode to $hvalue"
    ilog "$(ipmitool raw 0x32 0x67 ${hvalue} 2>&1)"
}

get_bmc_token()
{
	cmd=$(echo curl -sSk -H \"Content-Type: application/json\" -X POST https://${BMC_IP}/login -d $BMC_CREDENTIALS)
	BMC_TOKEN=$(eval $cmd | jq -r ' .token')
	if [[ -z "$BMC_TOKEN" || "$BMC_TOKEN" == "null" ]]; then
		rlog "ERR Failed to get BMC token. Check BMC user/password"
		ilog "- ERROR: Failed to get BMC token using command: $cmd. Check BMC user/password."
		RC=$((RC+1))
		return 1
	fi
	return 0
}

change_uefi_password()
{
	UEFI_CREDENTIALS="'{\"Attributes\":{\"CurrentUefiPassword\":\"$UEFI_PASSWORD\",\"UefiPassword\":\"${NEW_UEFI_PASSWORD}\"}}'"
	cmd=$(echo curl -sSk -u $BMC_USER:"$BMC_PASSWORD" -H \"Content-Type: application/json\" -X PATCH https://${BMC_IP}/redfish/v1/Systems/Bluefield/Bios/Settings -d $UEFI_CREDENTIALS)
	ilog "Command: $cmd"
	output=$(eval $cmd)
	status=$(echo $output | jq '."@Message.ExtendedInfo"[0].Message')
	if [ "$status" != "\"The request completed successfully."\" ]; then
		rlog "ERR Failed to change UEFI password."
		ilog "Failed to change UEFI password. Output: $output"
		return 1
	fi

	ilog "UEFI password is set for the update. The new password will be activated on the second DPU reboot."

	return 0
}

change_bmc_password()
{
	current_password="$1"
	new_password="$2"

	NEW_BMC_CREDENTIALS="'{\"Password\":\"${new_password}\"}'"
	cmd=$(echo curl -sSk -u $BMC_USER:$current_password  -H \"Content-Type: application/json\" -X PATCH https://${BMC_IP}/redfish/v1/AccountService/Accounts/$BMC_USER -d $NEW_BMC_CREDENTIALS)
	output=$(eval $cmd)
	status=$(echo $output | jq '."@Message.ExtendedInfo"[0].Message')
	if [ "$status" != "\"The request completed successfully."\" ]; then
		rlog "ERR Failed to change BMC $BMC_USER password."
		ilog "Failed to change the password. Output: $output"
		return 1
	fi

	ilog "BMC password updated successfully."

	return 0
}

get_bmc_public_key()
{
		get_bmc_token
        SSH_PUBLIC_KEY=$(ssh-keyscan -t ed25519 ${OOB_IP} 2>&1 | tail -1 | cut -d ' ' -f 2-)

        pk_cmd=$(echo curl -sSk -H \"X-Auth-Token: $BMC_TOKEN\" -H \"Content-Type: application/json\" -X POST -d \'{\"RemoteServerIP\":\"${BMC_IP}\", \"RemoteServerKeyString\":\"$SSH_PUBLIC_KEY\"}\' https://$BMC_IP/redfish/v1/UpdateService/Actions/Oem/NvidiaUpdateService.PublicKeyExchange)
        bmc_pk=$(eval $pk_cmd | jq -r ' .Resolution')
		if [ "$bmc_pk" != "null" ]; then
			echo "$bmc_pk" >> /root/.ssh/authorized_keys
		fi
}

bmc_get_task_id()
{
	get_bmc_token
	task_id=$(curl -sSk -H "X-Auth-Token: $BMC_TOKEN" -X GET https://${BMC_IP}/redfish/v1/TaskService/Tasks | jq -r ' .Members' | grep odata.id | tail -1 | awk '{print $NF}' | tr -d '"')
	ilog "Task id: $task_id"
}

wait_bmc_task_complete()
{
	copy_status=$(curl -sSk -u $BMC_USER:$BMC_PASSWORD -X GET https://${BMC_IP}/redfish/v1/Chassis/Bluefield_ERoT | jq -r ' .Oem.Nvidia.BackgroundCopyStatus')
	if [ "X$copy_status" != "Xnull" ]; then
		if [ "$copy_status" != "Completed" ]; then
			ilog "BMC background copy is: $copy_status"
		fi

		SECONDS=0
		while [ "$copy_status" != "Completed" ]
		do
			if [ $SECONDS -gt $BMC_TASK_TIMEOUT ]; then
				ilog "- ERROR: BMC copy task timeout"
				RC=$((RC+1))
				break
			fi
			sleep 10
			copy_status=$(curl -sSk -u $BMC_USER:$BMC_PASSWORD -X GET https://${BMC_IP}/redfish/v1/Chassis/Bluefield_ERoT | jq -r ' .Oem.Nvidia.BackgroundCopyStatus')
		done
	fi

	bmc_get_task_id
	if [ -z "${task_id}" ]; then
		ilog "No active BMC task"
		return
	fi
	output=$(mktemp)
	#Check upgrade progress (%).
	get_bmc_token
	curl -sSk -H "X-Auth-Token: $BMC_TOKEN" https://${BMC_IP}${task_id} > $output
	percent_done=$(cat $output | jq -r ' .PercentComplete')
	SECONDS=0
	while [ "$percent_done" != "100" ]; do
		if [ "$percent_done" == "null" ]; then
			ilog "- ERROR: There is no task with task id: $task_id"
			RC=$((RC+1))
			break
		fi
		if [ $SECONDS -gt $BMC_TASK_TIMEOUT ]; then
			ilog "- ERROR: BMC task $task_id timeout"
			RC=$((RC+1))
			break
		fi
		get_bmc_token
		curl -sSk -H "X-Auth-Token: $BMC_TOKEN" https://${BMC_IP}${task_id} > $output
		percent_done=$(cat $output | jq -r ' .PercentComplete')
		task_state=$(cat $output | jq -r ' .TaskState')
		if [ "$task_state" == "Exception" ]; then
			ilog "- ERROR: BMC task $task_id exception"
			RC=$((RC+1))
			break
		elif [ "$task_state" == "Cancelled" ]; then
			ilog "- ERROR: BMC task $task_id cancelled"
			RC=$((RC+1))
			break
		fi
		sleep 10
	done
	task_state=$(jq '.TaskState' $output | tr -d '"')
	task_status=$(jq '.TaskStatus' $output | tr -d '"')

	if [ "$task_state$task_status" != "CompletedOK" ]; then
		echo "BMC task failed:"  >> $LOG
		cat $output >> $LOG
		RC=$((RC+1))
	fi
	/bin/rm -f $output
}

get_bmc_firmware_version()
{
	get_bmc_token
	BMC_FIRMWARE_URL=$(curl -sSk -H "X-Auth-Token: $BMC_TOKEN" -X GET https://${BMC_IP}/redfish/v1/UpdateService/FirmwareInventory | grep BMC_Firmware | awk '{print $NF}' | tr -d \")
	ilog "- INFO: BMC_FIRMWARE_URL: $BMC_FIRMWARE_URL"
	BMC_INSTALLED_VERSION="$(curl -sSk -H "X-Auth-Token: $BMC_TOKEN" -X GET https://${BMC_IP}${BMC_FIRMWARE_URL} | jq -r ' .Version' | grep -o "\([0-9]\+\).\([0-9]\+\)-\([0-9]\+\)")"
	if [ -z "$BMC_INSTALLED_VERSION" ]; then
		ilog "- ERROR: Cannot detect running BMC firmware version"
		RC=$((RC+1))
		return 1
	fi
	ilog "Running BMC firmware version: $BMC_INSTALLED_VERSION"
}

is_pldm_file()
{
	# Read the first 16 bytes and print as hex
	file_uuid=$(xxd -p -l 16 "$1")
	if [ "$file_uuid" == "$PLDM_UUID" ]; then
		return 0
	fi
	return 1
}

update_bmc_fw()
{
	rc=0
	wait_bmc_task_complete
	if [[ $ACTIVATE_FLOW -eq 1 || $COMPATIBILITY_FLOW -eq 1 ]]; then
		log "Updating BMC firmware"
	elif [ $UPGRADE_FLOW -eq 1 ]; then
		log "Staging BMC firmware"
	else
		return 0
	fi
	#Set upload image from local BFB storage (or tempfs).
	image="${BMC_IMAGE}"
	if [ -z "$image" ]; then
		ilog "- ERROR: Cannot find BMC firmware image"
		RC=$((RC+1))
		return 1
	fi
	ilog "Found BMC firmware image: $image"

	# BMC_IMAGE_VERSION=$(cat {${CHROOT_DIR},/}${BMC_PATH}/${bmc_pref}-bmc-fw.version 2> /dev/null | head -1)
	if [ -z "$BMC_IMAGE_VERSION" ]; then
		if [[ "$image" =~ tar ]]; then
			# BlueField-2
			BMC_IMAGE_VERSION="$(strings -a -t d $image | grep -m 1 ExtendedVersion | cut -d '-' -f 2,3)"
		else
			BMC_IMAGE_VERSION="$(strings -a -t d $image | grep -m 1 BF- | cut -d '-' -f 2- | sed -e 's/ //g')"
		fi
		if [ -z "$BMC_IMAGE_VERSION" ]; then
			ilog "- ERROR: Cannot detect included BMC firmware version"
			RC=$((RC+1))
			return 1
		fi
	fi
	ilog "Provided BMC firmware version: $BMC_IMAGE_VERSION"

	if [ -z "$BMC_INSTALLED_VERSION" ]; then
		get_bmc_firmware_version
	fi

	if [ "${BMC_IMAGE_VERSION}" == "${BMC_INSTALLED_VERSION}" ]; then
		if [ "X${FORCE_BMC_FW_INSTALL}" == "Xyes" ]; then
			ilog "Installed BMC version is the same as provided. FORCE_BMC_FW_INSTALL is set."
		else
			ilog "Installed BMC version is the same as provided. Skipping BMC firmware update."
			return 0
		fi
	fi

	if [[ $(echo -e "${BMC_MIN_MULTIPART_VERSION}\n${BMC_INSTALLED_VERSION}" | sort -V | head -n1) == "${BMC_MIN_MULTIPART_VERSION}" ]]; then
		if [[ $ACTIVATE_FLOW -eq 1 || $COMPATIBILITY_FLOW -eq 1 ]]; then
			ilog "Proceeding with the BMC firmware update using update-multipart."
			ilog "curl -sSk -u <BMC_USER:BMC_PASSWORD> https://${BMC_IP}/redfish/v1/UpdateService/update-multipart -F 'UpdateParameters={\"ForceUpdate\":true};type=application/octet-stream' -F UpdateFile=@${image}"
			output=$(curl -sSk -u $BMC_USER:$BMC_PASSWORD https://${BMC_IP}/redfish/v1/UpdateService/update-multipart -F 'UpdateParameters={"ForceUpdate":true};type=application/octet-stream' -F UpdateFile=@${image} 2>&1)
			ilog "BMC Firmware update: $output"
			BMC_FIRMWARE_UPDATED="yes"
		elif [ $UPGRADE_FLOW -eq 1 ]; then
			ilog "Proceeding with the BMC firmware staging."
			ilog "curl -sSk -u <BMC_USER:BMC_PASSWORD> https://${BMC_IP}/redfish/v1/UpdateService/update-multipart -F 'UpdateParameters={\"Oem\":{\"Nvidia\":{\"UpdateOption\":\"StageOnly\"}}};type=application/octet-stream' -F UpdateFile=@${image}"
			output=$(curl -sSk -u $BMC_USER:$BMC_PASSWORD https://${BMC_IP}/redfish/v1/UpdateService/update-multipart -F 'UpdateParameters={"Oem":{"Nvidia":{"UpdateOption":"StageOnly"}}};type=application/octet-stream' -F UpdateFile=@${image} 2>&1)
			ilog "BMC Firmware staging: $output"
			BMC_FIRMWARE_STAGED="yes"
		fi
	else
		ilog "Proceeding with the BMC firmware update."
		ilog "curl -sSk -u <BMC_USER:BMC_PASSWORD> -H "Content-Type: application/octet-stream" -X POST -T ${image} https://${BMC_IP}/redfish/v1/UpdateService"
		output=$(curl -sSk -u $BMC_USER:$BMC_PASSWORD -H "Content-Type: application/octet-stream" -X POST -T ${image} https://${BMC_IP}/redfish/v1/UpdateService 2>&1)
		ilog "BMC Firmware update: $output"
		BMC_FIRMWARE_UPDATED="yes"
	fi

	wait_bmc_task_complete
	if [ "$BMC_FIRMWARE_UPDATED" == "yes" ]; then
		post_bmc_install_message="INFO: BMC firmware was updated to: ${BMC_IMAGE_VERSION}"
		if [ "$BMC_REBOOT" != "yes" ]; then
			post_bmc_install_message="${post_bmc_install_message}. BMC restart is required"
		fi
		log "${post_bmc_install_message}"
		if [ "$BMC_UPGRADE_RESET" == "yes" ]; then
			BMC_UPGRADE_RESET=1 bfcfg -f /dev/null
		fi
	elif [ "$BMC_FIRMWARE_STAGED" == "yes" ]; then
		post_bmc_install_message="INFO: BMC firmware was staged"
		if [ "$ACTIVATE_FLOW" -ne 1 ]; then
			post_bmc_install_message="${post_bmc_install_message}. BMC Firmware activation is required"
		fi
		log "${post_bmc_install_message}"
		touch ${BFB_DIR}/bmc_firmware_staged
	fi

	return $rc
}

update_cec_fw()
{
	rc=0
	wait_bmc_task_complete
	log "Updating CEC firmware"
	image="${CEC_IMAGE}"

	if [ -z "$image" ]; then
		ilog "- ERROR: Cannot find CEC firmware image"
		RC=$((RC+1))
		return 1
	fi
	ilog "Found CEC firmware image: $image"

	# CEC_IMAGE_VERSION=$(cat {${CHROOT_DIR},/}${CEC_PATH}/${bmc_pref}-cec-fw.version 2> /dev/null | head -1)
	if [ -z "$CEC_IMAGE_VERSION" ]; then
		CEC_IMAGE_VERSION="$(strings -a -t d $image | grep -m 1 cec | cut -d '-' -f 2- | sed -e 's/ //g;s/.n02//')"
	fi
	if [ -z "$CEC_IMAGE_VERSION" ]; then
		# BlueField-2 CEC version format
		CEC_IMAGE_VERSION_HEXA="$(echo $image | grep -o '\-\(.*\)_' | grep -o '\([0-9a-fA-F]\+\).\([0-9a-fA-F]\+\)')"
		CEC_IMAGE_VERSION=$(printf "%d" 0x${CEC_IMAGE_VERSION_HEXA%*.*}).$(printf "%d" 0x${CEC_IMAGE_VERSION_HEXA##*.})
	fi
	if [ -z "$CEC_IMAGE_VERSION" ]; then
		ilog "- ERROR: Cannot detect included CEC firmware version"
		RC=$((RC+1))
		return 1
	fi
	ilog "Provided CEC firmware version: $CEC_IMAGE_VERSION"

	get_bmc_token

	CEC_INSTALLED_VERSION="$(curl -sSk -H "X-Auth-Token: $BMC_TOKEN" -X GET https://${BMC_IP}/redfish/v1/UpdateService/FirmwareInventory/Bluefield_FW_ERoT | jq -r ' .Version' | sed -e "s/[_|-]/./g;s/.n02//")"
	if [ -z "$CEC_INSTALLED_VERSION" ]; then
		ilog "- ERROR: Cannot detect running CEC firmware version"
		RC=$((RC+1))
		return 1
	fi
	ilog "Running CEC firmware version: $CEC_INSTALLED_VERSION"

	if [ "${CEC_IMAGE_VERSION}" == "${CEC_INSTALLED_VERSION}" ]; then
		if [ "X${FORCE_CEC_INSTALL}" == "Xyes" ]; then
			ilog "Installed CEC version is the same as provided. FORCE_CEC_INSTALL is set."
		else
			ilog "Installed CEC version is the same as provided. Skipping CEC firmware update."
			return 0
		fi
	fi

	ilog "Proceeding with the CEC firmware update..."

	if [ -z "$BMC_INSTALLED_VERSION" ]; then
		get_bmc_firmware_version
	fi

	if [[ $(echo -e "${BMC_MIN_MULTIPART_VERSION}\n${BMC_INSTALLED_VERSION}" | sort -V | head -n1) == "${BMC_MIN_MULTIPART_VERSION}" ]]; then
		ilog "curl -sSk -u <BMC_USER:BMC_PASSWORD> https://${BMC_IP}/redfish/v1/UpdateService/update-multipart -F 'UpdateParameters={\"ForceUpdate\":true};type=application/octet-stream' -F UpdateFile=@${image}"
		output=$(curl -sSk -u $BMC_USER:$BMC_PASSWORD https://${BMC_IP}/redfish/v1/UpdateService/update-multipart -F 'UpdateParameters={"ForceUpdate":true};type=application/octet-stream' -F UpdateFile=@${image} 2>&1)
	else
		ilog "curl -sSk -u <BMC_USER:BMC_PASSWORD> -H "Content-Type: application/octet-stream" -X POST -T ${image} https://${BMC_IP}/redfish/v1/UpdateService"
		output=$(curl -sSk -u $BMC_USER:$BMC_PASSWORD -H "Content-Type: application/octet-stream" -X POST -T ${image} https://${BMC_IP}/redfish/v1/UpdateService 2>&1)
	fi
	ilog "CEC Firmware update: $output"

	wait_bmc_task_complete
	if [ "$CEC_REBOOT" == "yes" ]; then
		if [[ $(echo -e "${CEC_MIN_RESET_VERSION}\n${CEC_INSTALLED_VERSION}" | sort -V | head -n1) == "${CEC_MIN_RESET_VERSION}" ]]; then
			log "Rebooting CEC..."
			output=$(curl -sSk -u $BMC_USER:"$BMC_PASSWORD" -H "Content-Type: application/json" -X POST -d '{"ResetType": "GracefulRestart"}' https://${BMC_IP}/redfish/v1/Chassis/Bluefield_ERoT/Actions/Chassis.Reset)
			status=$(echo $output | jq '."@Message.ExtendedInfo"[0].Message')
			if [ "$status" == "\"The request completed successfully."\" ]; then
				log "INFO: CEC firmware was updated to ${CEC_IMAGE_VERSION}."
			else
				rc=1
				rlog "ERR Failed to reset CEC"
				ilog "Failed to reset CEC. Output: $output"
				log "INFO: CEC firmware was updated to ${CEC_IMAGE_VERSION}. Host power cycle is required"
			fi
		else
			log "INFO: CEC firmware was updated to ${CEC_IMAGE_VERSION}. Host power cycle is required"
		fi
	fi
	if [ "$BMC_UPGRADE_RESET" == "yes" ]; then
		BMC_UPGRADE_RESET=1 bfcfg -f /dev/null
	fi

	return $rc
}

bmc_reboot()
{
	log "Rebooting BMC..."
	get_bmc_token
	curl -sSk -H "X-Auth-Token: $BMC_TOKEN" -H "Content-Type: application/json" -X POST https://${BMC_IP}/redfish/v1/Managers/Bluefield_BMC/Actions/Manager.Reset -d '{"ResetType":"GracefulRestart"}'
	sleep 10
	wait_for_bmc_ip
}

bmc_reboot_from_dpu()
{
	ipmitool mc reset cold
	sleep 10
}

update_dpu_golden_image()
{
	rc=0
	image_type=""
	log "Updating DPU Golden Image"
	image=${DPU_GOLDEN_IMAGE}

	if [ -z "$image" ]; then
		ilog "DPU golden image was not found"
		RC=$((RC+rc))
		return 1
	fi

	if is_pldm_file $image; then
		image_type="pldm"
	else
		image_type="bfb"
	fi

	if [ "$image_type" == "bfb" ]; then
		prepare_sshpass_environment

		ilog "Found DPU Golden Image: $image"
		DPU_GI_IMAGE_VERSION="$(sha256sum $image | awk '{print $1}')"
		ilog "Provided DPU Golden Image version: $DPU_GI_IMAGE_VERSION"

		DPU_GI_INSTALLED_VERSION="$(sshpass -p $BMC_SSH_PASSWORD $SSH ${BMC_SSH_USER}@${BMC_IP} dpu_golden_image golden_image_arm -V 2> /dev/null)"
		ilog "Installed DPU Golden Image version: $DPU_GI_INSTALLED_VERSION"

		if [ "$DPU_GI_IMAGE_VERSION" == "$DPU_GI_INSTALLED_VERSION" ]; then
			ilog "Installed DPU Golden Image version is the same as provided. Skipping DPU Golden Image update."
		else
			sshpass -p $BMC_SSH_PASSWORD $SSH ${BMC_SSH_USER}@${BMC_IP} mkdir -p ${BMC_TMP_DIR}/golden-image-arm
			ilog "Copying $image to ${BMC_SSH_USER}@${BMC_IP}:${BMC_TMP_DIR}/golden-image-arm"
			sshpass -p $BMC_SSH_PASSWORD $SCP $image ${BMC_SSH_USER}@${BMC_IP}:${BMC_TMP_DIR}/golden-image-arm
			ilog "Installing DPU Golden Image on the BMC by: dpu_golden_image golden_image_arm -w ${BMC_TMP_DIR}/golden-image-arm/$(basename $image)"
			output=$(sshpass -p $BMC_SSH_PASSWORD $SSH ${BMC_SSH_USER}@${BMC_IP} dpu_golden_image golden_image_arm -w ${BMC_TMP_DIR}/golden-image-arm/$(basename $image) 2>&1)
			if [ $? -eq 0 ]; then
				log "DPU Golden Image installed successfully"
			else
				log "ERR DPU Golden Image installed failed"
				rc=1
			fi
			ilog "$output"
		fi
	elif [ "$image_type" == "pldm" ]; then

		wait_bmc_task_complete

		if [ -z "$BMC_INSTALLED_VERSION" ]; then
			get_bmc_firmware_version
		fi

		if [[ $(echo -e "${BMC_MIN_MULTIPART_VERSION_PLDM}\n${BMC_INSTALLED_VERSION}" | sort -V | head -n1) == "${BMC_MIN_MULTIPART_VERSION_PLDM}" ]]; then
			ilog "curl -k -H \"X-Auth-Token: <BMC_TOKEN>\" -X POST https://${BMC_IP}/redfish/v1/UpdateService/update-multipart  -F 'UpdateParameters={};type=application/json'  -F \"UpdateFile=@${image};type=application/octet-stream\""
			output=$(curl -sSk -H "X-Auth-Token: $BMC_TOKEN" -X POST https://${BMC_IP}/redfish/v1/UpdateService/update-multipart  -F 'UpdateParameters={};type=application/json'  -F "UpdateFile=@${image};type=application/octet-stream" 2>&1)
			ilog "BMC Firmware update: $output"
		else
			ilog "DPU Golden Image update is not supported for BMC firmware version $BMC_INSTALLED_VERSION"
			ilog "Please update BMC firmware to version $BMC_MIN_MULTIPART_VERSION_PLDM or higher"
			RC=$((RC+1))
			return 1
		fi
	fi

	if (echo "${BD_PSIDS}" | grep -qw "${PSID}"); then
		if [ "X${UPLOAD_CONFIG_IMAGE}" == "Xyes" ]; then
			if [ "X${GENERATE_CONFIG_IMAGE}" == "Xyes" ]; then
				generate_config_image $image
			fi
			config_image=${DPU_GI_PATH}/${CONFIG_IMAGE}
			if [ ! -e "$config_image" ]; then
				ilog "Config image does not exist: $config_image"
				RC=$((RC+1))
				return $RC
			fi
			sshpass -p $BMC_SSH_PASSWORD $SSH ${BMC_SSH_USER}@${BMC_IP} mkdir -p ${BMC_TMP_DIR}/golden-image-config
			ilog "Copying $config_image to ${BMC_SSH_USER}@${BMC_IP}:${BMC_TMP_DIR}/golden-image-config"
			sshpass -p $BMC_SSH_PASSWORD $SCP $config_image ${BMC_SSH_USER}@${BMC_IP}:${BMC_TMP_DIR}/golden-image-config
			if [ "X$BMC_INSTALL_CONFIG_IMAGE" == "Xyes" ]; then
				ilog "Installing DPU Config Image on the BMC by: dpu_golden_image golden_image_config -w ${BMC_TMP_DIR}/golden-image-config/$(basename $config_image)"
				output=$(sshpass -p $BMC_SSH_PASSWORD $SSH ${BMC_SSH_USER}@${BMC_IP} dpu_golden_image golden_image_config -w ${BMC_TMP_DIR}/golden-image-config/$(basename $config_image) 2>&1)
				if [ $? -eq 0 ]; then
					log "Installed BMC config image"
					BMC_CONFIG_UPDATED="yes"
				else
					log "ERR Failed to install BMC config image"
				fi
				ilog "$output"
			fi
		fi
	fi

	RC=$((RC+rc))
	return $RC
}

update_nic_firmware_golden_image()
{
	rc=0
	log "Updating NIC firmware Golden Image"
	image_type=""
	image=${NIC_FW_GOLDEN_IMAGE}

	if [ -z "$image" ]; then
		ilog "NIC firmware Golden Image for $dpu_part_number was not found"
		RC=$((RC+1))
		return 1
	fi

	if is_pldm_file $image; then
		image_type="pldm"
	else
		image_type="bfb"
	fi

	if [ "$image_type" == "bfb" ]; then
		prepare_sshpass_environment

		ilog "Found NIC firmware Golden Image: $image"
		NIC_GI_IMAGE_VERSION="$(sha256sum $image | awk '{print $1}')"
		ilog "Provided NIC firmware Golden Image version: $NIC_GI_IMAGE_VERSION"

		NIC_GI_INSTALLED_VERSION="$(sshpass -p $BMC_SSH_PASSWORD $SSH ${BMC_SSH_USER}@${BMC_IP} dpu_golden_image golden_image_nic -V 2> /dev/null)"
		ilog "Installed NIC firmware Golden Image version: $NIC_GI_INSTALLED_VERSION"

		if [ "$NIC_GI_IMAGE_VERSION" == "$NIC_GI_INSTALLED_VERSION" ]; then
			ilog "Installed NIC firmware Golden Image version is the same as provided. Skipping NIC firmware Golden Image update."
			return 0
		fi

		sshpass -p $BMC_SSH_PASSWORD $SSH ${BMC_SSH_USER}@${BMC_IP} mkdir -p ${BMC_TMP_DIR}/golden-image-nic
		sshpass -p $BMC_SSH_PASSWORD $SCP $image ${BMC_SSH_USER}@${BMC_IP}:${BMC_TMP_DIR}/golden-image-nic
		output=$(sshpass -p $BMC_SSH_PASSWORD $SSH ${BMC_SSH_USER}@${BMC_IP} dpu_golden_image golden_image_nic -w ${BMC_TMP_DIR}/golden-image-nic/$(basename $image) 2>&1)
		if [ $? -eq 0 ]; then
			log "NIC firmware Golden Image installed successfully"
		else
			log "ERR NIC firmware Golden Image installed failed"
			rc=1
		fi
		ilog "$output"
	elif [ "$image_type" == "pldm" ]; then

		wait_bmc_task_complete

		if [ -z "$BMC_INSTALLED_VERSION" ]; then
			get_bmc_firmware_version
		fi

		if [[ $(echo -e "${BMC_MIN_MULTIPART_VERSION_PLDM}\n${BMC_INSTALLED_VERSION}" | sort -V | head -n1) == "${BMC_MIN_MULTIPART_VERSION_PLDM}" ]]; then
			ilog "curl -k -H \"X-Auth-Token: <BMC_TOKEN>\" -X POST https://${BMC_IP}/redfish/v1/UpdateService/update-multipart  -F 'UpdateParameters={};type=application/json'  -F \"UpdateFile=@${image};type=application/octet-stream\""
			output=$(curl -sSk -H "X-Auth-Token: $BMC_TOKEN" -X POST https://${BMC_IP}/redfish/v1/UpdateService/update-multipart  -F 'UpdateParameters={};type=application/json'  -F "UpdateFile=@${image};type=application/octet-stream" 2>&1)
			ilog "BMC Firmware update: $output"
		else
			ilog "NIC Firmware Golden Image update is not supported for BMC firmware version $BMC_INSTALLED_VERSION"
			ilog "Please update BMC firmware to version $BMC_MIN_MULTIPART_VERSION_PLDM or higher"
			RC=$((RC+1))
			return 1
		fi
	fi
	RC=$((RC+rc))
	return $rc
}

bmc_components_update()
{
	if function_exists pre_bmc_components_update; then
		log "INFO: Running pre_bmc_components_update from bf.cfg"
		pre_bmc_components_update
	fi

	if [[ ! -z "$UEFI_PASSWORD" && ! -z "$NEW_UEFI_PASSWORD" ]]; then
		if [[ -z "$BMC_USER" || -z "$BMC_PASSWORD" ]]; then
			ilog "BMC_USER and/or BMC_PASSWORD are not defined. Skipping UEFI password change."
		else
			create_vlan || return
			change_uefi_password
			update_progress uefi_password $?
		fi
	fi

	if [[ "$UPDATE_BMC_FW" == "yes" || "$UPDATE_CEC_FW" == "yes" || "$UPDATE_DPU_GOLDEN_IMAGE" == "yes" || "$UPDATE_NIC_FW_GOLDEN_IMAGE" == "yes" || ! -z "$NEW_BMC_PASSWORD" ]]; then
		if [[ -z "$BMC_USER" || -z "$BMC_PASSWORD" ]]; then
			ilog "BMC_USER and/or BMC_PASSWORD are not defined. Skipping BMC components upgrade."
			skip_bmc 0
			return
		else
			ilog "INFO: Running BMC components update flow"
			if ! (ping -c 3 $BMC_IP > /dev/null 2>&1); then
				create_vlan || return
			else
				BMC_LINK_UP="yes"
			fi
			# get_bmc_public_key
			if [ "$BMC_LINK_UP" == "yes" ]; then
				if [ ! -z "$NEW_BMC_PASSWORD" ]; then
					if change_bmc_password "$BMC_PASSWORD" "$NEW_BMC_PASSWORD"; then
						update_progress bmc_password 0
						if [ "$BMC_PASSWORD" == "$BMC_SSH_PASSWORD" ]; then
							BMC_SSH_PASSWORD="$NEW_BMC_PASSWORD"
						fi
						BMC_PASSWORD="$NEW_BMC_PASSWORD"
					else
						update_progress bmc_password 0
						skip_bmc
						return
					fi
				elif [ "$BMC_PASSWORD" == "$DEFAULT_BMC_PASSWORD" ]; then
					ilog "BMC password has the default value. Changing to the temporary password."
					if change_bmc_password "$BMC_PASSWORD" "$TMP_BMC_PASSWORD"; then
						BMC_PASSWORD="$TMP_BMC_PASSWORD"
						BMC_SSH_PASSWORD="$TMP_BMC_PASSWORD"
					else
						skip_bmc
						return
					fi
					RESET_BMC_PASSWORD=1
				fi
				BMC_CREDENTIALS="'{\"username\":\"$BMC_USER\", \"password\":\"${BMC_PASSWORD}\"}'"
			else
				skip_bmc
				return
			fi
		fi

		if ! get_bmc_token; then
			skip_bmc
			return
		fi

	else
		return
	fi

	if [ "$RESET_BMC_RSHIM_LOG" == "yes" ]; then
		reset_bmc_rshim_log
	fi

	if function_exists bmc_custom_action1; then
		log "INFO: Running bmc_custom_action1 from bf.cfg"
		bmc_custom_action1
	fi

	if [[ "$UPDATE_CEC_FW" == "yes" && "$BMC_LINK_UP" == "yes" ]]; then
		update_cec_fw
		update_progress cec_fw $?
	fi

	if function_exists bmc_custom_action2; then
		log "INFO: Running bmc_custom_action2 from bf.cfg"
		bmc_custom_action2
	fi

	if [[ "$UPDATE_BMC_FW" == "yes" && "$BMC_LINK_UP" == "yes" ]]; then
		update_bmc_fw
		update_progress bmc_fw $?
	fi

	if function_exists bmc_custom_action3; then
		log "INFO: Running bmc_custom_action3 from bf.cfg"
		bmc_custom_action3
	fi

	if [[ "$UPDATE_BMC_FW" == "yes" && "$BMC_LINK_UP" == "yes" && "$BMC_REBOOT" == "yes" && "$BMC_FIRMWARE_UPDATED" == "yes" ]]; then
		bmc_reboot
	fi

	if function_exists bmc_custom_action4; then
		log "INFO: Running bmc_custom_action4 from bf.cfg"
		bmc_custom_action4
	fi

	if [[ "$UPDATE_DPU_GOLDEN_IMAGE" == "yes" && "$BMC_LINK_UP" == "yes" ]]; then
		update_dpu_golden_image
		update_progress dpu_golden_image $?
	fi

	if function_exists bmc_post_dpu_gi; then
		log "INFO: Running bmc_post_dpu_gi from bf.cfg"
		bmc_post_dpu_gi
	fi

	if [[ "$UPDATE_NIC_FW_GOLDEN_IMAGE" == "yes" && "$BMC_LINK_UP" == "yes" ]]; then
		if [ -z "${dpu_part_number}" ]; then
			log "Cannot identify DPU Part Number. Skipping NIC firmware Golden Image update."
		else
			update_nic_firmware_golden_image
			update_progress nic_firmware_golden_image $?
		fi
	fi

	if function_exists bmc_post_nic_fw_gi; then
		log "INFO: Running bmc_post_nic_fw_gi from bf.cfg"
		bmc_post_nic_fw_gi
	fi

	if [ "X${BMC_REBOOT}" == "Xyes" ]; then
		bmc_reboot
	fi

	if [ $RESET_BMC_PASSWORD -eq 1 ]; then
		ilog "Reset BMC configuration to default"
		output=$(curl -sSk -u $BMC_USER:"$BMC_PASSWORD" -H "Content-Type: application/json" -X POST https://${BMC_IP}/redfish/v1/Managers/Bluefield_BMC/Actions/Manager.ResetToDefaults -d '{"ResetToDefaultsType": "ResetAll"}')
		status=$(echo $output | jq '."@Message.ExtendedInfo"[0].Message')
		if [ "$status" != "\"The request completed successfully."\" ]; then
			rlog "ERR Failed to reset BMC $BMC_USER password."
			ilog "Failed to reset BMC $BMC_USER password. Output: $output"
		fi
	fi

	if [ $FIELD_MODE_SET -eq 1 ]; then
		if [ "$(get_field_mode)" == "00" ]; then
			set_field_mode '01'
			FIELD_MODE_SET=0
			bmc_reboot_from_dpu
		fi
	fi

	if function_exists post_bmc_components_update; then
		log "INFO: Running post_bmc_components_update from bf.cfg"
		post_bmc_components_update
	fi
}

bmc_activate()
{
	if [ "$BMC_LINK_UP" == "yes" ]; then
		output=$(curl -sSk -u $BMC_USER:"$BMC_PASSWORD" -X POST https://$BMC_IP/redfish/v1/UpdateService/Actions/Oem/NvidiaUpdateService.ActivateFirmware)
		status=$(echo $output | jq '."@Message.ExtendedInfo"[0].Message')
		if [ "$status" != "\"The request completed successfully."\" ]; then
			rlog "ERR Failed to activate BMC firmware."
			ilog "Failed to activate BMC firmware. Output: $output"
			RC=$((RC+1))
			return 1
		else
			log "INFO: BMC firmware activated successfully."
			if [ "$BMC_UPGRADE_RESET" == "yes" ]; then
				BMC_UPGRADE_RESET=1 bfcfg -f /dev/null
			fi
			return 0
		fi
		ilog "$output"
	else
		ilog "BMC link is not up. Skipping BMC firmware activation."
		RC=$((RC+1))
		return 1
	fi
}
