#!/bin/bash

#
# Copyright(C) Advanced Micro Devices, Inc. All rights reserved.
#
# You may not use this software and documentation (if any) (collectively,
# the "Materials") except in compliance with the terms and conditions of
# the Software License Agreement included with the Materials or otherwise as
# set forth in writing and signed by you and an authorized signatory of AMD.
# If you do not have a copy of the Software License Agreement, contact your
# AMD representative for a copy.
#
# You agree that you will not reverse engineer or decompile the Materials,
# in whole or in part, except as allowed by applicable law.
#
# THE MATERIALS ARE DISTRIBUTED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR
# REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
#

#set -x
#
# FIRMWARE UPDATE UTILITY
#
cd "$PWD" > /dev/null 2>&1 || { echo "current directory '$PWD' not found"; exit 1 ; }

FWUPDATE_LOG_PATH=/tmp/ainic
mkdir -p $FWUPDATE_LOG_PATH

# Unknown boardid is 0
BOARD_ID_UNKNOWN="0x00000000"

CMDLINE="$*"

usage()
{
cat << EOF >&2
usage: fwupdate-nic action [options]
  action:
    -i "fwnames"    - Install package (requires -p and fwnames)
    -v              - Verify package file (required -p)
    -r              - Show running image name
    -s fwname       - Set startup image name
    -S              - Show startup image name
    -l              - List installed images
    -L              - List loaded (running) images

  mandatory:
    -B pcie_bdf     - PCIE-BDF in the format 0000:00:00.0

  options:
    -p pkgfile      - Package filename
    -R              - Force CPLD installation (ignore rev)
    -d              - Debug mode

  fwnames:
    altfw           - Alternate (non-running) main firmware (for -i)
    mainfwa         - Main firmware slot A (for -i or -s)
    mainfwb         - Main firmware slot B (for -i or -s)
    goldfw          - Golden recovery image (for -i or -s)
    cpld            - CPLD bitfile (for -i)
    boot0           - boot0 bootloader (for -i)
    all             - Synonym for "altfw". Implies -I

  help:
   --help           - Display this help message
EOF
exit 1
}

FW_REVOKEDB=$FWUPDATE_LOG_PATH/fwcompat-revoked.json
LOGDIR=""
LOGFILE=""
LOGMAXSZ=1048576
asic_name=unknown
BDF=""
ALT_FW=""

do_log_emit()
{
    (
    local dry="" sz
    if [ "$DRY_RUN" != "" ]; then
        dry=" dry-run"
    fi
    if [ ! -f $LOGFILE ]; then
        mkdir -p $LOGDIR
        : > $LOGFILE
    fi
    sz=`stat -c "%s" $LOGFILE`
    if [ $sz -gt $LOGMAXSZ ]; then
        [ -f ${LOGFILE}.1 ] && mv ${LOGFILE}.1 ${LOGFILE}.2
        mv $LOGFILE ${LOGFILE}.1
        : > $LOGFILE
    fi
    echo "$1 [$BOOTNUMBER `date '+%Y-%m-%d %H:%M:%S.000000'`] fwupdate[$$]:$dry $2" >> $LOGFILE
    ) 2>/dev/null
}

log_emit()
{
    local pref=$1
    shift
    do_log_emit $pref "$*" || true
}

log_info()
{
    log_emit I "$*"
}

log_debug()
{
    log_emit D "$*"
}

log_error()
{
    local emit=0

    if [ "X-$1" = "X--e" ]; then
        emit=1
        shift
    fi
    log_emit E "$*"
    if [ $emit -ne 0 ]; then
        echo "$*" >&2
    fi
}

fatal()
{
    log_error -e "FATAL: $1"
    exit 1
}

fatal_err()
{
    log_error -e "FATAL: $1: `head -1 $ERR`"
    exit 1
}

log_eval()
{
    local x res
    local want_echo=1

    if [ "X-$1" = "X--n" ]; then
        want_echo=0
        shift
    fi
    set -o pipefail
    eval "$@" 2>&1 |
    while read x; do
        log_info "$x"
        if [ $want_echo -ne 0 ]; then
            echo $x
        fi
    done
    res=$?
    set +o pipefail
    return $res
}

run_flash_update_client()
{
    local cmd=$1 args=$2

    bash -c "flash_update_client $* $BDF" &
    child_pid=$!
    wait $child_pid

    child_exit_status=$?
    if [ $child_exit_status -ne 0 ]; then
        log_error "flash_update_client failed with args $* exit :$child_exit_status"
        exit 1
    fi
}

check_package_sanity()
{
    local pkg=$1

    # Check tar file sanity
    tar xfO $pkg &> /dev/null || fatal "Invalid/corrupted package file"

    # Check that MANIFEST file is present in the package tar file.
    tar xfO $pkg MANIFEST &> /dev/null || \
        fatal "Invalid package file. Package file does not contain MANIFEST"
}


extract_manifest()
{
    local llen
    local pkg=$1 manifile=$2

    tar xfO $pkg MANIFEST > $manifile 2>/dev/null
    if [ $? -ne 0 ]; then
        fatal "unable to extract MANIFEST"
    fi

    llen=`mani_length .asic_compat_list`
    for i in `seq 1 $llen`; do
        pkg_asic=`mani_value ".asic_compat_list[$i - 1]"`
        mkdir -p $D/$pkg_asic

        name=`mani_opt_value ".asic_manifests.$pkg_asic.name"`
        if [ "$name" = "null" ]; then
            fatal "package corrupted,$pkg_asic name not found"
        fi
        algo=`mani_opt_value ".asic_manifests.$pkg_asic.verify.algorithm"`
        if [ "$algo" = "null" ]; then
            fatal "package corrupted,$name hash algo not found"
        fi
        check_hash_algo "$algo"
        want_hash=`mani_opt_value ".asic_manifests.$pkg_asic.verify.hash"`
        if [ "$want_hash" = "null" ]; then
            fatal "package corrupted,$name hash not found"
        fi
        got_hash=`tar xfO $pkg $name | hash $algo`
        if [ "$got_hash" != "$want_hash" ]; then
            fatal "package corrupted,$name hash verification failed"
        fi

        tar xfO $pkg $name > $D/$pkg_asic/MANIFEST 2>/dev/null
        if [ $? -ne 0 ]; then
            fatal "unable to extract $name"
        fi
    done
}

# asic type
get_asic_name()
{
    local fname=$BDF_DIR/card_info.txt
    # Below command will fetch asic type as well
    bash -c "flash_update_client --get-card-info $BDF" &
    child_pid=$!
    wait $child_pid

    child_exit_status=$?
    if [ $child_exit_status -ne 0 ]; then
        fatal "unable to connect AINIC - ipc error"
    fi
    if [ -e "$fname" ]; then
        echo `jq -r .asic $fname`
    fi
}

is_fwupdate_for_ainic()
{
    if [ $asic_name == salina ]; then
        return 0
    fi
    return 1
}

# Globals
PKG=""
DRY_RUN=""
INSTALL_IMAGES=""
WANT_STARTUP=""
VERIFY_FWNAME=""
SKIP_BITFILES=0
IGNORE_MIN_REV_CHECK=0
CPLD_AT_LATEST_VERSION=2
PACKAGE_VERSION=rev2
FWLIST_NAMES=""
INSTALL_NAMES=""
SW_VERSION=""
D=""
KEYS_DIR=""
ENG_PUB_KEY=""
PROD_PUB_KEY=""
LCS_STATE=prod
HAVE_BOARD_CONFIG=1
HAVE_OPENSSL=1
OPENSSL=openssl
BL1_AR_VERSION=""
BL1_AR_NV_COUNTER=""
D=/tmp/update$$
MANIFEST=$D/MANIFEST
MANIFEST_SIG=$D/MANIFEST.sig
METASIGFILE=$D/metasig
SIGBINFILE=$D/sigbin
ERR=$D/err.out
TMPFILE=$D/tmpfile
DEFAULT_FIRMWARE_CONFIG_PATH=""
ASIC_MANIFEST=""
COMMON_LOCK_FILE="$FWUPDATE_LOG_PATH/common.lock"
LOCKFILE="$FWUPDATE_LOG_PATH/lock"
CURRENT_PID=$$
BOARD_HW_FUSE=0
BOARD_SECURE_ENABLE=0
BOARD_SECURE_BOOT=0
BL31_SUPPORT=0
# below variable value should match with the
# enum "FWUPDATE_ERR_SEC_FW_NOT_RUNNING"
FWUPDATE_ERR_SEC_FW_NOT_RUNNING=99

mkdir -p $D

finish() {
    declare exit_code=$?
    (
        flock 200
        sed -i "/$CURRENT_PID/d" "$LOCKFILE"
    ) 200>$COMMON_LOCK_FILE
    rm -rf $D
    exit "$exit_code"
}

handle_error() {
    declare exit_code=$?
    if [ $exit_code == $CPLD_AT_LATEST_VERSION ]; then
        exit $CPLD_AT_LATEST_VERSION;
    fi
    exit -1
}

trap 'handle_error $LINENO' ERR
trap finish EXIT
trap '' INT QUIT PIPE HUP TSTP SIGTERM

take_lock()
{
    (
        flock 200

        # Create a temporary file to store the updated contents
        TEMP_LOCK_FILE="$FOLDER/temp.lock"
        if [ ! -e "$TEMP_LOCK_FILE" ]; then
            touch $TEMP_LOCK_FILE
        fi

        # Read the lock file line by line
        while IFS=" " read -r device pid; do
        # Check if the process with the PID is running
        if ! kill -0 "$pid" 2>/dev/null; then
            # If the PID is not running, don't write "device pid" to the temp file
            continue
        fi
        # If the process is running, keep the line
        echo "$device $pid" >> "$TEMP_LOCK_FILE"
        done < "$LOCKFILE"

        # Replace the original lock file with the updated file
        mv "$TEMP_LOCK_FILE" "$LOCKFILE"
    ) 200>$COMMON_LOCK_FILE

    if grep -q "$BDF_DIR" "$LOCKFILE"; then
        if [[ "$action" == "LIST_FW" || "$action" == "LIST_LOADED" ]]; then
            fatal "Firmware show already in progress for $BDF"
        else
            fatal "Firmware update already in progress for $BDF"
        fi
        exit 1
    fi

    (
        flock 200
        echo "$BDF_DIR $CURRENT_PID" >>$LOCKFILE
        log_info "Lock taken $BDF_DIR $CURRENT_PID"
    ) 200>$COMMON_LOCK_FILE
}

ainic_dpu_board_info_check()
{
    log_info "BOARD_COMPAT_BOARDID: $BOARD_COMPAT_BOARDID"
    # Verify that the package is asic compatible
    check_asic_compat

    # Check compatibility of the board
    check_board_compat
    check_board_revoked
}

get_required_field()
{
    local file=$1 key=$2 errmsg=$3
    local val

    val=$(jq -r $key < $file 2>/dev/null)
    if [ $? -ne 0 -o "$val" = "null" ]; then
        log_error -e "$errmsg not found"
        return 1
    fi
    echo $val
}

verify_required_field()
{
    local file=$1 key=$2 want_val=$3 errmsg=$4
    local got_val

    got_val=$(get_required_field $file $key "$errmsg") || exit 1
    if [ "$got_val" != "$want_val" ]; then
        log_error -e "invalid $errmsg"
        exit 1
    fi
}

key_name_to_file()
{
    local key_name=$1
    local key_file

    case "$key_name" in
    eng)
        key_file=$ENG_PUB_KEY
        ;;
    prod)
        key_file=$PROD_PUB_KEY
        ;;
    *)
        log_error -e "invalid key name"
        return 1
        ;;
    esac

    if [ ! -f $key_file ]; then
        log_error -e "key $key_name not found"
        return 1
    fi
    echo $key_file
}

sig_to_sigbin()
{
    local sig=$1  sigbin=$2

    echo $sig | $OPENSSL base64 -d > $sigbin 2>/dev/null
    if [ $? -ne 0 ]; then
        fatal "Unable to generate sigbin file $sigbin"
    fi
}

do_verify_file_signature()
{
    local file=$1 sigfile=$2
    local ver algo padding key_name sig keyfile

    # if openssl is not available, treat as fail
    if [ $HAVE_OPENSSL -eq 0 ]; then
        log_error -e "openssl not available"
        return 1
    fi

    # version must be 1
    # algorithm must be sha512,rsa4096
    # padding must be pss
    verify_required_field $sigfile .version 1 "signature version"
    verify_required_field $sigfile .algorithm "sha512,rsa4096" "signature algorithm"
    verify_required_field $sigfile .padding pss "signature padding"

    # key_name must be eng or prod
    key_name=$(get_required_field $sigfile .key_name "signature key name") || return 1
    if [ "$key_name" != "eng" -a "$key_name" != "prod" ]; then
        log_error -e "invalid metadata signature key name"
        return 1
    fi
    keyfile=$(key_name_to_file $key_name) || return 1

    # signature must be present
    sig=$(get_required_field $sigfile .signature signature) || return 1
    sig_to_sigbin $sig $SIGBINFILE

    if ! $OPENSSL dgst -verify $keyfile -signature $SIGBINFILE -sha512 -sigopt rsa_padding_mode:pss $file >/dev/null 2>&1; then
        log_error -e "signature verification failed"
        exit 1
    fi
    # return the key name
    echo $key_name
}

# Verify a file's signature, making sure the key is valid for the LCS
verify_file_signature()
{
    local file=$1 sigfile=$2
    local key_name

    key_name=$(do_verify_file_signature $file $sigfile) || return 1

    if [ $SIGCHK_REQUIRED -eq 1 -a "$key_name" != "prod" ]; then
        log_error -e "PROD signature required"
        return 1
    fi
    log_error -r "verify file signature done "
    echo $key_name
}

extract_package_signature()
{
    local pkg=$1 ofile=$2

    tar xfO $pkg MANIFEST.sig &> /dev/null || return 1

    tar xfO $pkg MANIFEST.sig > $ofile 2>/dev/null
    if [ $? -ne 0 ]; then
        fatal "Unable to extract signature (MANIFEST.sig)"
    fi
}

# Check the package signature
check_package_signature()
{
    local s

    extract_package_signature $PKG $MANIFEST_SIG

    if [ $? -ne 0 ]; then
        # No signature
        log_info "Package signature not present; LCS $LCS_STATE"
        if [ $SIGCHK_REQUIRED -eq 1 ]; then
            fatal "Required package signature not found"
        fi
        return 0
    fi
    log_info "Package signature present; LCS $LCS_STATE"

    if [ $SIGCHK_SUPPORTED -eq 0 ]; then
        log_info "Signature verification not supported: skipping"
        return 0
    fi

    log_info "Verifying package signature"
    s=$(verify_file_signature $MANIFEST $MANIFEST_SIG) || exit 1
}

set_sigchk_supported()
{
    if [ ! -d $KEYS_DIR ] || [ $HAVE_OPENSSL -eq 0 ]; then
        SIGCHK_SUPPORTED=0
    else
        SIGCHK_SUPPORTED=1
    fi
}

set_prod_pub_key_file()
{
    local keyname
    local this_asic=$asic_name

    keyname=$BOARD_KEY_NAME

    if [[ "$keyname" = "none" ]]; then
        return
    fi

    # override the default prod public key only for open cards.
    if [ "$LCS_STATE" = "open" ]; then
        PROD_PUB_KEY=$KEYS_DIR/${keyname}_key-pub.pem
    fi
}

set_sigchk_required()
{
    local sc

    sc=$BOARD_SIG_CHK

    if [ $sc -eq 1 -o  "$LCS_STATE" != "open" ]; then
        SIGCHK_REQUIRED=1
    else
        SIGCHK_REQUIRED=0
    fi
}

ainic_dpu_fwupdate()
{
    A35_RUNNING_FW=`ainic_dpu_get_firmware "running"`

    if [ $A35_RUNNING_FW == "invalid" ]; then
        fatal "unable to connect AINIC - ipc error"
        return
    fi

    # Do we have openssl?
    which $OPENSSL >/dev/null 2>&1 || HAVE_OPENSSL=0
    if [ "$BOARD_COMPAT_BOARDID" = "$BOARD_ID_UNKNOWN" ]; then
       HAVE_BOARD_CONFIG=0
    fi

    if [[ "$BOARD_SECURE_ENABLE" == "1" ||  "$BOARD_SECURE_BOOT" == "1" ]]; then
        if [ $BOARD_SECURE_BOOT == 1 ]; then
            LCS_STATE=`get_lifecycle_next_state`
            if [ -z "$LCS_STATE" ]; then
                LCS_STATE=open
            fi
        else
            LCS_STATE=open
        fi
        set_sigchk_supported
        set_sigchk_required
        set_prod_pub_key_file
    fi

    log_info "sigchk reqd $SIGCHK_REQUIRED sigchk support $SIGCHK_SUPPORTED lcs state $LCS_STATE"
    # Check the package, if supplied
    if [ $need_pkg -ne 0 ]; then
        check_package_sanity $PKG
        extract_manifest $PKG $MANIFEST

        if [[ "$BOARD_SECURE_ENABLE" == "1" ||  "$BOARD_SECURE_BOOT" == "1" ]]; then
            # Verify the package signature
            check_package_signature
        fi
        # Extract the resources
        s=`mani_opt_value .installer.resources`
        if [ "$s" != "null" ]; then
            extract_resources
        fi

        if tar -tf "$PKG" | grep -q "fwcompat-revoked.json"; then
            tar xfO $PKG fwcompat-revoked.json > $FW_REVOKEDB 2>/dev/null
        fi

        # Get board id and check package compatible
        if [ $asic_name == salina ]; then
            ainic_dpu_board_info_check
        fi
    fi

    # Set package strategy
    set_package_strategy $need_pkg

    case "$action" in
    SET_STARTUP_FW)
        ainic_dpu_set_startup_image
        ;;

    VERIFY_PACKAGE)
        verify_package
        ;;

    INSTALL)
        ainic_dpu_install_images
        if [[ -n "$WANT_STARTUP" ]]; then
            ainic_dpu_set_startup_image
        fi
        log_info "END"
        ;;

    GET_RUNNING_FW)
        echo "$A35_RUNNING_FW"
        ;;

    SHOW_STARTUP_FW)
        a35_startup_fw=`ainic_dpu_get_firmware "startup"`
        echo "$a35_startup_fw"
        ;;

    LIST_FW)
        list_firmware
        ;;

    LIST_LOADED)
        ainic_dpu_list_loaded_firmware
        ;;

    *)
    ;;
    esac
    return
}

main()
{
    local action need_pkg opt

    set +o pipefail

    action=NONE
    need_pkg=0

    if [[ "$1" == "--help" ]]; then
        usage
    fi

    while getopts "i:vV:rs:SlLp:B:dDICxRW:T:FXA:" opt; do
        case "$opt" in
        i) action=INSTALL; need_pkg=1; INSTALL_IMAGES=$OPTARG; ;;
        v) action=VERIFY_PACKAGE; need_pkg=1; ;;
        r) action=GET_RUNNING_FW; ;;
        s) action=SET_STARTUP_FW; WANT_STARTUP=$OPTARG; ;;
        S) action=SHOW_STARTUP_FW; ;;
        l) action=LIST_FW; ;;
        L) action=LIST_LOADED; ;;
        p) PKG=$OPTARG; action=INSTALL; need_pkg=1;
           if [[ $INSTALL_IMAGES == "" ]]; then
               INSTALL_IMAGES=all;
           fi; ;;
        B) BDF=$OPTARG; ;;
        R) IGNORE_MIN_REV_CHECK=1; ;;
        d) set -x; ;;
        *) usage; ;;
        esac
    done

    if [ -z "$BDF" ]; then
        BDF="$DSC_BDF"
        if [ -z "$BDF" ]; then
            fatal "pcie-bdf is null"
            exit 1
        fi
    fi

    if [[ ! "$BDF" =~ ^[0-9A-Fa-f:.]*$ ]]; then
        fatal "pcie-bdf doesnt have valid values"
        exit 1
    fi
    echo "$BDF" | grep -Pq '^[0]{4}:[0-9a-fA-F]{2}:[0-9a-fA-F]{2}\.[0-9]$'
    if [ $? -eq 1 ]; then
        fatal "invalid BDF format"
        exit 1
    fi

    BDF_DIR=$FWUPDATE_LOG_PATH/$BDF
    mkdir -p $BDF_DIR

    if [ ! -e "$LOCKFILE" ]; then
        touch $LOCKFILE
    fi

   # get asic name after update the BDF info
    asic_name=`get_asic_name`
    if [[ "$asic_name" != "salina" ]]; then
        fatal "unable to connect AINIC - ipc error"
        exit 1
    fi

    LOGDIR=$FWUPDATE_LOG_PATH/$BDF
    LOGFILE=$LOGDIR/fwupdate.log

    ASIC_MANIFEST=$D/$asic_name/MANIFEST
    DEFAULT_FIRMWARE_CONFIG_PATH="$asic_name/firmware_config_default.dtb"
    KEYS_DIR=/etc/amd/ainic/$asic_name/fwkeys
    ENG_PUB_KEY=$KEYS_DIR/eng_key-pub.pem
    PROD_PUB_KEY=$KEYS_DIR/prod_key-pub.pem

    SW_VERSION=$(jq -r ".sw_version" $BDF_DIR/card_info.txt 2>/dev/null)
    BOARD_COMPAT_BOARDID=$(jq -r ".board_id" $BDF_DIR/card_info.txt 2>/dev/null)
    BOARD_SIG_CHK=$(jq -r ".board_sigchk" $BDF_DIR/card_info.txt 2>/dev/null)
    BOARD_KEY_NAME=$(jq -r ".board_keyname" $BDF_DIR/card_info.txt 2>/dev/null)
    BOARD_HW_FUSE=$(jq -r ".board_hw_fuse" $BDF_DIR/card_info.txt 2>/dev/null)
    BOARD_SECURE_ENABLE=$(jq -r ".board_secure_enable" $BDF_DIR/card_info.txt 2>/dev/null)
    BOARD_SECURE_BOOT=$(jq -r ".board_secure_boot" $BDF_DIR/card_info.txt 2>/dev/null)
    log_info "VER:$SW_VERSION BOARD_COMPAT_ID:$BOARD_COMPAT_BOARDID"
    log_info "BOARD_SIG_CHK:$BOARD_SIG_CHK BOARD_KEY_NAME:$BOARD_KEY_NAME"
    log_info "BOARD_SECURE_ENABLE:$BOARD_SECURE_ENABLE BOARD_SECURE_BOOT:$BOARD_SECURE_BOOT"

    case "$INSTALL_IMAGES" in
       mainfwa)
         WANT_STARTUP=mainfwa
         ;;
       mainfwb)
         WANT_STARTUP=mainfwb
         ;;
       altfw|all)
         WANT_STARTUP=altfw
         ;;
     *)
    esac

    if [ "$INSTALL_IMAGES" == "goldfw" ]; then
        if [[ "$PKG" != *"gold"* ]]; then
            fatal "Invalid image \"$PKG\" for goldfw update"
            return
        fi
    fi

    take_lock

    if is_fwupdate_for_ainic; then
        ainic_dpu_fwupdate $@
        return
    fi
}

# If the package has a .asic_compat, and we know our asic type,
# and they mismatch, then reject the package
check_asic_compat()
{
    local llen i pkg_asic

    # Check this_asic against those listed in the optional asic_compat_list
    llen=`mani_length .asic_compat_list`
    for i in `seq 1 $llen`; do
        pkg_asic=`mani_value ".asic_compat_list[$i - 1]"`
        if [ "$asic_name" = "$pkg_asic" ]; then
            return 0
        fi
    done

    # Check this_asic against the optional, single asic_compat
    pkg_asic=`mani_opt_value .asic_compat`
    if [ "$pkg_asic" != "null" ]; then
        if [ "$asic_name" != "$pkg_asic" ]; then
            fatal "Package file incompatible with this ASIC"
        fi
    fi
}

#Package compatibility check: Look for the BOARD_COMPAT_BOARDID in the MANIFEST
compat_match_boardid()
{
    local s l i

    if [ "$BOARD_COMPAT_BOARDID" = "$BOARD_ID_UNKNOWN" ]; then
        log_info "compat_match_boardid: boardid unavailable; compatibility assumed"
        return 0
    fi

    s=`mani_opt_value .package_compat.boardid`
    if [ "$s" = "null" ]; then
        log_error "exp_compat: .package_compat.boardid not found"
        return 1;
    fi
    l=`mani_length ".package_compat.boardid"`
    for i in `seq $l`; do
        if [ "`mani_value ".package_compat.boardid[$i - 1]"`" = "$BOARD_COMPAT_BOARDID" ]; then
            # Match found; compatible
            log_info "exp_compat: boardid: $BOARD_COMPAT_BOARDID found"
            return 0
        fi
    done
    # No match; incompatible
    log_info "exp_compat: boardid: $BOARD_COMPAT_BOARDID not found"
    return 1
}

# Check the package for compatibility with this board.
# Use a MANIFEST package_compat node, if present, or a legacy database
# if not.
check_board_compat()
{
    policy=`mani_opt_value .package_compat.board_policy`
    if [ "$policy" = "accept" ]; then
        log_info "check_board_compat: policy accept"
        policy=`mani_opt_value .package_compat.board_policy2`
        if [ "$policy" = "null" ]; then
            return
        fi
    fi
    case "$policy" in
    accept)
        # Universal package; no board-type checks
        # Can be used to defer to the embedded installer in the future
        log_info "check_board_compat: policy accept"
        return
        ;;
    match-boardid)
        # Match the boardid against the MANIFEST boardid array
        if compat_match_boardid; then
            return
        fi
        ;;
    *)
        log_error "check_board_compat: unknown policy $policy"
        ;;
    esac
    fatal "Package file incompatible with this board"
}

# Parse a software_version string
# Formats are:
#       1.4.0-E-50  -> tree=1.4.0-E, bld=50
#       1.4-E-50    -> tree=1.4-E,   bld=50
#       1.4.0-50    -> tree=1.4.0,   bld=50
#
# software_version strings can also have suffixes, e.g:
#       1.4.0-E-50
#       1.4.0-E-50-dirty
#       1.4.0-E-50-1-g80c6c65e1
#       1.4.0-E-50-1-g80c6c65e1-dirty
rel_to_tree_bld()
{
    local tag=$1
    local tree bld

    set +e

    # 1.5.1-C-5
    tree=$(expr "$tag" : '\([0-9]\+.[0-9]\+.[0-9]\+-[A-Z]\)-[0-9]\+')
    bld=$(expr "$tag" : '[0-9]\+.[0-9]\+.[0-9]\+-[A-Z]-\([0-9]\+\)')
    if [ -z "$tree" -o -z "$bld" ]; then
        # 1.5-C-5
        tree=$(expr "$tag" : '\([0-9]\+.[0-9]\+-[A-Z]\)-[0-9]\+')
        bld=$(expr "$tag" : '[0-9]\+.[0-9]\+-[A-Z]-\([0-9]\+\)')
        if [ -z "$tree" -o -z "$bld" ]; then
            # 1.5.1-5
            tree=$(expr "$tag" : '\([0-9]\+.[0-9]\+.[0-9]\+\)-[0-9]\+')
            bld=$(expr "$tag" : '[0-9]\+.[0-9]\+.[0-9]\+-\([0-9]\+\)')
        fi
    fi
    set -e
    echo "$tree $bld"
}

# Check for image revocation, which can happen if a problem is found
# with an image that claims compatibility with a board.
# The software release is split into (tree, build), e.g. 1.5.1-E, 50
# and the revoked db maps each BOARD_COMPAT_BOARDID and tree to an array of revoked
# build ranges, e.g. { "min": 10, "max": 20 }
#
# Assume compatibility if the deicison logic breaks down due to missing files
# or parsing errors. Those are more likely than an actually incompatible image.
#
# The revoke check only logs on revocation.
check_board_revoked()
{
    local sw_vers tree bld rev_len i rmin rmax

    if [ ! -f $FW_REVOKEDB ]; then
        # No DB; assume ok
        return
    fi

    sw_vers=$(mani_opt_value .software_version)
    if [ "$sw_vers" = "null" ]; then
        # No software_version; assume ok
        return
    fi

    set -- $(rel_to_tree_bld "$sw_vers")
    tree=$1; bld=$2
    if [ "X-$bld" = "X-" ]; then
        # mystery sw_vers; assume ok
        return
    fi

    # We think have reliable info; make the real determination
    rev_len=$(jq -r ."\"$BOARD_COMPAT_BOARDID\".\"$tree\" | length" $FW_REVOKEDB)
    for i in $(seq $rev_len); do
        rmin=$(jq -r ."\"$BOARD_COMPAT_BOARDID\".\"$tree\"[$i - 1].min" $FW_REVOKEDB)
        rmax=$(jq -r ."\"$BOARD_COMPAT_BOARDID\".\"$tree\"[$i - 1].max" $FW_REVOKEDB)
        [ "$rmin" = "null" -o "$rmax" = "null" ] && continue
        if [ $bld -ge $rmin -a $bld -le $rmax ]; then
            log_error "check_revoked: tree $tree, build $bld revoked for $BOARD_COMPAT_BOARDID ($bld in range $rmin...$rmax)"
            fatal "Package file incompatible with this board"
        fi
    done
}

set_package_strategy()
{
    if [ $need_pkg -ne 0 ]; then
	s=`mani_opt_value .package_version`
	case "$s" in
        null) PACKAGE_VERSION=rev2; ;;
        2)    PACKAGE_VERSION=rev2; ;;
        3)    PACKAGE_VERSION=rev3; ;;
        *)    log_error "Unknown package type"; return 1; ;;
        esac
    else
        if [ "$BOARD_SECURE_BOOT" == "1" ]; then
	       # If card booted with secure image, set the strategy to rev3
            PACKAGE_VERSION=rev3
        fi
    fi
    # TODO check bl1slot to decide rev3 or rev2
    fwname_to_fwobj=${PACKAGE_VERSION}_fwname_to_fwobj
    fwobj_to_fwtype=${PACKAGE_VERSION}_fwobj_to_fwtype
    fwtype_to_files=${PACKAGE_VERSION}_fwtype_to_files
    file_to_target=${PACKAGE_VERSION}_file_to_target
    is_file_valid_for_fwname=${PACKAGE_VERSION}_is_file_valid_for_fwname
    fwname_to_inv_file=${PACKAGE_VERSION}_fwname_to_inv_file
    check_install_list=${PACKAGE_VERSION}_check_install_list
    FWLIST_NAMES=`eval echo \\$${PACKAGE_VERSION}_FWLIST_NAMES`
}

#------------------------------------------------------------------------------
# rev2 support

rev2_FWLIST_NAMES="boot0 mainfwa mainfwb goldfw dtb dtb-b fw_cfg fw_cfg-b"
# Map a specific firmware name to an fwobj in the MANIFEST
rev2_fwname_to_fwobj()
{
    case "$1" in
    mainfwa)        echo extos; ;;
    mainfwb)        echo extos; ;;
    goldfw)         echo gold; ;;
    boot0)          echo boot0; ;;
    dtb-b)          echo device_config ;;
    dtb)            echo device_config ;;
    fw_cfg)         echo fw_cfg ;;
    fw_cfg-b)       echo fw_cfg ;;
    cpld)           echo cpld ;;
    *)          fatal "rev2_fwname_to_fwobj: unknown fwname $1"
    esac
}

# Map an fwobj to an fwtype
rev2_fwobj_to_fwtype()
{
    fwobj=$1
    case "$fwobj" in
    boot0)          echo "boot0"; ;;
    extos)          echo "extos"; ;;
    gold)           echo "goldlinux"; ;;
    device_config)  echo "device_config"; ;;
    fw_cfg)         echo "fw_cfg"; ;;
    fw_cfgb)        echo fw_cfgb ;;
    *)          fatal "rev2_fwobj_to_fwtype: unknown fwobj $fwobj"
    esac
}

# Return the component files of a firmware type, in the order they are
# installed (mtd last).  The last object will be the one invalidated before
# the others are installed, and thus validates the whole install.
rev2_fwtype_to_files()
{
    fwtype=$1
    case "$fwtype" in
    boot0)      echo "image"; ;;
    extos)      echo "kernel_fit uboota ubootb"; ;;
    goldlinux)  echo "kernel_fit golduboot"; ;;
    device_config) echo "device_config"; ;;
    fw_cfg)     echo "fw_cfg"; ;;
    fw_cfgb)    echo fw_cfgb ;;
    *)          fatal "rev2_fwtype_to_files: unknown fwtype $fwtype"; ;;
    esac
}

rev2_fwname_to_inv_file()
{
    local fwname=$1 fwtype=$2

    case "$fwname" in
    boot0)          echo "none"; ;;
    mainfwa)        echo "uboota"; ;;
    mainfwb)        echo "ubootb"; ;;
    goldfw)         echo "golduboot"; ;;
    *)          fatal "rev2_fwname_to_inv_file: unknown $fwname"; ;;
    esac
}

# Omit uboota being considered valid for mainfwb, or ubootb for mainfwa.
rev2_is_file_valid_for_fwname()
{
    local fwname=$1 f=$2
    [ "$fwname" = "mainfwa" -a "$f" = "ubootb" ] && return 1
    [ "$fwname" = "mainfwb" -a "$f" = "uboota" ] && return 1
    return 0
}

rev2_check_install_list()
{
    local fwname

    for fwname in $INSTALL_IMAGES; do
        case $fwname in
        mainfwa|mainfwb|extosa|extosb)
            if [ "$A35_RUNNING_FW" = "$fwname" ]; then
                fatal "rev2_check_install_list: Cannot install over running firmware"
            fi
            ;;
        altfw|altextos|goldfw|diagfw|boot0|ubootpack|cpld|extdiag|extosgoldfw)
            ;;
        all)
            if is_fwupdate_for_ainic; then
                INSTALL_IMAGES="altfw altdevcfg altfwcfg boot0 cpld"
                pkg_fwobj=`mani_keys .firmware`
                if [[ "$pkg_fwobj" == *"gold"* ]]; then
                    INSTALL_IMAGES="goldfw"
                    WANT_STARTUP=""
                fi
            fi
            ;;
        dtb)
            if ! is_fwupdate_for_ainic; then
                fatal "rev2_check_install_list: Unknown firmware name $fwname"
            fi

            if [ $A35_RUNNING_FW == mainfwa ]; then
                 INSTALL_IMAGES=dtb-b
            elif [ $A35_RUNNING_FW == mainfwb  -o  $A35_RUNNING_FW == goldfw ]; then
                 INSTALL_IMAGES=dtb
            fi
            ;;
        fw_cfg)
            if ! is_fwupdate_for_ainic; then
                fatal "rev2_check_install_list: Unknown firmware name $fwname"
            fi

            if [ $A35_RUNNING_FW == mainfwa ]; then
                 INSTALL_IMAGES=fw_cfg-b
            elif [ $A35_RUNNING_FW == mainfwb  -o  $A35_RUNNING_FW == goldfw ]; then
                 INSTALL_IMAGES=fw_cfg
            fi
            ;;
        *)
            fatal "rev2_check_install_list: Unknown firmware name $fwname"
            ;;
        esac
    done
}

#------------------------------------------------------------------------------
# rev3 support

rev3_FWLIST_NAMES="softrom-0 pentrustfw-0 bl1-0 mainfwa dtb fw_cfg softrom-1 pentrustfw-1 bl1-1 mainfwb dtb-b fw_cfg-b goldfw"

# Map a specific firmware name to an fwobj in the MANIFEST
rev3_fwname_to_fwobj()
{
    case "$1" in
    pentrustfw)     echo pentrustfw; ;;
    pentrustfw-0)   echo pentrustfw; ;;
    pentrustfw-1)   echo pentrustfw; ;;
    bl1)            echo bl1; ;;
    bl1-0)          echo bl1; ;;
    bl1-1)          echo bl1; ;;
    softrom-0)      echo pentrustsoftrom; ;;
    softrom-1)      echo pentrustsoftrom; ;;
    goldfw)         echo goldfip; ;;
    mainfwa)        echo fip; ;;
    mainfwb)        echo fip; ;;
    dtb-b)          echo device_config ;;
    dtb)            echo device_config ;;
    fw_cfg)         echo fw_cfg ;;
    fw_cfg-b)       echo fw_cfg ;;
    cpld)           echo cpld ;;
    *)              fatal "rev3_fwname_to_fwobj: unknown fwname $1"
    esac
}

# Map an fwobj to an fwtype
rev3_fwobj_to_fwtype()
{
    fwobj=$1
    case "$fwobj" in
    pentrustsoftrom)   echo pentrustsoftrom; ;;
    pentrustfw) echo "pentrustfw"; ;;
    bl1)        echo "bl1"; ;;
    fip)        echo "fip"; ;;
    goldfip)    echo "goldfip"; ;;
    device_config)  echo "device_config"; ;;
    fw_cfg)     echo "fw_cfg"; ;;
    *)          fatal "rev3_fwobj_to_fwtype: unknown fwobj $fwobj"
    esac
}

# Return the component files of a firmware type, in the order they are
# installed (mtd last).  The last object will be the one invalidated before
# the others are installed, and thus validates the whole install.
rev3_fwtype_to_files()
{
    fwtype=$1
    case "$fwtype" in
    pentrustsoftrom) echo "ptsoftrom"; ;;
    pentrustfw) echo "ptimage"; ;;
    bl1)        echo "ptimage"; ;;
    fip)        echo "fip"; ;;
    goldfip)    echo "goldfip"; ;;
    device_config) echo "device_config"; ;;
    fw_cfg)     echo "fw_cfg"; ;;
    *)          fatal "rev3_fwtype_to_files: unknown fwtype $fwtype"; ;;
    esac
}

# No filtering required
rev3_is_file_valid_for_fwname()
{
    return 0
}

rev3_check_install_list()
{
    local fwname

    for fwname in $INSTALL_IMAGES; do
        case $fwname in
        mainfwa|mainfwb)
            if [ $A35_RUNNING_FW = $fwname ]; then
                fatal "rev3_check_install_list: Cannot install over running firmware"
            fi
            ;;
        pentrustfw-0|pentrustfw-1|softrom-0|softrom-1|bl1-0|bl1-1|altfw|boot0|cpld|pentrustfw|bl1)
            ;;
        all)
            INSTALL_IMAGES="bl1-0 bl1-1 pentrustfw-0 pentrustfw-1 altfw altdevcfg altfwcfg cpld"
            pkg_fwobj=`mani_keys .firmware`
            if [[ "$pkg_fwobj" == *"gold"* ]]; then
                INSTALL_IMAGES="bl1-0 bl1-1 pentrustfw-0 pentrustfw-1 goldfw"
                WANT_STARTUP=""
            fi
            ;;
        softrom)
            INSTALL_IMAGES="softrom-0 softrom-1"
            WANT_STARTUP=""
            ;;
        goldfw)
            INSTALL_IMAGES="bl1-0 bl1-1 pentrustfw-0 pentrustfw-1 goldfw"
            WANT_STARTUP=""
            ;;
        *)
            fatal "rev3_check_install_list: Unknown firmware name $fwname"
            ;;
        esac
    done
}

dry_run()
{
    echo "(no action)"
}

alt_fw_of()
{
    local fwname=$1

    case "$fwname" in
    mainfwa)
        echo mainfwb
        ;;
    mainfwb|goldfw)
        echo mainfwa
        ;;
    *)
        fatal "alt_fw_of: Unknown fwname $fwname"
        ;;
    esac
}

alt_devcfg_of()
{
    local fwname=$1

    case "$fwname" in
    mainfwa)
        echo dtb-b
        ;;
    mainfwb|goldfw)
        echo dtb
        ;;
    *)
        fatal "alt_devcfg_of: Unknown fwname $fwname"
        ;;
    esac
}

alt_fwcfg_of()
{
    local fwname=$1

    case "$fwname" in
    mainfwa)
        echo fw_cfg-b
        ;;
    mainfwb|goldfw)
        echo fw_cfg
        ;;
    *)
        fatal "alt_fwcfg_of: Unknown fwname $fwname"
        ;;
    esac
}

extract_resources()
{
    local name algo want_hash got_hash

    name=`mani_opt_value ".installer.resources.name"`
    if [ "$name" = "null" ]; then
        fatal "resources not found"
    fi
    algo=`mani_opt_value ".installer.resources.verify.algorithm"`
    if [ "$algo" = "null" ]; then
        fatal "package corrupted, resources hash algorithm not found"
    fi
    check_hash_algo "$algo"
    want_hash=`mani_opt_value ".installer.resources.verify.hash"`
    if [ "$want_hash" = "null" ]; then
        fatal "package corrupted, resources hash not found"
    fi
    got_hash=`tar xfO $PKG $name | hash $algo`
    if [ "$got_hash" != "$want_hash" ]; then
        fatal "package corrupted, resources verifcation failed"
    fi
    ( tar xfO $PKG $name | ( cd $D && tar xf - ) ) 2>/dev/null
    if [ $? -ne 0 ]; then
        fatal "resource extraction failed"
    fi
}

mani_keys()
{
    local cmd="$1 | keys | join(\" \") | tostring"
    local s="null"

    if [ -f $ASIC_MANIFEST ]; then
        s=`jq -r "$cmd" $ASIC_MANIFEST`
    fi

    if [ "$s" = "null" ]; then
        s=`jq -r "$cmd" $MANIFEST`
    fi

    echo $s
}

mani_opt_value()
{
    local cmd="$1 | tostring"
    local s="null"

    if [ -f $ASIC_MANIFEST ]; then
        s=`jq -r "$cmd" $ASIC_MANIFEST`
    fi

    if [ "$s" = "null" ]; then
        s=`jq -r "$cmd" $MANIFEST`
    fi

    echo $s
}

mani_value()
{
    local cmd
    local s="null"

    cmd="$1 | tostring"

    if [ -f $ASIC_MANIFEST ]; then
        s=`jq -r "$cmd" $ASIC_MANIFEST`
    fi

    if [ "$s" = "null" ]; then
        s=`jq -r "$cmd" $MANIFEST`
        if [ "$s" = "null" ]; then
            fatal "mani_value: $1 not found"
        fi
    fi

    echo $s
}

mani_length()
{
    local cmd="$1 | length"
    local s=0

    if [ -f $ASIC_MANIFEST ]; then
        s=`jq -r "$cmd" $ASIC_MANIFEST`
    fi

    if [ "$s" = "0" ]; then
        s=`jq -r "$cmd" $MANIFEST`
    fi

    echo $s
}

mani_fileobj_key()
{
    local fwobj=$1 $fileobj=$2 ref

    ref=`mani_opt_value ".firmware.$fwobj.files.$fileobj.ref"`
    if [ "$ref" = "null" ]; then
        ref=".firmware.$fwobj.files.$fileobj"
    fi
    echo $ref
}

mani_fileobj_value()
{
    local fwobj=$1 fileobj=$2 key=$3 ref

    ref=`mani_fileobj_key $fwobj $fileobj`
    mani_value "$ref$key"
}

check_hash_algo()
{
    local algo=$1
    case "$algo" in
    sha256|sha512|md5) return 0; ;;
    *) fatal "hash_hash_algo: unsupported algo $algo"; ;;
    esac
}

hash()
{
    local algo=$1
    case "$algo" in
    md5)    md5sum | awk '{ print $1 }'; ;;
    sha256) sha256sum | awk '{ print $1 }'; ;;
    sha512) sha512sum | awk '{ print $1 }'; ;;
    *) fatal "hash: invalid algo $algo"; ;;
    esac
}

# Verify the contents of a naples_fw.tar file
verify_package_firmware()
{
    local fwobj want_type want_files remainder found i fileobj
    local fileobj path algo want_hash got_hash ref

    num_asics=`mani_length .asic_compat_list`
    for i in `seq 1 $num_asics`; do
        pkg_asic=`mani_value ".asic_compat_list[$i - 1]"`
        ASIC_MANIFEST=$D/$pkg_asic/MANIFEST

        for fwobj in `mani_keys .firmware`; do
            #
            # File collection verification
            #
            want_type=`$fwobj_to_fwtype $fwobj`
            want_files=`$fwtype_to_files $want_type`
            if [ -z "$want_type" ] || [ -z "$want_files" ]; then
                fatal "verify_package_firmware: $fwobj: failed to get image_type/image_files"
            fi
            if [ `mani_value .firmware.$fwobj.type` != $want_type ]; then
                fatal "verify_package_firmware: $fwobj: invalid image type"
            fi
            for fileobj in `mani_keys ".firmware.$fwobj.files"`; do
                remainder=""
                found=0
                for i in $want_files; do
                    if [ "$i" = "$fileobj" ]; then
                        found=1
                    else
                        remainder="$remainder $i"
                    fi
                done
                if [ "$found" -eq 0 ]; then
                    fatal "verify_package_firmware: $fwobj: unexpected file type $fileobj"
                fi
                want_files=$remainder
            done
            if [ ! -z "$want_files" ]; then
                fatal "verify_package_firmware: $fwobj: expected file type $want_files not found"
            fi

            #
            # File data verification
            #
            for fileobj in `mani_keys ".firmware.$fwobj.files"`; do
                ref=`mani_opt_value ".firmware.$fwobj.files.$fileobj.ref"`
                if [ "$ref" != "null" ]; then
                    # Verify link is valid.  File content validated elsewhere
                    path=`mani_opt_value "${ref}.name"`
                    if [ "$path" = "null" ]; then
                        fatal "verify_package_firmware: $fwobj: required file data not found"
                    fi
                    continue
                fi
                path=`mani_opt_value ".firmware.$fwobj.files.$fileobj.name"`
                if [ "$path" = "null" ]; then
                    fatal "verify_package_firmware: $fwobj: required file data not found"
                fi
                echo "Verifying package file: $path..."
                algo=`mani_opt_value ".firmware.$fwobj.files.$fileobj.verify.algorithm"`
                if [ "$algo" = "null" ]; then
                    fatal "verify_package_firmware: $path: required hash algorithm not found"
                fi
                check_hash_algo "$algo"
                want_hash=`mani_opt_value ".firmware.$fwobj.files.$fileobj.verify.hash"`
                if [ "$want_hash" = "null" ]; then
                    fatal "verify_package_firmware: $path: required hash value not found"
                fi
                got_hash=`tar xfO $PKG $path | hash $algo`
                if [ "$got_hash" != "$want_hash" ]; then
                    echo "FAILED"
                    fatal "verify_package_firmware: $path: verifcation failed"
                fi
                echo "OK"
            done
        done
    done

    # Set the ASIC_MANIFEST to current ASIC MANIFEST for future lookups
    ASIC_MANIFEST=$D/$asic_name/MANIFEST
}

verify_package_bitfiles()
{
    local bitfileslength counter path algo want_hash got_hash

    num_asics=`mani_length .asic_compat_list`
    for i in `seq 1 $num_asics`; do
        pkg_asic=`mani_value ".asic_compat_list[$i - 1]"`
        ASIC_MANIFEST=$D/$pkg_asic/MANIFEST

        bitfileslength=`mani_length .bitfiles`
        for counter in `seq 1 $bitfileslength`
        do
            #path=`jq -r ".bitfiles[$counter - 1].name" $MANIFEST`
            path=`mani_value ".bitfiles[$counter - 1].name"`
            if [ "$path" = "null" ]; then
                fatal "verify_package_bitfiles: required file data not found"
            fi
            echo "Verifying bitfile file: $path..."
            algo=`mani_value ".bitfiles[$counter - 1].verify.algorithm"`
            if [ "$algo" = "null" ]; then
                fatal "verify_package_bitfiles: $path: required hash algorithm not found"
            fi
            want_hash=`mani_value ".bitfiles[$counter - 1].verify.hash"`
            if [ "$want_hash" = "null" ]; then
                fatal "verify_package_bitfiles: $path: required hash value not found"
            fi
            got_hash=`tar xfO $PKG $path | hash $algo`
            if [ "$got_hash" != "$want_hash" ]; then
                echo "FAILED"
                fatal "verify_package_bitfiles: $path: verifcation failed"
            fi
            echo "OK"
        done
    done

    # Set the ASIC_MANIFEST to current ASIC MANIFEST for future lookups
    ASIC_MANIFEST=$D/$asic_name/MANIFEST
}

# Verify the contents of a naples_fw.tar file
verify_package()
{
    local content=0
    echo "===> Verifying package"
    log_info "Verifying package"
    swver=`mani_opt_value ".software_version"`
    if [ "$swver" != "null" ]; then
        log_info "Software version $swver"
    fi
    if [ `mani_length .firmware` -ne 0 ]; then
        content=1
        verify_package_firmware
    fi
    if [ `mani_length .bitfiles` -ne 0 ]; then
        content=1
        verify_package_bitfiles
    fi
    if [ $content -eq 0 ]; then
        fatal "verify_package: No content"
    fi
    echo "Package file OK"
}

do_ainic_dpu_verify_image()
{
    local fwname=$1
    local fwobj fwtype files fileobj size
    local s dev part offs f path devsz ersz offblks
    local algo hash got_hash blks dblks v_algo v_hash v_offs v_size

    fwobj=`$fwname_to_fwobj $fwname`
    fwtype=`$fwobj_to_fwtype $fwobj`
    files=`$fwtype_to_files $fwtype`

    for f in $files; do
        ! $is_file_valid_for_fwname $fwname $f && continue
        if [ $f == "kernel_fit" ]; then
            if [ $fwname == "mainfwa" ]; then
                fname=fw-a
            elif [ $fwname == "mainfwb" ]; then
                fname=fw-b
            else
                fname="a35-goldfip"
            fi
        elif [ $f == fip ]; then
            if [ $fwname == "mainfwa" ]; then
                fname=fw-a
            else
                fname=fw-b
            fi
        elif [ $f == "goldfip" ]; then
            fname="a35-goldfip"
        elif [ $f == "uboota" ] && [ $fwname == "mainfwa" ]; then
            fname="uboot-a"
        elif [ $f == "ubootb" ] && [ $fwname == "mainfwb" ]; then
            fname="uboot-b"
        elif [ $f == "golduboot" ] && [ $fwname == "goldfw" ]; then
            fname="a35-golduboot"
        else
            fatal "Invalid filename $f"
            return
        fi

        bash -c "flash_update_client --verify-startup-image $fname $BDF" &
        child_pid=$!
        wait $child_pid

        child_exit_status=$?
        if [ $child_exit_status -ne 0 ]; then
            fatal "verify startup image failed for $fname"
            return
        fi
        sleep 1
        s=`jq -r -M ".software_version" $BDF_DIR/$fname.txt`
        rm -rf $BDF_DIR/$fname.txt
    done
    [ "$s" = "null" ] && s="unknown"
    log_info "New startup image version $s vs. running $SW_VERSION"
    echo "===> New startup version $s vs. running $SW_VERSION"
    echo "OK"
}

get_mani_swver()
{
    local ver

    ver=$(mani_opt_value .software_version)
    [ "$ver" = "null" ] && ver="unknown"
    echo $ver
}

ainic_dpu_get_firmware()
{
    bash -c "flash_update_client --get-firmware-image $1 $BDF" &
    child_pid=$!
    wait $child_pid

    child_exit_status=$?
    if [ $child_exit_status -eq 0 ]; then
        echo "mainfwa"
    elif [ $child_exit_status -eq 1 ]; then
        echo "mainfwb"
    elif [ $child_exit_status -eq 2 ]; then
        echo "goldfw"
    else
        echo "invalid"
    fi
    return
}

ainic_dpu_verify_file_signature()
{
    local file=$1 sigfile=$2
    local key_name

    key_name=$(do_verify_file_signature $file $sigfile) || return 1

    if [ $SIGCHK_REQUIRED -eq 1 -a "$key_name" != "prod" ]; then
        log_error -e "PROD signature required"
        exit 1
    fi
    echo $key_name
}

ainic_dpu_verify_image_signature()
{
    local fwname=$1
    local signed key_name
    local suffix_filename=_signed.txt

    run_flash_update_client "--list-firmware" "$fwname"

    # Look for a signature
    signed=$(jq -r .verify.metadata_signed $BDF_DIR/$fwname.txt)

    if [ "$signed" != "1" ]; then
        log_info "signed information not present for image $fwname"
        rm -rf $BDF_DIR/$fwname.txt
        # Not signed; we're done
        return 0
    fi

    if [ $SIGCHK_SUPPORTED -eq 0 ]; then
        # system does not support signature check
        rm -rf $BDF_DIR/$fwname.txt
        return 0
    fi

    run_flash_update_client "--get-signed-data" "$fwname"
    log_info "signed meta data $BDF_DIR/$fwname$suffix_filename for $fwname"

    meta_file_name=$BDF_DIR/$fwname.txt
    meta_signature_file_name=$BDF_DIR/$fwname$suffix_filename

    key_name= ainic_dpu_verify_file_signature $meta_file_name $meta_signature_file_name
    if [ $? -ne 0 ]; then
        rm -rf $meta_file_name
        rm -rf $meta_signature_file_name
        return 1
    fi

    rm -rf $meta_file_name
    rm -rf $meta_signature_file_name
    echo "$region signature verification done"
    return;
}

ainic_dpu_erase_and_install()
{
    local region=$1 path=$2 hash=$3 size=$4

    if [ "$region" != "cpld"  -a "$region" != "ufm1" ]; then
        echo "Erasing $region"
        bash -c "flash_update_client --erase $region $size $BDF" &
        child_pid=$!
        wait $child_pid

        child_exit_status=$?
        if [ $child_exit_status -ne 0 ]; then
            fatal "flash update failed for erase $region"
            return
        fi
        sleep 1
    fi

    echo "Installing $region"
    bash -c "flash_update_client --install $region $path $BDF" &
    child_pid=$!
    wait $child_pid

    child_exit_status=$?
    if [ $child_exit_status -ne 0 ]; then
        fatal "flash update failed for flash write $path"
        return
    fi
    sleep 1

    if [ "$region" != "cpld"  -a "$region" != "ufm1" ]; then
        echo "Verifying $region"
        bash -c "flash_update_client --verify-checksum $region $path $hash $BDF" &
        child_pid=$!
        wait $child_pid

        child_exit_status=$?
        if [ $child_exit_status -ne 0 ]; then
            fatal "flash update failed for verify checksum $path"
            return
        fi
    fi

    case "$region" in
    pentrustfw-0|pentrustfw-1|a35-bl1-0|a35-bl1-1|fw-a|fw-b)
        if [[ "$BOARD_SECURE_ENABLE" == "1" ||  "$BOARD_SECURE_BOOT" == "1" ]]; then
            ainic_dpu_verify_image_signature $region
        fi
      ;;
    *)
    ;;
    esac
    echo "$region programmed successfully"
    return
}

ainic_dpu_set_boot_option()
{
    local boot_option=$1
    bash -c "flash_update_client --set-boot-options $boot_option $BDF" &
    child_pid=$!
    wait $child_pid

    child_exit_status=$?
    if [ $child_exit_status -ne 0 ]; then
        fatal "unable to connect AINIC - ipc"
    fi
    echo "Boot option $boot_option programmed successfully"
    return
}

print_install_info()
{
    log_info "Image path: $1 Size: $2"
    log_info "algo: $3 hash: $4"
}

ainic_dpu_catalogue_install()
{
    local length counter path algo want_hash got_hash cfg_file_found

    cfg_file_found=0
    default_file_found=0
    default_file_count=0
    region=$2

    echo "===> Installing device catalog for board $BOARD_COMPAT_BOARDID"
    num_asics=`mani_length .asic_compat_list`
    for i in `seq 1 $num_asics`; do
        pkg_asic=`mani_value ".asic_compat_list[$i - 1]"`
        ASIC_MANIFEST=$D/$pkg_asic/MANIFEST

        length=`mani_length .fw_cfg`
        for counter in `seq 1 $length`
        do
            path=`mani_value ".fw_cfg[$counter - 1].name"`
            if [ "$path" = "null" ]; then
                fatal "ainic_dpu_catalog_install: required file data not found"
            fi

            if [ "$path" = "$DEFAULT_FIRMWARE_CONFIG_PATH" ]; then
                default_file_found=1
                default_file_counter=$counter
            fi

            if [ -z "$BOARD_COMPAT_BOARDID" ] || [ "$BOARD_COMPAT_BOARDID" = "$BOARD_ID_UNKNOWN" ]; then
                log_info "Board id is unknown and fall back to default fw_cfg"
                continue
            fi

            if [[ "$path" == *"$BOARD_COMPAT_BOARDID"* ]]; then
                log_info "ainic_dpu_fw_cfg_install file: $path..."
                algo=`mani_value ".fw_cfg[$counter - 1].verify.algorithm"`
                if [ "$algo" = "null" ]; then
                    fatal "ainic_dpu_catalogue_install: $path: required hash algorithm not found"
                fi
                want_hash=`mani_value ".fw_cfg[$counter - 1].verify.hash"`
                if [ "$want_hash" = "null" ]; then
                    fatal "ainic_dpu_catalogue_install: $path: required hash value not found"
                fi
                got_hash=`tar xfO $PKG $path | hash $algo`
                if [ "$got_hash" != "$want_hash" ]; then
                    echo "FAILED"
                    fatal "ainic_dpu_catalogue_install: $path: verifcation failed"
                fi
                size=`tar tvf $PKG $path | awk '{ print $3 }'`
                log_info "INSTALL Image path :$path algo:$algo want_hash:$want_hash got_hash:$got_hash"
                ainic_dpu_erase_and_install $region $D/$path $want_hash $size
                echo "Install device catalog successfully"
                cfg_file_found=1
                break
            fi
        done
    done
    if [ $cfg_file_found == 0 ]; then
        if [ $default_file_found == 1 ]; then
            path=`mani_value ".fw_cfg[$default_file_counter - 1].name"`
            algo=`mani_value ".fw_cfg[$default_file_counter - 1].verify.algorithm"`
            want_hash=`mani_value ".fw_cfg[$default_file_counter - 1].verify.hash"`
            size=`tar tvf $PKG $path | awk '{ print $3 }'`
            log_info "INSTALL Image path :$path algo:$algo want_hash:$want_hash got_hash:$got_hash"
            ainic_dpu_erase_and_install $region $D/$path $want_hash $size
            echo "Install default device catalogue successfully"
        fi
    fi
}

ainic_dpu_install_bitfile_images()
{
    local running_cpld_rev bitfileslength found
    local bitfile_cpld_rev mani_cpld_rev path mani_cpld_id
    local bitfile_path bitfile_size
    local running_cpld_minor_rev running_cpld_id
    local fname=$BDF_DIR/card_info.txt
    local ufm1_update=0

    if [ ! -e "$fname" ]; then
        flash_update_client --get-card-info $BDF
    fi
    if [ -e "$fname" ]; then
        echo `jq -r .asic $fname`
    fi

    # Get current id and revision of CPLD.
    running_cpld_rev=`jq -r .cpld_version $fname`
    running_cpld_id=`jq -r .cpld_id $fname`

    # Get current minor revision of CPLD
    running_cpld_minor_rev=`jq -r .cpld_minor_version $fname`

    # Get running image type, main or gold/recovery
    is_gold_running=`jq -r .is_cpld_gold $fname`

    bitfileslength=`mani_length .bitfiles`
    found=0
    for counter in `seq 1 $bitfileslength`
    do
        bitfile_cpld_rev=`mani_value ".bitfiles[$counter - 1].version"`
        mani_cpld_id=`mani_value ".bitfiles[$counter - 1].cpld_ufm_id"`
        # Bit 7 of the running and candidate CPLD revisions must match
        if [ $((($running_cpld_rev ^ $bitfile_cpld_rev) & 0x80)) -ne 0 ]; then
            continue
        fi
        # Check if cpld id is matching
        if [ $running_cpld_id -eq $mani_cpld_id ]; then
            path=`mani_value ".bitfiles[$counter - 1].name"`
            if [[ "$path" != *"ufm"* ]]; then
               found=1
               break
            fi
            continue
        fi
    done

    if [ $found -eq 0 ]; then
        # CPLD image not present for this card
        echo "===> No bitfile update required"
        return
    fi

    # Found a CPLD image for this card.  Check the version.
    mani_cpld_rev=`mani_value ".bitfiles[$counter - 1].version"`
    mani_cpld_min_rev=`mani_opt_value ".bitfiles[$counter - 1].min_version"`
    mani_cpld_minor_rev=`mani_opt_value ".bitfiles[$counter - 1].minor_version"`
    if [ $mani_cpld_min_rev = "null" ]; then
        mani_cpld_min_rev=$running_cpld_rev
    fi
    mani_hash=`mani_value ".bitfiles[$counter - 1].verify.hash"`
    mani_algo=`mani_value ".bitfiles[$counter - 1].verify.algorithm"`

    if [ $IGNORE_MIN_REV_CHECK -eq 0 ]; then
        if [ $running_cpld_rev -lt $mani_cpld_min_rev ]; then
            log_info "Running CPLD cannot be updated cur=$running_cpld_rev, min_revision=$mani_cpld_min_rev, install=NO"
            echo "Running CPLD cannot be updated cur=$running_cpld_rev, min_revision=$mani_cpld_min_rev, install=NO"
            exit 1
        elif [ $running_cpld_rev -gt $mani_cpld_rev ]; then
            log_info "CPLD already at latest cpld cur=$running_cpld_rev.$running_cpld_minor_rev, manifest=$mani_cpld_rev.$mani_cpld_minor_rev, install=NO"
            echo "CPLD already at latest cpld cur=$running_cpld_rev.$running_cpld_minor_rev, manifest=$mani_cpld_rev.$mani_cpld_minor_rev, install=NO"
            if [[ "$INSTALL_IMAGES" == *"cpld"* && "$INSTALL_IMAGES" != "cpld" ]]; then
                # with all option, should not exit
                return
            else
                # to differentiate the status of cpld operation to nicctl
                exit $CPLD_AT_LATEST_VERSION
            fi
        elif [ $running_cpld_rev -eq $mani_cpld_rev ]; then
            if [ $running_cpld_minor_rev -ge $mani_cpld_minor_rev ]; then
                log_info "CPLD already at latest cpld cur=$running_cpld_rev.$running_cpld_minor_rev, manifest=$mani_cpld_rev.$mani_cpld_minor_rev, install=NO"
                echo "CPLD already at latest cpld cur=$running_cpld_rev.$running_cpld_minor_rev, manifest=$mani_cpld_rev.$mani_cpld_minor_rev, install=NO"
                if [[ "$INSTALL_IMAGES" == *"cpld"* && "$INSTALL_IMAGES" != "cpld" ]]; then
                    # with all option, should not exit
                    return
                else
                    # to differentiate the status of cpld operation to nicctl
                    exit $CPLD_AT_LATEST_VERSION
                fi
            fi
        fi
    fi
    log_info "CPLD cur=$running_cpld_rev.$running_cpld_minor_rev, new=$mani_cpld_rev.$mani_cpld_minor_rev, install=YES"
    echo "===> Commit install CPLD rev $mani_cpld_rev.$mani_cpld_minor_rev over rev $running_cpld_rev.$running_cpld_minor_rev"
    path=`mani_value ".bitfiles[$counter - 1].name"`
    size=`tar tvf $PKG $path | awk '{ print $3 }'`
    hash=`tar xfO $PKG $path | hash $algo`
    log_info "Install bitfile image path :$path hash:$hash algo:$mani_algo manifest hash:$mani_hash"
    echo "===> Install bitfile image path :$path"

    ainic_dpu_erase_and_install cpld $D/$path $mani_hash $size

    # update ufm1 only if running cpld version is less than 1.12 and package
    # version is 1.12 and above.
    if [ $mani_cpld_rev -gt 1 ]; then
        ufm1_update=1
    elif [ $mani_cpld_rev -eq 1 -a $mani_cpld_minor_rev -ge 12 ]; then
        ufm1_update=1
    fi
    if [ $ufm1_update -eq 1 ]; then
        ufm1_file=`ls $D/salina/salina400_pollaraufm1_rev*.bin`
        if [ $running_cpld_rev -lt 1 ]; then
            ainic_dpu_erase_and_install ufm1 $ufm1_file $mani_hash $size
        elif [ $running_cpld_rev -eq 1 -a $running_cpld_minor_rev -le 12 ]; then
            ainic_dpu_erase_and_install ufm1 $ufm1_file $mani_hash $size
        elif [ $running_cpld_rev -ge 2 ]; then
            ainic_dpu_erase_and_install ufm1 $ufm1_file $mani_hash $size
        fi
    fi
    log_info "Done"
    echo "Done"
}

is_boot0_update_required()
{
    local card_info_file this_boardid running_boot0_ver mani_boot0_ver boot0_min_ver
    local l i boardid

    card_info_file=$BDF_DIR/card_info.txt
    this_boardid=$BOARD_COMPAT_BOARDID
    if [ -z "$this_boardid" ] || [ "$this_boardid" = "$BOARD_ID_UNKNOWN" ]; then
        log_info "boardid info not found in is_boot0_update_required"
    fi
    log_info "Considering boot0 upgrade for board $this_boardid"
    if [ ! -e "$card_info_file" ]; then
        flash_update_client --get-card-info $BDF
    fi
    running_boot0_ver=`jq -r '.["boot0_version"]' $card_info_file`
    mani_boot0_ver=`mani_value .firmware.boot0.files.image.image_version`
    boot0_min_ver=0
    l=`mani_length ".firmware.boot0.board_map"`
    for i in `seq $l`; do
        boardid=`mani_value ".firmware.boot0.board_map[$i - 1].boardid"`
        if [ "$boardid" = '#' -o "$boardid" = "$this_boardid" ]; then
            boot0_min_ver=`mani_value ".firmware.boot0.board_map[$i - 1].min_version"`
            break
        fi
    done
    if [ $mani_boot0_ver -gt $running_boot0_ver -a $running_boot0_ver -lt $boot0_min_ver ]; then
        log_info "boot0 cur=$running_boot0_ver, min=$boot0_min_ver, new=$mani_boot0_ver, install=YES"
        echo "boot0 cur=$running_boot0_ver, min=$boot0_min_ver, new=$mani_boot0_ver, install=YES"
        return 0
    else
        log_info "boot0 cur=$running_boot0_ver, min=$boot0_min_ver, new=$mani_boot0_ver, install=NO"
        echo "boot0 cur=$running_boot0_ver, min=$boot0_min_ver, new=$mani_boot0_ver, install=NO"
        return 1
    fi
}

get_lifecycle_next_state()
{
    local lcs=open
    bash -c "flash_update_client --secure-info lcs  $BDF" &
    child_pid=$!
    wait $child_pid

    child_exit_status=$?
    if [ $child_exit_status -eq $FWUPDATE_ERR_SEC_FW_NOT_RUNNING ]; then
        log_error "flash_update_client failed to get lcs state-bl31 not supported"
    elif [ $child_exit_status -eq 0 ]; then
        log_error "flash_update_client failed to get lcs state"
        if [ -f $BDF_DIR/secure_info_lcs.txt ]; then
            lcs=$(jq -r ".lcs_next_state" $BDF_DIR/secure_info_lcs.txt 2>/dev/null)
            rm -rf $BDF_DIR/secure_info_lcs.txt
        fi
    fi
    echo $lcs
}

get_pentrust_slot()
{
    local slot
    run_flash_update_client "--secure-info" "pentrust"
    if [ -f $BDF_DIR/secure_info_pentrust.txt ]; then
        slot=$(jq -r ".current_boot_partition" $BDF_DIR/secure_info_pentrust.txt 2>/dev/null)
        if [ "$slot" -eq 0 ] || [ "$slot" -eq 1 ]; then
            echo $slot
        else
            echo "-1"
        fi
        rm -rf $BDF_DIR/secure_info_pentrust.txt
    else
        echo "-1"
    fi
}

get_bl1_slot()
{
    local slot
    run_flash_update_client "--secure-info" "bl1"
    if [ -f $BDF_DIR/secure_info_bl1.txt ]; then
        slot=$(jq -r ".current_boot_partition" $BDF_DIR/secure_info_bl1.txt 2>/dev/null)
        if [ "$slot" -eq 0 ] || [ "$slot" -eq 1 ]; then
            echo $slot
        else
            echo "-1"
        fi
        rm -rf $BDF_DIR/secure_info_bl1.txt
    else
        echo "-1"
    fi
}

get_ptimage_slot()
{
    local pt_slot=-1
    if [ $1 == "bl1" ]; then
        pt_slot=`get_bl1_slot`
    elif [ $1 == "pentrustfw" ]; then
        pt_slot=`get_pentrust_slot`
    elif [ $1 == "pentrustsoftrom" ]; then
        pt_slot=`get_pentrust_slot`
    else
        echo "-1"
    fi
    if [ -z "$pt_slot" ]; then
       pt_slot=-1
    fi
    echo "$pt_slot"
}

get_ar_version()
{
    local slot
    run_flash_update_client "--secure-info" "bl1"
    if [ -f $BDF_DIR/secure_info_bl1.txt ]; then
        BL1_AR_VERSION=$(jq -r ".bl1_ar_version" $BDF_DIR/secure_info_bl1.txt 2>/dev/null)
        BL1_AR_NV_COUNTER=$(jq -r ".bl1_ar_nv_count" $BDF_DIR/secure_info_bl1.txt 2>/dev/null)
        echo "BL1_AR_VERSION:$BL1_AR_VERSION BL1_AR_NV_COUNTER:$BL1_AR_NV_COUNTER"
    fi
}

verify_bl1_ar_version()
{
    local image_ar_ver=$1 bl1_hw_nv_cntr

    if [ $LCS_STATE == "open" ]; then
        # for OPEN cards. no need to check BL1 AR version
        return
    fi

    # LCS is "prod" or "soft-prod"
    # get BL1 hw nv counter
    bl1_hw_nv_cntr=$BL1_AR_NV_COUNTER

    # compare
    if [ $bl1_hw_nv_cntr -gt $image_ar_ver ]; then
        return 1
    fi
}


is_secure_image_upgrade_required()
{
    local fwobj=$1 fwname=$2 pt_slot=$3 fwtype files
    local pt_slot fwnameslot fwkeys
    fwnameslot=`echo $fwname | cut -d'-' -f2`

    fwtype=`$fwobj_to_fwtype $fwobj`
    files=`$fwtype_to_files $fwtype`

    if [ $fwnameslot == $pt_slot ];then
       # dont upgrade the slot we booted from
       return 1
    fi

    if [ $fwobj == "pentrustsoftrom" -a  $BOARD_HW_FUSE -eq 1 ]; then
        fatal "Cannot update softrom on fuse-enabled card"
    fi

    # skip softrom programing if entry is not present in the manifest
    fwkeys=`mani_keys .firmware`
    if [[ "$fwkeys" != *"$fwobj"* ]]; then
        if [ "$INSTALL_IMAGES" == "softrom-0 softrom-1" ]; then
            fatal "$fwobj image not present in the tar"
            exit 1
        else
            log_info "$fwobj image not present in the tar"
            return 1
        fi
    fi

    if [ $IGNORE_MIN_REV_CHECK -eq 1 ]; then
        echo "skipping minimum version check for $fwname"
        return 0
    fi
    if [ $fwobj == "bl1" ]; then
        if [ $BOARD_SECURE_BOOT -eq 1 ]; then
            get_ar_version $fwname
        fi
        fwname=a35-$fwname
    fi

    # TODO- running version need to revisit for softrom
    run_flash_update_client "--list-firmware" $fwname
    if [ -f $BDF_DIR/$fwname.txt ]; then
        if [[ "$fwname" == *"softrom"* ]]; then
            running_ver=0
        else
            running_ver=$(jq -r ".image_version" $BDF_DIR/$fwname.txt 2>/dev/null)
        fi
    fi

    mani_ver=`mani_value .firmware.$fwobj.files.$files.image_version`
    min_ver=0
    l=`mani_length ".firmware.$fwobj.board_map"`
    for i in `seq $l`; do
        boardid=`mani_value ".firmware.$fwobj.board_map[$i - 1].boardid"`
        if [ "$boardid" = '#' -o "$boardid" = "$this_boardid" ]; then
            min_ver=`mani_value ".firmware.$fwobj.board_map[$i - 1].min_version"`
            break
        fi
    done

    log_info "Image $fwname running_ver $running_ver mani_ver $mani_ver min_ver $min_ver"
    if [ $mani_ver -gt $running_ver -a $running_ver -lt $min_ver ]; then
        # Verify ar_ver only if it is specified in the MANIFEST
        # TODO - need revisit to check what ar_version should compare
        # either from metadata or from BL1_AR_VERSION value
        if [ -n "$BL1_AR_VERSION" ]; then
            verify_bl1_ar_version $BL1_AR_VERSION
            if [ $? -ne 0 ]; then
                log_info "$fwname ar_ver $BL1_AR_VERSION is too low to be installed"
                return 1
            fi
        fi
        log_info "$fwname cur=$running_ver, min=$min_ver, new=$mani_ver, install=YES"
        echo "$fwname cur=$running_ver, min=$min_ver, new=$mani_ver, install=YES"
        return 0
    else
        log_info "$fwname cur=$running_ver, min=$min_ver, new=$mani_ver, install=NO"
        echo "$fwname cur=$running_ver, min=$min_ver, new=$mani_ver, install=NO"
        return 1
    fi
}

ainic_dpu_install_fwobj()
{
    local fwobj=$1 fwname=$2
    local fwtype fileobj size

    fwtype=`mani_opt_value ".firmware.$fwobj.type"`
    if [ "$fwtype" = "null" ]; then
        fatal "$fwobj: invalid fwtype $fwtype"
    fi

    log_info "========> Installing $fwname type $fwtype"

    for fileobj in `$fwtype_to_files $fwtype`; do
        ! $is_file_valid_for_fwname $fwname $fileobj && continue
        path=`mani_fileobj_value $fwobj $fileobj .name`
        algo=`mani_fileobj_value $fwobj $fileobj .verify.algorithm`
        check_hash_algo "$algo"
        hash=`mani_fileobj_value $fwobj $fileobj .verify.hash`
        size=`tar tvf $PKG $path | awk '{ print $3 }'`

        case "$fwname" in
        mainfwa|mainfwb|goldfw|fw_cfg|fw_cfg-b|boot0|dtb-b|dtb|pentrustfw-0|pentrustfw-1|bl1-0|bl1-1|softrom-0|softrom-1)
            if ! [ -f "$D/$path" ]; then
                fatal "$path is not present"
            fi

            if [ $fwname == mainfwa ]; then
               if [[ "$path"  == *"uboot"* ]]; then
                  region=uboot-a
               else
                  region=fw-a
               fi
            elif [ $fwname == mainfwb ]; then
               if [[ "$path"  == *"uboot"* ]]; then
                  region=uboot-b
               else
                  region=fw-b
               fi
            elif [ $fwname == goldfw ]; then
               if [[ "$path"  == *"uboot"* ]]; then
                  region=a35-golduboot
               else
                  region=a35-goldfip
               fi
            elif [ $fwname == dtb ]; then
                region=dtb
            elif [ $fwname == dtb-b ]; then
                region=dtb-b
            elif [ $fwname == fw_cfg ]; then
                region=fw_cfg
            elif [ $fwname == boot0 ]; then
                region=boot0
            elif [ $fwname == pentrustfw-0 ] || [ $fwname == pentrustfw-1 ]; then
                region=$fwname
            elif [ $fwname == bl1-0 ]; then
                region=a35-bl1-0
            elif [ $fwname == bl1-1 ]; then
                region=a35-bl1-1
            elif [ $fwname == softrom-0 ] || [ $fwname == softrom-1 ]; then
                region=$fwname
            fi
            print_install_info $path $size $algo $hash
            ainic_dpu_erase_and_install $region $D/$path $hash $size
            sleep 1
            ;;
        *)
            fatal "invalid $fwname to install"
            ;;
        esac
        if [ $BOARD_SECURE_BOOT -eq 1 ]; then
            # If we installed a new version of BL1, notify bl1
            if [ $fwname == bl1-0 ] || [ $fwname == bl1-1 ]; then
                run_flash_update_client "--secure-image-update" "bl1" "boot-alt-partition"
                log_info "bl1 upgrade flag set"
            fi
            # If we installed a new version of pentrustfw, notify pentrustfw
            if [ $fwname == pentrustfw-0 ] || [ $fwname == pentrustfw-1 ]; then
                run_flash_update_client "--secure-image-update" "pentrust" "boot-alt-partition"
                log_info "pentrust upgrade flag set"
            fi
        fi
    done
    echo "Done"
    log_info "Done"
}

get_secure_image_running_slot()
{
    if [ "$BOARD_SECURE_ENABLE" == 0 ] || [ "$BOARD_SECURE_ENABLE" == "null" ]; then
       fatal "card is not secure enabled"
    fi

    if [ $BOARD_SECURE_BOOT -eq 0 ]; then
        if [ $A35_RUNNING_FW == mainfwa ]; then
            pt_slot=0
        else
            pt_slot=1
        fi
    else
        # find the slot number of the bl1 image that we booted from
        pt_slot=`get_ptimage_slot $fwobj`
    fi
    echo "$pt_slot"
}

ainic_dpu_install_images()
{
    local pkg_swver fwname fwobj

    # Check the install list to make sure its ok
    $check_install_list

    # Verify the package
    verify_package
    ALT_FW=`alt_fw_of $A35_RUNNING_FW`
    ALTDEV_CFG=`alt_devcfg_of $A35_RUNNING_FW`
    ALTFW_CFG=`alt_fwcfg_of $A35_RUNNING_FW`

    pkg_swver=$(get_mani_swver)
    echo "===> Installing package version $pkg_swver from running $SW_VERSION"
    tar -xf $PKG  -C $D &> /dev/null
    for fwname in $INSTALL_IMAGES; do
        if [ "$fwname" = "altfw" ]; then
            fwname=$ALT_FW
        elif [ "$fwname" = "altdevcfg" ]; then
            fwname=$ALTDEV_CFG
        elif [ "$fwname" = "altfwcfg" ]; then
            fwname=$ALTFW_CFG
        fi

        fwobj=`$fwname_to_fwobj $fwname`
        if [ "$fwobj" = "fw_cfg" ]; then
            log_info "Installing catalogue firmware"
            ainic_dpu_catalogue_install $fwobj $fwname
        elif [ $fwobj == "cpld" ]; then
            ainic_dpu_install_bitfile_images
        elif [ "$fwname" = "boot0" ]; then
            # install boot0 images if running version is less than
            # minimum boot0 version and running version is less than package
            # version in -all option
            if [[ "$INSTALL_IMAGES" == *"boot0"* && "$INSTALL_IMAGES" != "boot0" ]]; then
                is_boot0_update_required
                if [ $? -ne 0 ]; then
                    continue
                fi
            fi
        else
            # install secure images if running version is less than
            # minimum version and running version is less than package
            # version in -all option
            running_slot=`get_secure_image_running_slot $fwobj`
            echo "RUNNING_SLOT:$running_slot"
            if [[ $fwname == "pentrustfw-0" ||  $fwname == "pentrustfw-1" ]]; then
                if [[ "$INSTALL_IMAGES" != "pentrustfw-0" && "$INSTALL_IMAGES" != "pentrustfw-1" ]]; then
                    is_secure_image_upgrade_required $fwobj $fwname $running_slot
                    if [ $? -ne 0 ]; then
                        continue
                    fi
                fi
            elif [[ $fwname == "bl1-0" ||  $fwname == "bl1-1" ]]; then
                if [[ "$INSTALL_IMAGES" != "bl1-0" && "$INSTALL_IMAGES" != "bl1-1" ]]; then
                    is_secure_image_upgrade_required $fwobj $fwname $running_slot
                    if [ $? -ne 0 ]; then
                        continue
                    fi
                fi
            elif [[ ($fwname == "pentrustfw" && "$INSTALL_IMAGES" == "pentrustfw") ||
                    ($fwname == "bl1" && "$INSTALL_IMAGES" == "bl1") ]]; then
                if [ $running_slot == 1 ]; then
                    fwname=$fwname-0
                    fwnameslot=0
                else
                    fwname=$fwname-1
                    fwnameslot=1
                fi
                is_secure_image_upgrade_required $fwobj $fwname $running_slot
                if [ $? -ne 0 ]; then
                    continue
                fi
            elif [ $fwobj == "pentrustsoftrom" ]; then
                if [[ "$INSTALL_IMAGES" == *"softrom"* && "$INSTALL_IMAGES" != "softrom" ]]; then
                    is_secure_image_upgrade_required $fwobj $fwname $running_slot
                    if [ $? -ne 0 ]; then
                        continue
                    fi
                fi
            fi
            ainic_dpu_install_fwobj $fwobj $fwname
        fi
    done
}

ainic_dpu_set_startup_image()
{
    local fwname s

    if [ "$WANT_STARTUP" = "altfw" ]; then
        WANT_STARTUP=`alt_fw_of $A35_RUNNING_FW`
    fi

    case "$WANT_STARTUP" in
    goldfw)
        fwname=a35-goldfw
        ;;
    mainfwa)
        fwname=fw-a
        ;;
    mainfwb)
        fwname=fw-b
        ;;
    *)
        fatal "ainic_dpu_set_startup_image: Unknown image name $WANT_STARTUP"
        ;;
    esac

    echo "===> Verifying target startup image $fwname"
    do_ainic_dpu_verify_image $WANT_STARTUP

    echo "===> Setting startup firmware to $fwname"
    ainic_dpu_set_boot_option $fwname
    echo "OK"
}

show_meta()
{
    jq -r "del(.verify)" $TMPFILE | \
    jq -r "del(.metadata_version)" | sed -e '1d' -e '$d'
}

JPREF=""
JFPREF=""

ainic_dpu_show_meta()
{
    local fname=$1
    if [ -e $BDF_DIR/$1.txt ]; then
        jq -r "del(.verify)" $BDF_DIR/$1.txt | \
        jq -r "del(.metadata_version)" | sed -e '1d' -e '$d'
    fi
}

JPREF=""
ainic_dpu_print_firmware_list()
{
    local label=$1

    if [ "$label" == "dtb" ]; then
        label_print=dtb-a
    elif [ "$label" == "fw_cfg" ]; then
        label_print=fw_cfg-a
    elif [ "$label" == "a35-bl1-0" -o "$label" == "a35-bl1-1" ]; then
        label_print="${label//a35-/}"
    else
        label_print=$label
    fi

    cat << EOF
    $JPREF "$label_print": {
         `ainic_dpu_show_meta $label`
    }
EOF
    JPREF=,
    # remove the txt file which is no longer required
    # after run the ainic_dpu_show_meta api
    rm -rf $BDF_DIR/$label.txt
}

ainic_dpu_do_list_firmware()
{
    local fwname=$1 fwobj fwtype files dev part f s off

    if [ "$fwname" == "dtb" ]; then
        fwname_print=dtb-a
    elif [ "$fwname" == "fw_cfg" ]; then
        fwname_print=fw_cfg-a
    else
        if [ "$PACKAGE_VERSION" == "rev3" ]; then
            if [ "$fwname" == "mainfwa" ]; then
                fwname_print="fip-a"
            elif [ "$fwname" == "mainfwb" ]; then
                fwname_print="fip-b"
            else
                 fwname_print=$fwname
            fi
        else
           fwname_print=$fwname
        fi
    fi

    echo "    $JFPREF \"$fwname_print\": {"
    fwobj=`$fwname_to_fwobj $fwname`
    fwtype=`$fwobj_to_fwtype $fwobj`
    files=`$fwtype_to_files $fwtype`

    for f in $files; do
        ! $is_file_valid_for_fwname $fwname $f && continue
        if [ $f == "kernel_fit" ] && [ $fwname == "mainfwa" ]; then
            fname="fw-a"
        elif [ $f == "kernel_fit" ] && [ $fwname == "mainfwb" ]; then
            fname="fw-b"
        elif [ $f == "uboota" ] && [ $fwname == "mainfwa" ]; then
            fname="uboot-a"
        elif [ $f == "ubootb" ] && [ $fwname == "mainfwb" ]; then
            fname="uboot-b"
        elif [ $f == "device_config" ] && [ $fwname == "dtb" ]; then
            fname="dtb"
        elif [ $f == "device_config" ] && [ $fwname == "dtb-b" ]; then
            fname="dtb-b"
        elif [ $f == "fw_cfg" ] && [ $fwname == "fw_cfg" ]; then
            fname="fw_cfg"
        elif [ $f == "fw_cfg" ] && [ $fwname == "fw_cfg-b" ]; then
            fname="fw_cfg-b"
        elif [ $fwname == "goldfw" ]; then
            if [ $f == "kernel_fit" ] || [ $f == "goldfip" ]; then
                fname="a35-goldfip"
            elif [ $fwname == "goldfw" ]; then
                fname="a35-golduboot"
            fi
        elif [ $f == "image" ]; then
            fname=boot0
        elif [ $f == "ptimage" ] &&
            { [ $fwname == pentrustfw-0 ] || [ $fwname == pentrustfw-1 ]; }; then
            fname=$fwname
        elif [ $f == "ptimage" ] &&
            { [ $fwname == bl1-0 ] || [ $fwname == bl1-1 ]; }; then
            fname=a35-$fwname
        elif [ $f == "ptsoftrom" ] &&
             { [ $fwname == softrom-0 ] || [ $fwname == softrom-1 ]; }; then
            fname=$fwname
         elif [ $f == fip ]; then
           if [ $fwname == mainfwa ]; then
                fname=fw-a
           else
               fname=fw-b
           fi
        else
            fatal "Invalid filename $f $fwname"
            return
        fi
        bash -c "flash_update_client --list-firmware $fname $BDF" &
        child_pid=$!
        wait $child_pid

        child_exit_status=$?
        if [ $child_exit_status -ne 0 ]; then
            fatal "unable to connect AINIC - ipc error"
        fi

        ainic_dpu_print_firmware_list $fname
    done
    echo "    }"
    JFPREF=","
}

ainic_dpu_show_cpld_version()
{
    local fname=$BDF_DIR/card_info.txt
    if [ -e $fname ]; then
        jq -r "del(.date)" $fname | \
        jq -r "del(.cpld_id)" | \
        jq -r "del(.sw_version)" | \
        jq -r "del(.board_id)" | \
        jq -r "del(.is_cpld_gold)" | \
        jq -r "del(.fwupdate_ipc_version)" | \
        jq -r "del(.board_hw_fuse)" | \
        jq -r "del(.board_sigchk)" | \
        jq -r "del(.board_keyname)" | \
        jq -r "del(.board_secure_enable)" | \
        jq -r "del(.board_secure_boot)" | \
        jq -r "del(.bl1_slot_num)" | \
        jq -r "del(.pentrust_slot_num)" | \
        jq -r "del(.life_cycle_state)" | \
        jq -r "del(.life_cycle_state_fuses)" | \
        jq -r "del(.boot0_version)" | \
        jq -r "del(.asic)" | sed -e '1d' -e '$d'
    fi
}

do_list_cpld()
{
    echo "    $JFPREF \"cpld\": {"
    bash -c "flash_update_client --get-card-info $BDF" &
    child_pid=$!
    wait $child_pid

    child_exit_status=$?
    if [ $child_exit_status -ne 0 ]; then
        fatal "unable to connect AINIC - ipc error"
    fi

    ainic_dpu_show_cpld_version
    echo "    }"
    JFPREF=","
    # remove the txt file which is no longer required
    # after run the ainic_dpu_show_cpld_version api
    rm -rf $BDF_DIR/card_info.txt
}

list_firmware()
{
    local fwname

    (
    echo "{"
    for fwname in $FWLIST_NAMES; do
        JPREF=""
        if is_fwupdate_for_ainic; then
            ainic_dpu_do_list_firmware $fwname
        fi
    done
    if [ $SKIP_BITFILES -eq 0 ]; then
        do_list_cpld
    fi
    echo "}"
    ) | jq -r -M .
}

ainic_dpu_list_loaded_firmware()
{
    local pt_slot=-1
    local bl1_slot=-1

    if [ $A35_RUNNING_FW == mainfwa ]; then
        A35_RUNNING_DTB=dtb
        A35_RUNNING_FWCFG=fw_cfg
    elif [ $A35_RUNNING_FW == mainfwb ]; then
        A35_RUNNING_DTB=dtb-b
        A35_RUNNING_FWCFG=fw_cfg-b
    else
        A35_RUNNING_DTB=""
        A35_RUNNING_FWCFG=""
    fi

    if [[ "$BOARD_SECURE_ENABLE" == "1" ||  "$BOARD_SECURE_BOOT" == "1" ]]; then
        if [ $BOARD_SECURE_BOOT -eq 1 ]; then
            pt_slot=`get_pentrust_slot`
            bl1_slot=`get_bl1_slot`
        fi

        if [ $pt_slot != -1 ]; then
            A35_RUNNING_PEN_FW=pentrustfw-$pt_slot
            A35_RUNNING_PEN_SOFTROM=softrom-$pt_slot
        else
            A35_RUNNING_PEN_FW=""
            A35_RUNNING_PEN_SOFTROM=""
        fi

        if [ $bl1_slot != -1 ]; then
            A35_RUNNING_BL1=bl1-$bl1_slot
        else
            A35_RUNNING_BL1=""
        fi
    fi
    if [ $PACKAGE_VERSION = "rev2" ]; then
        FWLIST_NAMES="boot0 $A35_RUNNING_FW $A35_RUNNING_DTB $A35_RUNNING_FWCFG"
    else
        FWLIST_NAMES="$A35_RUNNING_PEN_SOFTROM $A35_RUNNING_PEN_FW $A35_RUNNING_BL1 $A35_RUNNING_FW $A35_RUNNING_DTB $A35_RUNNING_FWCFG"
    fi
    list_firmware
}

set -o pipefail
main "$@" 2>&1 |
(
    IFS='<newline>'
    while read x; do
        echo "$x" || true
    done
)
