#!/bin/bash
#
# calvados/spitfire/boot/scripts/pd-functions
# This script provides PD functions to other scripts.
#
# Copyright (c) 2017-2021 by Cisco Systems, Inc.
# All rights reserved.

PD_FUNCTION_SOURCED=1

# source PD-function library
if [ -f /etc/init.d/load_kernel_modules.sh ]; then
    source /etc/init.d/load_kernel_modules.sh
fi

# Include FPGA functions to implement watchdog functionality
if [ -f /usr/local/etc/fpga-functions ]; then
    source /usr/local/etc/fpga-functions
else
    echo "Error: missing /usr/local/etc/fpga-functions file."
fi

# Include FPGA functions to implement watchdog functionality
if [ -f /usr/local/etc/fpga-functions ]; then
    source /usr/local/etc/fpga-functions
else
    echo "Error: missing /usr/local/etc/fpga-functions file."
fi

#
# Get the file size in bytes, cross os
#
function filesize
{
    local file=$1
    size=`stat -c %s $file 2>/dev/null` # linux
    if [ $? -eq 0 ]; then
        echo $size
        return 0
    fi

    eval $(stat -s $file) # macos
    if [ $? -eq 0 ]; then
        echo $st_size
        return 0
    fi

    echo 0
    return -1
}

#
# Check if pd_funcions should be skipped
#
function skip_pd_functions {
    local install=$(cat /proc/cmdline | grep install=)
    if [ -n "${install}" ]; then
        return 1
    fi
    local sim=$(cat /proc/cmdline | grep simulator=)
    if [ -n "${sim}" ]; then
        return 1
    fi
    return 0
}

#
# Which TTY should calvados con use
#
function platform_enable_calvados_con
{
    echo "Calvados console is not relevant"
}

#
# Which TTY should calvados aux use
#
function platform_enable_calvados_aux
{
    echo "Calvados aux is not relevant"
}

#
# Which TTY should XR con use
#
function platform_enable_xr_con
{
    echo "XR console is not relevant"
}

#
# Which TTY should XR aux use
#
function platform_enable_xr_aux
{
    echo "XR aux is not relevant"
}


#
# Should we allow the first serial port to have a tty login?
#
function platform_enable_host_login_on_first_serial
{
    skip_pd_functions
    if [ $? -ne 0 ]; then
        return;
    fi
    ACTIVE_SERIAL=''
}

# Do we want to enable host access on extra ttys? Good for development but
# is a security risk to leave host access visible.
#
function platform_starts_serial
{
    # Allow starts-serial to start serial login
    # If no active serial set, and on hostos, start serial on ttyS0 and ttyS1

    [ -z "$ACTIVE_SERIAL" ] && ACTIVE_SERIAL=/dev/ttyS[01]

    SPEED=`grep -o "console[^[:space:]]*" /proc/cmdline | cut -d ',' -f2`
}

#
# Check we have somewhere to write logs to. At early boot we might not have
# /var/log, so use /tmp until then
#
function platform_log_choose_log_file
{
    #
    # Where to log shell script output to. If /var/log is not available, use
    # /tmp which surely must be usable.
    #
    local PLATFORM_LOG=/var/log/platform.log

    #
    # If we are installing, append onto the host install logs as platform.log 
    # is not saved by xrnginstall in save_install_log (yet)
    #
    grep -q "root=/dev/ram" /proc/cmdline
    if [ $? -eq 0 ]; then
        PLATFORM_LOG=/var/log/host-install.log
        #
        # A tad obscure, but this is the FD that pxe_install uses for logging.
        # Only if we log to this will our output get into host-install.log
        #
        PLATFORM_LOG=/dev/fd/107
    fi

    PLATFORM_LOG_FILE=$PLATFORM_LOG
    if [ ! -f $PLATFORM_LOG_FILE ]; then
        touch $PLATFORM_LOG_FILE &>/dev/null
    fi

    if [ ! -w $PLATFORM_LOG_FILE ]; then
        local PLATFORM_TMP_LOG=/tmp/platform.early.log

        PLATFORM_LOG_FILE=$PLATFORM_TMP_LOG
    fi

    #
    # Sanity check it doesn't get too large
    #
    if [ -f $PLATFORM_LOG_FILE ]; then
        local FILESIZE=`filesize $PLATFORM_LOG_FILE`
        local MAX=1048576

        if [ $FILESIZE -ge $MAX ]; then
            #
            # Chop off the start of the file
            #
            sed -i '1,100d' $PLATFORM_LOG_FILE
        fi
    fi
}

#
# Utility function to log to our platform log file to help with
# debugging shell script flow.
#
function platform_log
{
    platform_log_choose_log_file

    local DATE=`date`
    echo "$DATE ($0): $*" >> $PLATFORM_LOG_FILE
}

#
# Utility function to log to our platform log file to help with
# debugging shell script flow.
#
function platform_log_error
{
    platform_log_console $*

    backtrace
}

#
# Some calvados scripts use log message but it is not defined. Get the output
# into our platform log if log_message is not defined.
#
function log_message
{
    platform_log $*
}

#
# Log to file and console
#
function platform_log_console 
{
    platform_log_choose_log_file

    local DATE=`date`
    echo "$DATE ($0): $*" | tee -a $PLATFORM_LOG_FILE

    return ${PIPESTATUS[0]}
}

#
# Keep this function aliased as a variable, so that if PLATFORM_LOG_EXEC is 
# not defined due to an error in including this file, the callers command 
# still executes
#
PLATFORM_LOG_EXEC=platform_log_exec
PLATFORM_LOG_EXEC_CONSOLE=platform_log_exec_console

#
# Log results to the log file only
#
function platform_log_exec
{
    platform_log_choose_log_file
    platform_log "exec: $*"
    platform_log "    : in cwd" `pwd`

    local PREFIX="`date -u`: -- "
    $* 2>&1 | sed "s/^/${PREFIX}/g" >>$PLATFORM_LOG_FILE 2>&1

    return ${PIPESTATUS[0]}
}

#
# Log results to the log file and the console
#
function platform_log_exec_console
{
    platform_log_choose_log_file
    platform_log "exec: $*"
    platform_log "    : in cwd" `pwd`

    local PREFIX="`date -u`: -- "
    $* 2>&1 | sed "s/^/${PREFIX}/g" | tee -a $PLATFORM_LOG_FILE

    return ${PIPESTATUS[0]}
}

#
# Print a shell backtrace
#
function backtrace () {
    local deptn=${#FUNCNAME[@]}

    for ((i=1; i<$deptn; i++)); do
        local func="${FUNCNAME[$i]}"
        local line="${BASH_LINENO[$((i-1))]}"
        local src="${BASH_SOURCE[$((i-1))]}"
        printf '%*s at: %s(), %s, line %s\n' $i '' $func $src $line # indent
    done
}

function platform_log_backtrace_ () {
    local depth=${#FUNCNAME[@]}

    for ((i=1; i<$depth; i++)); do
        local func="${FUNCNAME[$i]}"
        local line="${BASH_LINENO[$((i-1))]}"
        local src="${BASH_SOURCE[$((i-1))]}"
        printf '%*s at: %s(), %s, line %s\n' $i '' $func $src $line >> $PLATFORM_LOG_FILE
    done
}

function platform_log_backtrace () {
    platform_log_choose_log_file

    platform_log_backtrace_
}


# Console Mux details
#
function platform_get_mux_tty_settings
{
# No console mux
  echo "Should not come to mux tty"
}

function platform_post_xr_launch
{
# No XR LXC
  echo "Should not come to XR launch"
}

# App hosting Cgroup settings
function pd_tpa_cgroup_settings
{
        APP_CGROUP_MEM_NUMA_NODES=0
        APP_CGROUP_CPUSET_CPUS=0-7
        APP_CGROUP_CPU_SHARES=256
        APP_CGROUP_MEM_LIMIT_IN_BYTES=1G
}

function platform_copy_fru_json ()
{
    cp /usr/local/etc/fru-db-ncs5700.json /usr/local/etc/fru-db.json
}

function platform_info_setup ()
{
    platform_copy_fru_json
}


#
# Function to return whether the system is a multi-XR node system
# Returns 0 if not a multi-XR node system.
# Returns 1 if it is a multi-XR node system.
# Usage:
#     pd_is_multi_xr_node
#     local multi_node=$?
function pd_is_multi_xr_node()
{
    return 0
}

#
# Function to set BOARDTYPE variable
function get_board_type ()
{
     # If BOARDTYPE variable is already set, don't try to get it again.
     if [ -n "${BOARDTYPE}" ]; then
         return 0
     fi
 
     # Setup platform info if is not yet done from this routine as this is
     # usually the first function that any PI script will call.
     fpga_platform_setup
 
     BOARDTYPE=${MY_CARD_TYPE}
 
     # Make this variable available to subshells, so we don't have to get
     # this data again.
     export BOARDTYPE
}

#
# Function to set MY_CARD_PID variable
function get_my_card_pid ()
{
    # If MY_CARD_PID variable is already set, don't try to get it again.
    if [ -n "${MY_CARD_PID}" ]; then
        return 0
    fi

    # Setup platform info if is not yet done from this routine as this is
    # usually the first function that any PI script will call.
    fpga_platform_setup

    if [ -n "${CARD_PID}" ]; then
        MY_CARD_PID=${CARD_PID}
    else
        MY_CARD_PID="UNKNOWN"
    fi

    # Make this variable available to subshells, so we don't have to get
    # this data again.
    export MY_CARD_PID
}

function platform_copy_required_kernel_crash_modules() {
    local dest=$1

    cp /lib/modules/4*/kernel/drivers/md/dm-mod.ko $dest
    # On sim, disk shows as virtio device. Copy these modules only for sim.
    if [ -n "`cat /proc/cpuinfo |grep 'model name.*:.*VXR'`" ]; then
       cp /lib/modules/4*/kernel/drivers/virtio/virtio.ko $dest
       cp /lib/modules/4*/kernel/drivers/virtio/virtio_ring.ko $dest
       cp /lib/modules/4*/kernel/drivers/virtio/virtio_pci.ko $dest
       cp /lib/modules/4*/kernel/drivers/block/virtio_blk.ko $dest
    fi
    cp /lib/modules/4*/kernel/drivers/md/dm-bio-prison.ko $dest
    cp /lib/modules/4*/kernel/drivers/md/persistent-data/dm-persistent-data.ko $dest
    cp /lib/modules/4*/kernel/drivers/md/dm-bufio.ko $dest
    cp /lib/modules/4*/kernel/drivers/md/dm-thin-pool.ko $dest
}

function platform_load_required_kernel_crash_modules() {
    insmod /lib64/modules/dm-mod.ko >/dev/null
    # Only insmod virtio modules for sim.
    if [ -n "`cat /proc/cpuinfo |grep 'model name.*:.*VXR'`" ]; then
       insmod /lib64/modules/virtio.ko >/dev/null
       insmod /lib64/modules/virtio_ring.ko >/dev/null
       insmod /lib64/modules/virtio_pci.ko >/dev/null
       insmod /lib64/modules/virtio_blk.ko >/dev/null
    fi
    insmod /lib64/modules/dm-bio-prison.ko >/dev/null
    insmod /lib64/modules/dm-bufio.ko >/dev/null
    insmod /lib64/modules/dm-persistent-data.ko >/dev/null
    insmod /lib64/modules/dm-thin-pool.ko >/dev/null
}

function platform_binaries_set
{
   local set=('udevd' 'dmsetup')
   for prog in $(rpm -ql $(rpm -qa | grep thin-prov))
    do
        [ -d $prog ] && continue
        set=(${set[@]} $(basename $prog))
    done
    echo ${set[@]}
}

function platform_copy_bins_libs
{
    local crash_root=$1
    for prog in $(rpm -ql $(rpm -qa | grep thin-prov))
    do
        [ -d $prog ] && continue
        cp -d $prog $crash_root/$prog
    done
    cp /sbin/udevd $crash_root/sbin/
    mkdir -p $crash_root/run $crash_root/lib/udev/rules.d
    cp /lib/udev/rules.d/*dm*.rules $crash_root/lib/udev/rules.d/
    cp /usr/sbin/dmsetup $crash_root/usr/sbin/
}


# Function to start any binaries
function pd_start_bin()
{
    udevd --daemon
}


function pd_get_watchdog_timeout()
{

    local _tmp_addr=0
    AIKIDO_ID=$(lspci -nd ${CISCO_PCI_VENDOR_ID}:${AIKIDO_PCI_ID} | \
            awk '{ print $1 }');
    if [ -z "${AIKIDO_ID}" ]; then
        msg_log "ERROR: Could not determine Aikido FPGA PCI address via lspci"
        return 1
    fi
          
    _tmp_addr=0x$(lspci -vs $AIKIDO_ID 2>&1 | grep "Memory at" | head -1 | \
            awk '{ print $3 } ');

    if [ "$_tmp_addr" == "0x" ]; then
         msg_log "ERROR: Could not find Aikido base address for PCI ID:\
                 $AIKIDO_ID"
         return 2
      else
         # Make sure that the address it is a valid integer value
          echo "$(($_tmp_addr))" > /dev/null
         if [ $? -eq 0 ]; then
             AIKIDO_BASE=$_tmp_addr
             return 0
         else
             msg_log "ERROR: Invalid Aikido base address of '$_tmp_addr' \
                     for PCI ID:$AIKIDO_ID"
             return 3
         fi
    fi

                            
}



function pd_cfg_watchdog_timeout()
{


        pd_get_watchdog_timeout
        if [ $? -ne 0 ]; then
                msg_log "ERROR: Could not find Aikido base address"
                return 1
        fi

        source /usr/local/etc/fpga_platform_data_config 2>/dev/null
        FPGA_ID=$FPGA_ID_KANGER
        PLATFORM_ID=$PLATFORM_ID_CRATERLAKE

        pd_config_data_based_on_platform_and_fpga_id 

        if [ $? -ne 0 ]; then
                msg_log "ERROR: Could not find IP BLOCK"
                retrun 1
        fi

        local akido_stage1_address=$((  ${AIKIDO_BASE} + \
                                        ${WATCHDOG_IP_BLK_OFFSET} + \
                                        ${WDOG_STG1_CTRL_REG_OFFSET} ))
        local akido_stage2_address=$((  ${AIKIDO_BASE} + \
                                        ${WATCHDOG_IP_BLK_OFFSET} + \
         
                                        ${WDOG_STG2_CTRL_REG_OFFSET} ))


        local _stage1_timeout=$(($1 * 10))  # Convert in 100ms unit value
        local _stage2_timeout=$(($1 * 10))  # Convert in 100ms unit value

        _reg_addr=$(printf 0x%08X $(($akido_stage1_address)) )
        _reg_val=$((($_stage1_timeout << 16) | (1 << 4) | 1))
        _reg_val_hex=$(printf 0x%08X $(($_reg_val)) )
        pcimemwrite $_reg_addr 4 $_reg_val_hex

        _reg_addr=$(printf 0x%08X $(($akido_stage2_address)) )
        _reg_val=$((($_stage2_timeout << 16) | (0 << 4) | 1))
        _reg_val_hex=$(printf 0x%08X $(($_reg_val)) )
        pcimemwrite $_reg_addr 4 $_reg_val_hex

        return 0
 
}
