#!/usr/bin/bash

###############################################################################
#
# Copyright 2024 NVIDIA Corporation
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
# the Software, and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
###############################################################################

ofed_ver_internal=$(ofed_info -n)
ofed_ver_minor_len=$(echo "$ofed_ver_internal" | cut -d- -f2 | wc -c)

if [ "$ofed_ver_minor_len" = 6 ]; then
    mlnx_ofed_release=$ofed_ver_internal.0
else
    mlnx_ofed_release=$ofed_ver_internal
fi

OFED=MLNX_OFED_LINUX-$mlnx_ofed_release

identify_installed_mlnx_pci_cards() {
	lspci \
		| grep "Mellanox Technologies" \
		| grep "controller" \
		| awk '{ print $1 }' \
		| sed 's/\.[0-9]*//' \
		| sort \
		| uniq \
		| xargs -n 1 echo
}

get_fw_for_mlnx_pci() {
	PCI_ID=$1
	test -f /sys/bus/pci/devices/*${PCI_ID}.0/infiniband/mlx5_0/fw_ver || return 1
	cat /sys/bus/pci/devices/*${PCI_ID}.0/infiniband/mlx5_0/fw_ver | xargs -n 1 echo
}

get_bluefield_gen() {
	PCI_ID=$1
	lspci \
		| grep "${PCI_ID}.0" \
		| grep -ioP 'bluefield([\S]+)' \
		| xargs -n 1 echo
}

declare -A BF_FW_ARR=(
	[BlueField]=/opt/mellanox/mlnx-fw-updater/firmware/mlxfwmanager_sriov_dis_aarch64_41682
	[BlueField-2]=/opt/mellanox/mlnx-fw-updater/firmware/mlxfwmanager_sriov_dis_aarch64_41686
	[BlueField-3]=/opt/mellanox/mlnx-fw-updater/firmware/mlxfwmanager_sriov_dis_aarch64_41692
)

IS_DPU=$(test -f /etc/mlnx-release && echo "true" || echo "false")
mapfile -t INSTALLED_CARDS_PCI_IDS < <(identify_installed_mlnx_pci_cards)

BOOTIMG_LOCATION=/lib/firmware/mellanox/boot/default.bfb

if [ -e "$BOOTIMG_LOCATION" ]; then
	BUILD_ATF=$(strings $BOOTIMG_LOCATION | grep -m 1 "(\(release\|debug\))")
	BUILD_UEFI=$(strings -e l $BOOTIMG_LOCATION | grep "BlueField" |\
		    cut -d':' -f 2)
fi

get_available_fw_version_for_bf() {
	BLUEFIELD_GEN=$1
	test -f "${BF_FW_ARR[$BLUEFIELD_GEN]}" || return 1
	echo $(${BF_FW_ARR[$BLUEFIELD_GEN]} --list | head -3 | tail -1 | awk '{print $4}')
}

print_fw_current() {
	BF_CURRENT_FW=
	PRINTED_HEADER="false"
	for id in "${INSTALLED_CARDS_PCI_IDS[@]}"
	do
		BLUEFIELD_GEN=$(get_bluefield_gen $id)
		BF_CURRENT_FW=$(get_fw_for_mlnx_pci $id)
		if [ "$rc" == 1 ]; then
			continue
		fi
		if [ "$PRINTED_HEADER" == "false" ]; then
			echo "Firmware (Current):"
			PRINTED_HEADER="true"
		fi
		echo "- $BLUEFIELD_GEN $BF_CURRENT_FW"
		if [ "$IS_DPU" == "true" ]; then
			break
		fi
	done
}

print_fw_available() {
	PRINTED_HEADER="false"

	if [[ ! -z "$ALL_FW" ]]; then
		for fw_file in /opt/mellanox/mlnx-fw-updater/firmware/*; do

			BLUEFIELD_GEN=$($fw_file -l | grep -ioP 'bluefield([\S]+)' | grep "BlueField-" | sort | uniq)

			BF_AVAILABLE_FW=$(${BF_FW_ARR[$BLUEFIELD_GEN]} --list | head -3 | tail -1 | awk '{print $4}')

			if [ "$PRINTED_HEADER" == "false" ]; then
				echo "Firmware (Available):"
				PRINTED_HEADER="true"
			fi
			echo "- $BLUEFIELD_GEN $BF_AVAILABLE_FW"
		done
		return
	fi

	for id in "${INSTALLED_CARDS_PCI_IDS[@]}"
	do
		PCI_ID=${id}
		BLUEFIELD_GEN=$(get_bluefield_gen $PCI_ID)
		BF_AVAILABLE_FW=$(get_available_fw_version_for_bf $BLUEFIELD_GEN)
		rc=$?
		if [ "$rc" == 1 ]; then
			continue
		fi
		if [ "$PRINTED_HEADER" == "false" ]; then
			echo "Firmware (Available):"
			PRINTED_HEADER="true"
		fi
		echo "- $BLUEFIELD_GEN $BF_AVAILABLE_FW"
		if [ "$IS_DPU" == "true" ]; then
			break
		fi
	done
}

print_fw_info() {
	print_fw_current
	echo
	print_fw_available
}

print_versions() {
	PRINTED_HEADER="false"

	MFT=$(get_version_and_release mft)
	DOCA=$(get_version_and_release doca-prime-runtime)

	echo "Versions:"
	if [[ ! -z "$BUILD_ATF" ]]; then
		echo "- ATF $BUILD_ATF"
	fi
	if [[ ! -z "$BUILD_UEFI" ]]; then
		echo "- UEFI $BUILD_UEFI"
	fi
	if [[ ! -z "$MFT" ]]; then
		echo "- MFT ${MFT}"
	fi
	if [[ ! -z "$OFED" ]]; then
		echo "- DOCA Base (OFED) ${OFED}"
	fi
	if [[ ! -z "$DOCA" ]]; then
		echo "- DOCA ${DOCA}"
	fi
}

print_snap_packages() {
	MLNX_LIBSNAP=$(get_version mlnx-libsnap)
	MLNX_SNAP=$(get_version mlnx-snap)
	SPDK=$(get_version spdk)
	echo "SNAP3:"
	echo "- $MLNX_LIBSNAP"
	echo "- $MLNX_SNAP"
	echo "- $SPDK"
}

get_dependencies() {
	TARGET_PACKAGE=$1

	if [ -e /etc/debian_version ]; then
		apt-cache depends $TARGET_PACKAGE 2>/dev/null \
			| grep "Depends:" \
			| awk '{ print $2 }' \
			| xargs -n 1 echo
	else
		rpm -q doca-libs-devel --requires \
			| grep -v -iE ".so\." \
			| grep -v "<=" \
			| grep -v "=" \
			| grep -v "rpmlib" \
			| grep -v "/" \
			| sed 's/pkgconfig(//' \
			| tr -d ')' \
			| xargs -n 1 echo
	fi
}

print_doca_dependencies() {
	DOCA_PACKAGES=()
	if [ -e /etc/debian_version ]; then
		DOCA_PACKAGES=(
			"doca-runtime"
			"doca-sdk"
			"doca-tools"
		)
	else
		mapfile -t DOCA_PACKAGES < <(rpm -qa --qf "%{NAME}\n" | grep doca | xargs -n 1 echo)
	fi



	DEPENDENCIES=()

	for dm in ${DOCA_PACKAGES[@]}; do

		mapfile -t TMP_DEPENDENCIES < <(get_dependencies $dm)

		for dep in ${TMP_DEPENDENCIES[@]}; do
			DEPENDENCIES+=($dep)
		done

	done

	SORTED_DEPENDENCIES=($(echo "${DEPENDENCIES[@]}" | tr ' ' '\n' | sort -u | tr '\n' ' '))

	echo "DOCA Dependencies:"
	for dep in ${SORTED_DEPENDENCIES[@]}; do
		if [[ $dep = *"doca"* ]] || [[ $dep = *"rxp"* ]]; then
			continue
		fi
		echo "- $(get_version $dep)"
	done
}

get_version()
{
	OUTPUT=
	if [ -e /etc/debian_version ]; then
		OUTPUT=$(dpkg --list $1 2> /dev/null | grep -w "$1" | awk '{print $2,$3}')
	else
		if (rpm -q --quiet $1); then
			OUTPUT=$(rpm -q --queryformat="[%{NAME}-%{VERSION}-%{RELEASE}]" $1)
		fi
	fi
	if [ ! -n "$OUTPUT" ]; then
    	echo "$1 NA (package not found)"
		return
	fi
	echo $OUTPUT
}

get_version_and_release()
{
	if [ -e /etc/debian_version ]; then
		dpkg --list $1 | grep -w "$1" | awk '{print $3}'
	else
		if (rpm -q --quiet $1); then
			rpm -q --queryformat="[%{VERSION}-%{RELEASE}]" $1
		fi
	fi
}

print_ofed()
{
	OUTPUT=
	if [ -e /etc/debian_version ]; then
		OUTPUT=$(ofed_info 2>/dev/null | sed -n '/^-------------------$/ { :a; n; p; ba; }' | awk '{if ($2) printf "- %s %s\n", $2, $3}')
	else
		OUTPUT=$(ofed_info 2>/dev/null | sed -n '/^-------------------$/ { :a; n; p; ba; }' | xargs rpm -q --queryformat="[- %{NAME} %{VERSION}-%{RELEASE}]\n")
	fi
	if [ ! -n "$OUTPUT" ]; then
		echo "OFED: NA (ofed_info not found)"
		return
	fi
	echo "OFED:"
	echo "$OUTPUT"
}

print_uefiatf_info()
{
	echo "UEFI\ATF versions:"
	for mst_device in /dev/mst/mt41692_pciconf[0-9]; do
		echo "- mst_device: ${mst_device/"/dev/mst/"/}"
		MISOC_UEFI=$(mlxreg -d $mst_device --reg_name MISOC --get --index "type=1" 2> /dev/null)
		MISOC_ATF=$(mlxreg -d $mst_device --reg_name MISOC --get --index "type=0" 2> /dev/null)
		if [[ $? -ne 0 ]]; then
				echo "     UEFI Version: N\A"
				printf "     ATF Version: N\A\n\n"
				continue
		fi
		STR_UEFI_HEX="";for i in {0..16};do tmp=$(echo "$MISOC_UEFI" |egrep -i "version\[$i\]"|cut -d '|' -f2);STR_UEFI_HEX="$STR_UEFI_HEX""${tmp:9:10}" ;done;
		STR_ATF_HEX="";for i in {0..16};do tmp=$(echo "$MISOC_ATF" |egrep -i "version\[$i\]"|cut -d '|' -f2);STR_ATF_HEX="$STR_ATF_HEX""${tmp:9:10}" ;done;
		STR_UEFI=$(echo "$STR_UEFI_HEX"|xxd -r -p)
		STR_ATF=$(echo "$STR_ATF_HEX"|xxd -r -p)
		echo "     UEFI Version: $STR_UEFI" # ;echo "$STR_UEFI"|xxd -r -p;echo ""
		echo "     ATF Version: $STR_ATF" # ;echo "$STR_ATF"|xxd -r -p;echo ""
		echo ""
	done
}

cat << EOF
`print_versions`

EOF

cat << EOF
`print_uefiatf_info`

EOF

cat << EOF
`print_fw_info`

EOF

cat << EOF
`print_snap_packages`

EOF

if [ -e /etc/debian_version ]; then

cat << EOF
DOCA:
$(for doca in `dpkg --list | grep -E 'doca|rxp|dpacc|flexio|dpa-compiler' | awk '{print $2}' | sort -n`; do echo "- `get_version $doca`";done)
EOF
else
cat << EOF

DOCA:
$(for doca in $(rpm -qa | grep -E 'doca|rxp|dpacc|flexio|dpa-compiler' | sort -n); do echo "- `get_version $doca`";done)
EOF
fi

cat << EOF

`print_doca_dependencies`

EOF

cat << EOF

`print_ofed`
EOF
