#!/bin/bash

source /ciena/scripts/utils.sh

# -----------------------------------------------------------------------------
#
# This script is used to walk through a set of ddr delay settings and to
# stress the system for each set of parameters.  Configuration settings
# can be controlled by u-boot environment variables (refer to the next
# comment section).
#
# The test is enabled by the memstress_enable u-boot environment variable.
# Data must be provided in the $MEMSTRESS_DATA file, with a set of integers
# on each line (for ddr_delay_clk, ddr_delay_dq and ddr_delay_cmd). This file
# is consumed, and results are provided in $MEMSTRESS_LOG.
#
# To start a test:
# - obtain the u-boot helpers: $UBOOT_FW_CONFIG
#                              $UBOOT_PRINTENV
#                              $UBOOT_SETENV
#   (these currently need to be downloaded)
# - use UBOOT_SETENV to set memstress_enable:
#   /mnt/log/fw_setenv memstress_enable 1
# - set up values to test in $MEMSTRESS_DATA
# - copy this script into the postrcS file:
#   cp /ciena/scripts/memstress_ddr_delay /mnt/sysfs/system/postrcS
# - reboot
# - the script will then run automatically over each $MEMSTRESS_DATA setting
#   and write output into $MEMSTRESS_LOG.
#
# -----------------------------------------------------------------------------
# Configuration settings
#
# These parameters can be overriden by u-boot parameters (use the lowercase
# version of the same name).
#
MEMSTRESS_INTERVAL=300                  # time in seconds for each iteration
MEMSTRESS_ALLOC=50                      # MB memory allocated for each instance
MEMSTRESS_INSTANCE=4                    # how many instances to start
MEMSTRESS_PATH=/ciena/bin/memstress     # where to find the memstress binary
# -----------------------------------------------------------------------------

# -------------------------------------
# Hardcoded input and output paths
MEMSTRESS_DATA=/mnt/log/memstress_data  # input (space separated CLK DQ CMD)
MEMSTRESS_LOG=/mnt/log/memstress_log    # log of test results

# -------------------------------------
# Hack to allow u-boot environment manipulation
UBOOT_FW_CONFIG=/mnt/log/fw_env.config
UBOOT_PRINTENV=/mnt/log/fw_printenv
UBOOT_SETENV=/mnt/log/fw_setenv

SYSLOG_ALERT="logger -p USER.ALERT -t $(basename $0)"
SYSLOG_ERR="logger -p USER.ERR  -t $(basename $0)"
SYSLOG_INFO="logger -p USER.INFO  -t $(basename $0)"

# -----------------------------------------------------------------------------
exit_err()
{
    $SYSLOG_ERR $*
    print_app_line "EXIT: $*"
    exit
}

# -----------------------------------------------------------------------------
uboot_getenv()
{
    local var_name=$1
    local var_default=$2

    temp_val=$($UBOOT_PRINTENV $var_name 2> /dev/null | awk -F = '{print $2}')
    
    # If we found a non-null value then echo it, otherwise echo the
    # default value if one was provided.
    if [ -n "$temp_val" ] ; then
        echo $temp_val
    else
        if [ -n "$var_default" ] ; then
            echo $var_default
        fi
    fi
}

# -----------------------------------------------------------------------------
uboot_setenv()
{
    local var_name=$1
    local var_value=$2

    # ignore errors here, as this seems to fail even when it works.
    $UBOOT_SETENV $var_name $var_value 2> /dev/null
}

# -----------------------------------------------------------------------------
check_exist_or_exit()
{
    if [ ! -f "$1" ] ; then
        exit_err "can not find $1"
    fi
}

# -----------------------------------------------------------------------------
check_exec_or_exit()
{
    check_exist_or_exit $1
    if [ ! -x "$1" ] ; then
        exit_err "can not find $1 with exec permission"
    fi
}

# -----------------------------------------------------------------------------
enable_root_account()
{
    touch /mnt/sysfs/system/enableRoot
    rm -f /etc/securetty
}

# -----------------------------------------------------------------------------
setup()
{
    # Make sure we have access to the u-boot enviroment
    check_exec_or_exit $UBOOT_PRINTENV
    check_exec_or_exit $UBOOT_SETENV

    # If there is no config file in /etc, make sure we can get one
    # from elsewhere
    if [ ! -f "/etc/$(basename $UBOOT_FW_CONFIG)" ] ; then
        check_exist_or_exit $UBOOT_FW_CONFIG
        cp $UBOOT_FW_CONFIG /etc
    fi

    if [ -z "$(uboot_getenv memstress_enable)" ] ; then
        exit_err "memstress_enable is not present"
    fi

    # Allow u-boot environment to override these settings
    #
    MEMSTRESS_PATH=$(uboot_getenv "memstress_path" $MEMSTRESS_PATH)
    MEMSTRESS_INTERVAL=$(uboot_getenv "memstress_interval" $MEMSTRESS_INTERVAL)
    MEMSTRESS_ALLOC=$(uboot_getenv "memstress_alloc" $MEMSTRESS_ALLOC)
    MEMSTRESS_INSTANCE=$(uboot_getenv "memstress_instance" $MEMSTRESS_INSTANCE)

    check_exec_or_exit $MEMSTRESS_PATH
    check_exist_or_exit $MEMSTRESS_DATA

    enable_root_account
}

# -----------------------------------------------------------------------------
setup_first_run_and_reboot()
{
    touch $MEMSTRESS_LOG
    echo "$(date) memstress initialized (rebooting)"        >> $MEMSTRESS_LOG
    echo "$(date) MEMSTRESS_PATH     = $MEMSTRESS_PATH"     >> $MEMSTRESS_LOG
    echo "$(date) MEMSTRESS_INTERVAL = $MEMSTRESS_INTERVAL" >> $MEMSTRESS_LOG
    echo "$(date) MEMSTRESS_ALLOC    = $MEMSTRESS_ALLOC"    >> $MEMSTRESS_LOG
    echo "$(date) MEMSTRESS_INSTANCE = $MEMSTRESS_INSTANCE" >> $MEMSTRESS_LOG

    print_app_line "rebooting to start stress tests"
    uboot_setenv ddr_verbose 1
    reboot
    exit
}

# -----------------------------------------------------------------------------
cleanup_and_reboot()
{
    echo "$(date) memstress complete (rebooting)" >> $MEMSTRESS_LOG
    rm $MEMSTRESS_DATA
    uboot_setenv ddr_delay_clk
    uboot_setenv ddr_delay_dq
    uboot_setenv ddr_verbose
    print_app_line "rebooting back to normal settings"
    reboot
    exit
}

# -----------------------------------------------------------------------------
next_parameters()
{
    # Grab the current values of the ddr_delay parameters.
    # Set the no_env flag if anything is missing.
    ddr_delay_clk=$(uboot_getenv ddr_delay_clk)
    ddr_delay_dq=$(uboot_getenv ddr_delay_dq)
    ddr_delay_cmd=$(uboot_getenv ddr_delay_cmd)

    if [ -z "$ddr_delay_clk" ] ; then
        no_env=1
    fi

    if [ -z "$ddr_delay_dq" ] ; then
        no_env=1
    fi

    if [ -z "$ddr_delay_cmd" ] ; then
        no_env=1
    fi

    # If we have no environment settings, and we have no more data items
    # to use, then clean-up after ourselves an reboot to a normal setup.
    if [ -n "$no_env" ] ; then
        if [ "$(cat $MEMSTRESS_DATA | wc -l)" -eq "0" ] ; then
            cleanup_and_reboot
        fi
    fi

    # Find the next values to use, and then use sed to remove the
    # data from the input file.
    next_ddr_delay_clk=$(head -n1 $MEMSTRESS_DATA | awk '{print $1}')
    next_ddr_delay_dq=$(head -n1 $MEMSTRESS_DATA | awk '{print $2}')
    next_ddr_delay_cmd=$(head -n1 $MEMSTRESS_DATA | awk '{print $3}')
    sed -i '1d' $MEMSTRESS_DATA

    # Put the next parameter values into the u-boot environment.  These
    # will be used on the next reboot.
    uboot_setenv ddr_delay_clk $next_ddr_delay_clk
    uboot_setenv ddr_delay_dq $next_ddr_delay_dq
    uboot_setenv ddr_delay_cmd $next_ddr_delay_cmd

    # If we get this far with the no_env flag set, then it indicates
    # this is our first run.
    if [ -n "$no_env" ] ; then
        setup_first_run_and_reboot
    fi
}

# -----------------------------------------------------------------------------
log_memstress()
{
    echo "$(date) memstress $1: ddr_delay_clk=$ddr_delay_clk ddr_delay_dq=$ddr_delay_dq ddr_delay_cmd=$ddr_delay_cmd" >> $MEMSTRESS_LOG
}

# -----------------------------------------------------------------------------
start_memstress()
{
    log_memstress START
    
    for ((i=0; i < MEMSTRESS_INSTANCE; i++)) ; do
        $MEMSTRESS_PATH $MEMSTRESS_ALLOC > /dev/null &
    done
}

# -----------------------------------------------------------------------------
start_reboot_timer()
{
    # Start this subshell in the background to force the system to reboot
    # once the test interval has completed.
    (
        sleep $MEMSTRESS_INTERVAL
        log_memstress STOP
        print_app_line_cr "memstress cycle complete, rebooting to next"
        sync ; reboot
    ) &
}

# main ------------------------------------------------------------------------
echo
setup
next_parameters
start_memstress
start_reboot_timer

print_app_line "memstress started, reboot in $MEMSTRESS_INTERVAL seconds"

