#!/bin/bash
#
# Neil McGill
#
# The file will be sync-ed from host to cal/xr LXCs at the running time.
# Therefore, any SMU on pd-dunctions file should be targeted at host 
# with system reload.
#
# Copyright (c) 2014-2019, 2021 by Cisco Systems, Inc.
# All rights reserved.
#

source_list=(
    /etc/cisco/xr_sysctl.rc
    /etc/rc.d/init.d/baremetal-functions.sh
    /etc/rc.d/init.d/xrv9k-mode-functions.sh
    /etc/rc.d/init.d/xrv9k.profile
    /etc/rc.d/init.d/hw_profiles_functions
    /etc/rc.d/init.d/calvados_bootstrap.cfg
    /etc/rc.d/init.d/huge.sh
)

for s in ${source_list[@]}; do
    if [ -f $s ]; then
        source $s
    fi
done

#
# 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 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)
    #
    if [[ "${IOS_XR_ROOT}" == "/dev/ram" ]] ; 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_
}

function pd_get_boot_dev()
{
    if [[ -b /dev/vda1 ]]; then
        PD_BOOT_DEV="/dev/vda1"
    elif [[ -b /dev/sda1 ]]; then
        PD_BOOT_DEV="/dev/sda1"
    elif [[ -b /dev/nvme0n1p1 ]]; then
        PD_BOOT_DEV="/dev/nvme0n1p1"
    else
        PD_BOOT_DEV=""
    fi
    echo $PD_BOOT_DEV
}

function is_vxr()
{
    lspci -nn | grep -q -i "RAM memory.*Xilinx Corporation Device.*10ee:0ccc"
    if [[ $? -eq 0 ]]; then
        platform_log "xrv9000: VXR DEVELOPMENT MODE IMAGE (found CCC)"
        return 1
    fi

    return 0
}

function is_simulation_platform()
{
    is_vxr
    if [[ $? -eq 1 ]]; then
        return 1
    fi

    if [[ "$IOS_XR_SIMULATOR" == "true" ]]; then
        platform_log "xrv9000: SIMULATION MODE IMAGE"
        return 1
    fi

    return 0
}

function on_sim()
{
    if [[ "${SIMULATION}" == "" ]]; then
        #
        # Only log the first time we decide on the simulation setting to avoid
        # noise and repeated looking up of the information we need.
        # 
        is_simulation_platform

        if [[ $? != 0 ]]; then
            readonly SIMULATION=1
        else
            readonly SIMULATION=0
        fi
    fi

    #
    # Also call other mode check functions so we do not need to change all 
    # callers.
    #
    platform_get_settings

    return $SIMULATION
}

function on_production()
{
    if [[ "$SUNSTONE_PRODUCTION" = "" ]]; then

        if [[ "$DEVELOPMENT" = "0" && "$SIMULATION" = "0" ]]; then
            readonly SUNSTONE_PRODUCTION=1
        else
            readonly SUNSTONE_PRODUCTION=0
        fi
    fi
}

function is_cloud_platform()
{
    if [[ "$VGA_CONSOLE_MODE" != "" ]]; then
        if [[ "$VGA_CONSOLE_MODE" = "TRUE" ]]; then
            platform_log "xrv9000: CLOUD MODE IMAGE"
            true
            return
        else
            false
            return
        fi
    fi

    if [[ "$IOS_XRV9K_CLOUD" == "true" ]]; then
        platform_log "xrv9000: CLOUD MODE IMAGE"
        true
        return
    fi

    false
}

function on_cloud()
{
    if [[ "${SUNSTONE_CLOUD_PLATFORM}" == "" ]]; then
        #
        # Only log the first time we decide on the simulation setting to avoid
        # noise and repeated looking up of the information we need.
        # 
        is_cloud_platform
        if [[ $? == 0 ]]; then
            readonly SUNSTONE_CLOUD_PLATFORM=1
        else
            readonly SUNSTONE_CLOUD_PLATFORM=0
        fi
    fi

    return $SUNSTONE_CLOUD_PLATFORM
}

function is_development_platform()
{
    if [[ "$DEVELOPMENT_MODE" != "" ]]; then
        if [[ "$DEVELOPMENT_MODE" = "TRUE" ]]; then
            platform_log "xrv9000: DEVELOPMENT MODE IMAGE"
            true
            return
        else
            false
            return
        fi            
    fi

    if [[ "$IOS_XR_DEVELOPMENT" == "true" ]]; then
        platform_log "xrv9000: DEVELOPMENT MODE IMAGE"
        true
        return
    fi

    false
}

function on_dev()
{
    if [[ "${DEVELOPMENT}" == "" ]]; then
        #
        # Only log the first time we decide on the simulation setting to avoid
        # noise and repeated looking up of the information we need.
        # 
        is_development_platform
        if [[ $? == 0 ]]; then
            readonly DEVELOPMENT=1
        else
            readonly DEVELOPMENT=0
        fi
    fi

    return $DEVELOPMENT
}

#
# General use term codes for color
#
function platform_init_tty_termcodes
{
    ESC="["
    _nl=`echo X | tr X '\012'`
    _cr=`echo X | tr X '\015'`
    _eol="${_cr}${_nl}"

    DULL=0
    FG_BLACK=30
    FG_RED=31
    FG_GREEN=32
    FG_YELLOW=33
    FG_BLUE=34
    FG_MAGENTA=35
    FG_CYAN=36
    FG_WHITE=37
    FG_NULL=00

    BG_NULL=00
    BG_BLACK=40
    BG_RED=41
    BG_GREEN=42
    BG_YELLOW=43
    BG_BLUE=44
    BG_MAGENTA=45
    BG_CYAN=46
    BG_WHITE=47

    RESET="${ESC}${DULL};${FG_WHITE};${BG_NULL}m"

    BLACK="${ESC}${DULL};${FG_BLACK}m"
    RED="${ESC}${DULL};${FG_RED}m"
    GREEN="${ESC}${DULL};${FG_GREEN}m"
    YELLOW="${ESC}${DULL};${FG_YELLOW}m"
    BLUE="${ESC}${DULL};${FG_BLUE}m"
    MAGENTA="${ESC}${DULL};${FG_MAGENTA}m"
    CYAN="${ESC}${DULL};${FG_CYAN}m"
    WHITE="${ESC}${DULL};${FG_WHITE}m"

    ON_BLACK="${ESC}${DULL};${BG_BLACK}m"
    ON_RED="${ESC}${DULL};${BG_RED}m"
    ON_GREEN="${ESC}${DULL};${BG_GREEN}m"
    ON_YELLOW="${ESC}${DULL};${BG_YELLOW}m"
    ON_BLUE="${ESC}${DULL};${BG_BLUE}m"
    ON_MAGENTA="${ESC}${DULL};${BG_MAGENTA}m"
    ON_CYAN="${ESC}${DULL};${BG_CYAN}m"
    ON_WHITE="${ESC}${DULL};${BG_WHITE}m"
}

#
# Check if a given serial port exists by checking to see if it has a device 
# driver in /proc/tty. This is safer than writing to the device which can
# hang (hello ESXi).
#
# cat /proc/tty/driver/serial
# serinfo:1.0 driver revision:
# 0: uart:16550A port:000003F8 irq:4 tx:14383 rx:30 RTS|CTS|DTR|DSR|CD
# 1: uart:16550A port:000002F8 irq:3 tx:54695 rx:656 RTS|CTS|DTR|DSR|CD
# 2: uart:unknown port:000003E8 irq:4
# 3: uart:unknown port:000002E8 irq:3
#
function serial_device_exists
{
    local DEVICE=$1

    case $DEVICE in
        hvc*)
            find /sys/devices | grep -q "/$DEVICE/dev"
            if [[ $? -eq 0 ]]; then
                true
                return
            fi

            false
            return
        ;;
    esac

    cat /proc/tty/driver/serial | \
        grep -v unknown | \
        sed 's/^/ttyS/g' | \
        grep -q $DEVICE
}

function platform_use_virtio_console
{
    serial_device_exists hvc0
    if [[ $? -eq 0 ]]; then
        TTY_SER0=hvc0
    else
        TTY_SER0=ttyS0
    fi

    serial_device_exists hvc1
    if [[ $? -eq 0 ]]; then
        TTY_SER1=hvc1
    else
        TTY_SER1=ttyS1
    fi

    serial_device_exists hvc2
    if [[ $? -eq 0 ]]; then
        TTY_SER2=hvc2
    else
        TTY_SER2=ttyS2
    fi

    serial_device_exists hvc3
    if [[ $? -eq 0 ]]; then
        TTY_SER3=hvc3
    else
        TTY_SER3=ttyS3
    fi
}

#
# As we do not have virtio builtin to the kernel (as it would bloat the 
# kernel) we need to do some tricks that if we find virtion, we force
# console output to use it.
#
function switch_to_virtio_console_if_present()
{
    if [[ $SWITCHED_TO_VIRTIO_CONSOLE != "" ]]; then
        if [[ $SWITCHED_TO_VIRTIO_CONSOLE = 1 ]]; then
            ln -sf /dev/hvc0 /dev/console
            exec &>/dev/hvc0
            true
            return
        fi

        false
        return;
    fi

    serial_device_exists hvc0
    if [[ $? -eq 0 ]]; then
        SWITCHED_TO_VIRTIO_CONSOLE=1
        true
    else
        SWITCHED_TO_VIRTIO_CONSOLE=0
        false
    fi
}

#
# Normally does nothing, but for virtio, will redirect the current
# console to virtio
#
function platform_redirect_output()
{
    switch_to_virtio_console_if_present
}

#
# Work out what session goes where
#
#            VGA    |  Ser0  |   Ser1   |  Ser2    | Ser3
#           --------+--------+----------+----------+-------
# vga+dev    XR con | XR aux | Calv Con | Calv Aux | Host
# vga+prod   XR con | XR aux | Calv con | Calv Aux |
# product    -      | XR con | Xr aux   | Calv con | Calv Aux
# dev        -      | XR con | Xr aux   | Calv con | Host
# sim        -      | XR con | Xr aux   | Calv con | Host
#
function platform_init_tty_settings
{
    platform_use_virtio_console

    platform_init_tty_termcodes

    TTY_HOST=
    TTY_XR_CON=$TTY_SER0
    TTY_XR_AUX=$TTY_SER1
    TTY_CALVADOS_CON=$TTY_SER2
    TTY_CALVADOS_AUX=$TTY_SER3

    #
    # Ser 3 replaces calvados for host access
    #
    if (( $DEVELOPMENT )); then
        TTY_HOST=$TTY_SER3
        TTY_CALVADOS_AUX=
    fi

    if (( $SIMULATION )); then
        TTY_HOST=$TTY_SER3
        TTY_CALVADOS_AUX=
    fi

    if on_baremetal; then
        TTY_XR_CON=$TTY_SER0
        TTY_XR_AUX=
        TTY_CALVADOS_CON=$TTY_VGA
        TTY_CALVADOS_AUX=
        TTY_HOST=$TTY_SER1
    # favour aws/openstack console/terminal configuration
    elif is_xrv9k_aws; then
        TTY_XR_CON=$TTY_VGA
        TTY_XR_AUX=
        TTY_CALVADOS_CON=$TTY_SER0
        TTY_CALVADOS_AUX=
    elif (( $SUNSTONE_CLOUD_PLATFORM )); then
        TTY_XR_CON=$TTY_VGA
        TTY_XR_AUX=$TTY_SER0
        TTY_CALVADOS_CON=$TTY_SER1
        TTY_CALVADOS_AUX=$TTY_SER2
    fi

    #
    # On openstack the default is to have 2 serial ports. XR console is on 
    # the vga port, so we know we have XR aux and calvados console. However
    # this might mean that we have no serial port for calvados or for host
    # access. Check them here
    #
    if [[ "$TTY_CALVADOS_AUX" != "" ]]; then
        serial_device_exists $TTY_CALVADOS_AUX
        if [[ $? -ne 0 ]]; then
            platform_log "Calvados aux port $TTY_CALVADOS_AUX is not available"
            TTY_CALVADOS_AUX=
        fi
    fi

    if [[ "$TTY_HOST" != "" ]]; then
        serial_device_exists $TTY_HOST
        if [[ $? -ne 0 ]]; then
            platform_log "Host serial port $TTY_HOST is not available"
            TTY_HOST=
        fi
    fi

    #
    # Map device name "x" to /dev/x
    #
    DEV_TTY_VGA=/dev/$TTY_VGA
    DEV_TTY_XR_CON=/dev/$TTY_XR_CON
    DEV_TTY_XR_AUX=/dev/$TTY_XR_AUX
    DEV_TTY_CALVADOS_CON=/dev/$TTY_CALVADOS_CON
    DEV_TTY_CALVADOS_AUX=/dev/$TTY_CALVADOS_AUX
}

#
# Convert a tty name to human readable
#
function tty_to_name
{
    case $1 in
        $TTY_VGA) echo "VGA console";;
        $TTY_SER0) echo "1st serial port";;
        $TTY_SER1) echo "2nd serial port";;
        $TTY_SER2) echo "3rd serial port";;
        $TTY_SER3) echo "4th serial port";;
        *hvc0*) echo "1st virtual serial port";;
        *hvc1*) echo "2nd virtual serial port";;
        *hvc2*) echo "3rd virtual serial port";;
        *hvc3*) echo "4th virtual serial port";;
        *) echo $*;;
    esac
}

function tty_help
{
    local WHAT="$1"
    local TTY=`tty_to_name $2`

    if [[ "$TTY" != "" ]]; then
        printf '%-24s will start on the %s\n' "$WHAT" "$TTY"

        platform_log `printf '%-24s will start on the %s' "$WHAT" "$TTY"`
    else
        platform_log `printf '%-24s disabled' "$WHAT"`
    fi
}

function read_and_copy_bootstrap_profile
{
    if [[ "$VMTYPE" != "hostos" ]]; then
        platform_log "PROFILE-METADATA: VMTYPE '$VMTYPE' don't process profile meta data"
        return
    fi
    
    # Temporary stored mount point location before copy to XR
    local HOST_MOUNT=/mnt/md_profile
    local XR_PROFILE_MOUNT=/etc/rc.d/init.d
    local IOSXRV_PROFILE=xrv9k.profile
    local IOSXRV_PROFILE_YAML=xrv9k.yaml

    if [[ -d "$HOST_MOUNT" ]]; then
        # create this once and this check to prevent doing it
        # multiple times.
        return
    fi

    mkdir -p $HOST_MOUNT
    if [[ $? -ne 0 ]]; then
        platform_log_error "PROFILE-METADATA: Failed CMD: mkdir -p $HOST_MOUNT"
        return
    fi

    DEV=`blkid -t LABEL="config-1" -odevice`
    if [[ "$DEV" = "" ]]; then
        return
    fi

    platform_log "PROFILE-METADATA: Found XRv9k device '$DEV' with label config-1"

    mount $DEV $HOST_MOUNT -o loop
    if [[ $? -ne 0 ]]; then
        platform_log_error "PROFILE-METADATA: Failed mount $DEV to $XR_PROFILE_MOUNT/$IOSXRV_PROFILE_YAML"
        return
    fi        

    # Succesfully mounted - now look for a bootstrap CLI file
    platform_log "PROFILE-METADATA: Mounted $DEV to $XR_PROFILE_MOUNT"

    # check to see if we have profile meta data
    if [[ -e $HOST_MOUNT/$IOSXRV_PROFILE_YAML ]]; then
        platform_log "PROFILE-METADATA: Found Profile Meta-data: $HOST_MOUNT/$IOSXRV_PROFILE_YAML"
        cp $HOST_MOUNT/$IOSXRV_PROFILE_YAML $XR_PROFILE_MOUNT/$IOSXRV_PROFILE_YAML
        if [[ $? -eq 0 ]]; then
            platform_log "PROFILE-METADATA: Copied profile $HOST_MOUNT/$IOSXRV_PROFILE_YAML to $XR_PROFILE_MOUNT/$IOSXRV_PROFILE_YAML"
        else
            platform_log_error "PROFILE-METADATA: Failed to copy profile $HOST_MOUNT/$IOSXRV_PROFILE_YAML to $XR_PROFILE_MOUNT/$IOSXRV_PROFILE_YAML"
            return
        fi

        platform_log "PROFILE-METADATA: calling `which python` to convert \
                      $XR_PROFILE_MOUNT/$IOSXRV_PROFILE_YAML to \
                      $XR_PROFILE_MOUNT/$IOSXRV_PROFILE"

        `which python` $XR_PROFILE_MOUNT/xrv9k_profile_yaml_parser.py \
            --input $XR_PROFILE_MOUNT/$IOSXRV_PROFILE_YAML            \
            --output $XR_PROFILE_MOUNT/$IOSXRV_PROFILE
        
        source $XR_PROFILE_MOUNT/$IOSXRV_PROFILE
        platform_log "PROFILE-METADATA: Source Profile Meta-data Profile:$PROFILE"
    fi
    platform_log "PROFILE-METADATA: Unmount $DEV from $HOST_MOUNT"
    umount $HOST_MOUNT -d
}

#
# Pull together various platform settings into one place.
#
function platform_get_settings()
{
    if [[ "$PLATFORM_GET_SETTINGS_CALLED" != "" ]]; then
        return
    fi
    PLATFORM_GET_SETTINGS_CALLED=0

    #
    # Source calvados settings
    #
    . /etc/rc.d/init.d/calvados_bootstrap.cfg

    #
    # Are we in simulator (VXR) mode?
    #
    on_sim

    #
    # Or development mode? We enable ssh to host for this.
    #
    on_dev

    #
    # Or in baremetal mode with VGA console enabled.
    #
    on_baremetal

    #
    # Or in cloud mode with VGA console enabled.
    #
    on_cloud

    #
    # Or in production mode?
    #
    on_production

    #
    # Work out which TTYs go where
    #
    platform_init_tty_settings
    
    #
    # read bootstrap profile info
    #
    read_and_copy_bootstrap_profile

    #
    # Retrieve the current hardware profile 
    #
    declare -F platform_hw_profile_read_cmdline &>/dev/null && \
        platform_hw_profile_read_cmdline 
}

#
# Add a field to the cmdline only if it is not present already
#
function cmdline_add_uniq()
{
    local cmdline=$1
    local toadd=$2
    local target=$3

    echo $cmdline | grep -q "\<$toadd\>" -
    if [[ $? -ne 0 ]]; then
        sed -i "s;\(platform=xrv9k\);\1 $toadd;g" $target
    fi
}

#
# Pass all cisco specific command line arguments specified in "toadd" 
# into "cmdline" and storing the results in "target"
#
function cmdline_add()
{
    local toadd=$1
    local cmdline=$2
    local target=$3
    local token=

    for token in $toadd
    do
        if [[ $token =~ ^__ ]]; then
            cmdline_add_uniq "$cmdline" "$token" "$target"
        fi
    done
}

function platform_version
{
    dmidecode -s bios-version | grep "C220" | awk -F. '{print $1}'
}

#
# Any modules to exclude?
#
function create_blacklist_hostos 
{
    #
    # List of modules to block in initramfs (via grub) and in the host
    #
    # Exclude those 10G and SR-IOV NICs that are dedicated for the dataplane.
    # However, aws needs ixgbevf for mgmt, thus it cannot be blacklisted.
    #
    PLATFORM_MODULES_TO_BLACKLIST="ixgbevfa9k ixgbea9k ixgb ixgbe i40e i40evf vmxnet3 bcm87xx enic"
    if on_baremetal; then
        local xrv9k_platform=$(platform_version)
        PLATFORM_MODULES_TO_BLACKLIST="ixgbevfa9k ixgbea9k ixgb i40e i40evf vmxnet3 bcm87xx enic"
        case $xrv9k_platform in
        C220M4)
            PLATFORM_MODULES_TO_BLACKLIST+=" ixgbe"
            ;;
        C220M5)
            ;;
        *)
            platform_log_console "Unsupported platform ($xrv9k_platform), halt system!"
            platform_log_exec_console halt -f;
            ;;
        esac
    elif ! is_xrv9k_aws; then
        PLATFORM_MODULES_TO_BLACKLIST+=" ixgbevf"
    fi

    local rootdir="$1"
    local blacklist=$rootdir/etc/modprobe.d/blacklist.conf

    local module
    for module in $PLATFORM_MODULES_TO_BLACKLIST
    do
        echo "blacklist $module" >>$blacklist
        echo "install $module /bin/false" >>$blacklist

        echo "blacklist $module" >> $rootdir/etc/modprobe.conf
        echo "blacklist $module" >> $rootdir/etc/modprobe.d/$module.conf

        echo "alias $module /dev/null" >> $rootdir/etc/modprobe.conf
        echo "options $module needed_option=0" >> $rootdir/etc/modprobe.conf
    done
}

#
# Any modules to exclude at the grub level? This works for initramfs
#
function create_blacklist_grub 
{
    local menu=$1

    #
    # The host should only be using virtio and e1000 for control interfaces.
    # Exclude 10G and SR-IOV functions that we want to reserve for the 
    # dataplane.
    #
    local module
    for module in $PLATFORM_MODULES_TO_BLACKLIST
    do
        sed -i -e "s;platform=;$module.blacklist=true platform=;" $menu
    done
}

#
# Patch the linux command line arguments. This can be either embedded in the 
# grub config or as its own file.
#
function sunstone_patch_grub_cmdline()
{
    local menu=$1

    if [[ ! -f $menu ]]; then
        return
    fi

    platform_get_settings

    #
    # Platform name
    #
    sed -i -e "s;^title.*;title xrv9000;" $menu

    #
    # Make sure our platform type is set correctly.
    #
    grep -q "platform=xrv9k" $menu
    if [[ $? -ne 0 ]]; then
        sed -i 's/\(boardtype=\)/platform=xrv9k \1/g' $menu
    fi

    local host_cmdline=$IOS_XR_CMDLINE
    local dst_cmdline=`cat $menu`

    #
    # Bleed all parameters defined in the host into the VM cmdline
    #
    cmdline_add "$host_cmdline" "$dst_cmdline" "$menu"

    if (( $DEVELOPMENT )); then
        grep -q "__development=true" $menu
        if [[ $? -ne 0 ]]; then
            platform_log "Patch cmdline for DEVELOPMENT mode"

            #
            # Enable simulator aka development mode
            #
            sed -i 's/\(platform=xrv9k\)/\1 __development=true/g' $menu
            sed -i 's;^title .*;title xrv9000-DEVELOPMENT;g' $menu
        fi
    fi

    if (( $SIMULATION )); then
        grep -q "simulator=true" $menu
        if [[ $? -ne 0 ]]; then
            platform_log "Patch cmdline for SIMULATION mode"

            #
            # Enable simulator aka simulation mode
            #
            sed -i 's/\(platform=xrv9k\)/\1 simulator=true/g' $menu
            sed -i 's;^title .*;title xrv9000-SIMULATION;g' $menu
        fi
    fi

    if (( $SUNSTONE_CLOUD_PLATFORM )); then
        grep -q "__cloud=true" $menu
        if [[ $? -ne 0 ]]; then
            #
            # Enable cloud mode tweaks. Basically move the XR console from 
            # serial to VGA.
            #
            sed -i 's/\(platform=xrv9k\)/\1 __cloud=true/g' $menu
            sed -i 's/\(platform=xrv9k\)/\1 vga=791/g' $menu
            sed -i "s/console=$TTY_SER0 //g" $menu

            grep -q "console serial" $menu
            if [[ $? -ne 0 ]]; then
                sed -i '/^timeout .*/aterminal --timeout=5 console serial' $menu
                sed -i "/^timeout .*/a\
serial --unit=0 --speed=$TTY_SPEED" $menu
            fi
        fi
    else
        grep -q "console serial" $menu
        if [[ $? -ne 0 ]]; then
            sed -i '/^timeout .*/aterminal --timeout=5 console serial' $menu
            sed -i "/^timeout .*/a\
serial --unit=0 --speed=$TTY_SPEED" $menu
        fi
    fi

    #
    # Modify for virtio serial support if needed
    #
    if [[ "$TTY_XR_CON" = "hvc0" ]]; then
        sed -i "s/console=ttyS0 /console=$TTY_XR_CON hvc_iucv=4 /g" $menu
        sed -i "/serial --unit=0 --speed=115200/d" $menu
        sed -i "/terminal --timeout=5 console serial/d" $menu
    fi

    #
    # Make sure cgroups are enabled for LXC
    #
    sed -i -e "s; cgroup_disable=memory;;" $menu

    #
    # Add support for 10G niantic passthroug (intel_iommu=on)
    # In the baremetal, we need to set intel_iommu off
    #
    if on_baremetal; then

# CSCvn05746, vRR only UCS M5 Appliance
# will be removed when supporting vPE/vBNG Appliance
        grep -q "__hw_profile=" $menu
        if [[ $? -ne 0 ]]; then
            sed -i -e "s;platform=;__hw_profile=vrr platform=;" $menu
        else
            sed -i -e "s;__hw_profile=[a-zA-Z]* ;__hw_profile=vrr ;" $menu
        fi

        PROFILE=vrr
        platform_log_console "force hw_profile to be $PROFILE"

        sed -i -e "s;platform=;intel_iommu=off platform=;" $menu
    else
        sed -i -e "s;platform=;intel_iommu=on platform=;" $menu
    fi

    #
    # Disable Power Mgmt Feature
    #
    sed -i -e "s;platform=;pcie_aspm=off platform=;" $menu

    #
    # Enable the noop elevator as there is no need for our kernel to reorder
    # IO requests as the host kernel will do that anyway.
    #
    grep -q "elevator=" $menu
    if [[ $? -ne 0 ]]; then
        platform_log "Patch cmdline for elevator=noop"

        sed -i 's/\(platform=xrv9k\)/\1 elevator=noop/g' $menu
    fi

    #
    # Need to blacklist modules in grub so initramfs sees this
    #
    create_blacklist_grub $menu

    local hugepage_setting=$(get_hugepage_setting)
    platform_log_console "Based on system memory and socket number, use the folliwng huge page setting:"
    platform_log_console "    $hugepage_setting"
    sed -i "s/\(platform=xrv9k\)/\1 ${hugepage_setting}/g" $menu
    
    local isolcpus=$(platform_hw_profile_get_isolcpus_cfg)
    if [[ ${isolcpus} != "" ]]; then
        platform_log_console "Isolate CPUs (for Data Plane) Setting: $isolcpus"
        sed -i "s/\(platform=xrv9k\)/\1 ${isolcpus}/g" $menu
    fi
    
    #
    # Do we ever want quiet to be on ? Noisier boot for debugging at least 
    # until the platform is stable.
    #
    sed -i -e "s; quiet;;" $menu

    #
    # Timeout for the end user to select images
    #
    sed -i -e 's;^timeout *[0-9]*$;timeout 5;' $menu

    #
    # Clean up the cmdline
    #
    sed -i 's/  / /g' $menu

    #
    # Perform any last modifications of the cmdline common to all VMs
    #
    platform_update_cmdline $menu

    platform_log "xrv9000 grub config ($menu)"
    platform_log_exec cat $menu
    platform_log_exec_console cat $menu
}

function platform_update_cmdline()
{
    local cmdline=$1

    #
    # Remove the initial user-specified huge page hint setting now, it is only 
    # used in baking and not carried on into other containers.
    #
    sed -i -e "s/__hugepages=[0-9]* //g" $cmdline
}

#
# Tweaks set at install time
#
function pd_pxe_update_boot_grub()
{
    local menu=${grubdir}/menu.lst

    sunstone_patch_grub_cmdline $menu
}

#
# Tweak VXR_SIM_MODE
#
function pd_pxe_vxr_sim_mode_check {
    #
    # No sim tools are needed for sunstone but we can run in sim mode
    # where the only real difference is we allow login to host directly.
    #
    VXR_SIM_MODE=0
    if (( $SIMULATION )); then
        VXR_SIM_MODE=1
    fi
}

#
# Reboot for sunstone
#
function pd_pxe_call_reboot {
    # Eject CDROM before rebooting
    platform_log_console \
        "Eject CDROM and reboot XRv9k system after installation ..."
    /usr/bin/eject /dev/cdrom
    sleep 2
    sync
    # Reboot the vrouter, baking is complete
    /sbin/reboot -f
}

#
# Set the BOOTTYPE
#
function pd_pxe_set_boottype {
    BOOTTYPE="legacy"
}

#
# Skip a max disk size check on this platform
#
function pd_pxe_skip_disk_size_check {
    true
}

function interface_ip 
{
    /sbin/ifconfig $1 | \
        grep "inet addr" | \
        awk -F: '{print $2}' | \
        awk '{print $1}';
}

function is_valid_ip 
{
    if [[ $1 =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
        true
    else
        false
    fi
}

#
# Enable some extra help for developers trying to get host access.
#
function platform_display_ssh_host_help
{
    #
    # Only applicable in development mode
    #
    if [[ "$SUNSTONE_PRODUCTION" = "1" ]]; then
        return
    fi

    if [[ "$HOST_ETH" = "FALSE" ]]; then
        return
    fi

    local LOCAL_HOST_ETH

    if [[ "$CTRL_ETH" = "FALSE" ]]; then
        LOCAL_HOST_ETH=$PLATFORM_CTRL_ETH
    else
        LOCAL_HOST_ETH=$PLATFORM_HOST_ETH
    fi
    IP=`interface_ip $LOCAL_HOST_ETH`
    is_valid_ip $IP

    if [[ $? -eq 0 ]]; then
        sed -e "s/#/${ESC}${BG_BLUE};${FG_WHITE}m#${RESET}/g" \
            -e "s/$/$_eol/g" >$DEV_TTY_XR_CON << %%

To access the sunstone VM from the host,

  ${GREEN}ssh -l root $IP${RESET}

%%
    fi

    is_vxr
    if [[ $? -eq 1 ]]; then
        sed -e "s/#/${ESC}${BG_BLUE};${FG_WHITE}m#${RESET}/g" \
            -e "s/$/$_eol/g" >$DEV_TTY_XR_CON << %%

For VXR, to access the sunstone VM, you can do, for example, on the host:

  ${GREEN}ssh -l root localhost -p \`grep ssh Platform-paniniqq-0/Shelf-lcc-0/RouteCard-panini_rp-0/PortVector.txt | awk '{print \$2}'\`${RESET}

%%
    fi
}

#
# Allow root ssh login to the host?
#
function platform_block_root_ssh_login
{
    local rootdir=$1

    platform_get_settings

    #
    # Do this only on the host VM
    #
    if [[ "$VMTYPE" != "hostos" ]]; then
        platform_log "Allow root ssh login for VM $VMTYPE"
        return
    fi

    #
    # If in development mode we allow access.
    #
    if [[ "$SUNSTONE_PRODUCTION" = "0" ]]; then
        platform_log "Allow root ssh login, development mode"
        return
    fi

    grep -q "Permit access from calvados and XR to host" ${rootdir}/etc/ssh/sshd_config
    if [[ $? -eq 0 ]]; then
        #
        # Already enabled
        #
        return
    fi

    #
    # Production mode
    #
    platform_log "Block root ssh login"

    sed -i -e  's/^[#]PermitRootLogin yes/PermitRootLogin no/g' \
        ${rootdir}/etc/ssh/sshd_config

    #
    # Add overrides at the end of the file
    #
    cat >>${rootdir}/etc/ssh/sshd_config <<%%
#
# Permit access from calvados and XR to host
#
Match Address 10.11.12.*
  PermitRootLogin yes

#
# Permit access from host to calvados and XR
#
Match Address 10.0.2.*
  PermitRootLogin yes
%%

    #
    # Restart sshd
    #
    kill -HUP `pidof sshd`
}

#
# Enable extra tty ports for host access
#
function platform_block_root_tty_login
{
    local rootdir=$1

    platform_get_settings

    #
    # We don't want the host login to appear at the same time as XR or 
    # calvados login shells
    #
    sed -i '/tty0/d' ${rootdir}/etc/inittab
    sed -i '/tty1/d' ${rootdir}/etc/inittab
    sed -i '/ttyS0/d' ${rootdir}/etc/inittab
    sed -i '/ttyS1/d' ${rootdir}/etc/inittab
    sed -i '/hvc0/d' ${rootdir}/etc/inittab
    sed -i '/hvc1/d' ${rootdir}/etc/inittab

    #
    # Do this only on the host VM. Allow the containers to ssh into root.
    #
    if [[ "$VMTYPE" != "hostos" ]]; then
        platform_log "Allow root tty login for VM $VMTYPE"
        return
    fi

    #
    # If in development mode we allow access.
    #
    if [[ "$SUNSTONE_PRODUCTION" = "0" ]]; then
        platform_log "Allow root tty login, development mode"
        return
    fi

    #
    # Production mode. Block all host login.
    #
    platform_log "Block root tty login"

    echo >${rootdir}/etc/securetty
}

#
# Which TTY should XR con use
#
function platform_enable_xr_con
{
    #
    # Leave as a pseudo terminal and telnet to the instance on boot from
    # the first serial port.
    #
#    ACTIVE_SERIAL_XR_CON='ttyS0'

    platform_log "XR console on $ACTIVE_SERIAL_XR_CON"
}

#
# Which TTY should XR aux use
#
function platform_enable_xr_aux
{
    platform_get_settings

    ACTIVE_SERIAL_XR_AUX="$TTY_XR_AUX"

    platform_log "XR aux on $ACTIVE_SERIAL_XR_AUX"
}

#
# Which TTY should calvados con use
#
function platform_enable_calvados_con
{
    ACTIVE_SERIAL='/dev/pts/0'

    platform_log "Calvados console on $ACTIVE_SERIAL"
}

#
# Which TTY should calvados aux use
#
function platform_enable_calvados_aux
{
    platform_get_settings

    if [[ "$SUNSTONE_PRODUCTION" = "1" ]]; then
        #
        # Production mode, calvados starts up on the calvados aux port
        #
        ACTIVE_SERIAL='/dev/pts/1'

        platform_log "Calvados aux on $ACTIVE_SERIAL"

    elif [[ "$SUNSTONE_CLOUD_PLATFORM" = "1" ]]; then
        #
        # Cloud + dev mode we have space for it in ttyS2
        #
        ACTIVE_SERIAL='/dev/pts/1'

        platform_log "Calvados aux on $ACTIVE_SERIAL"
    else
        ACTIVE_SERIAL=

        platform_log "Do not start Calvados aux"
    fi
}

#
# Enable extra tty ports for host access
#
function platform_enable_host_tty
{
    if [[ "$TTY_HOST" = "" ]]; then
        return
    fi

    for tty in $TTY_HOST ; do
        grep -q $tty /etc/securetty 
        if [[ $? -ne 0 ]]; then
            #
            # Allow root login on the aux port
            #
            platform_log "Allow root login TTY on $tty"

            echo $tty >> /etc/securetty
        fi

        platform_log "Enable host TTY on $tty"

        # use host_login instead of default login for security purpose,
        # login allows many users to login to host but host_login only
        # allow first user
        initctl start serial TTY=/dev/$tty \
            SPEED="$TTY_SPEED -n -l /opt/cisco/hostos/bin/host_login" \
            LD_LIBRARY_PATH=/opt/cisco/hostos/usr/lib64 

    done
}

#
# Check we have one CPU or more.
#
function platform_calvados_launch_check_num_cpu
{
    if is_distributed; then
        get_board_type
        if [[ $BOARDTYPE == "RP" ]]; then
            # For distributed RP, we don't need to check num of cpu
            return
        fi
    fi    
}

#
# Check we have sufficient memory to boot.
#
function platform_calvados_launch_check_mem
{
    local min_socket_mem

    if is_distributed; then
        get_board_type
        if [[ $BOARDTYPE == "RP" ]]; then
            min_socket_mem=$PLATFORM_MINIMAL_SYS_MEM_GB_PER_SOCKET_RP_D
        else
            min_socket_mem=$PLATFORM_MINIMAL_SYS_MEM_GB_PER_SOCKET_LC_D
        fi
    else
        min_socket_mem=$PLATFORM_MINIMAL_SYS_MEM_GB_PER_SOCKET
    fi
    local num_sockets=$(get_num_sockets)
    local total_mem_needed=$(( $min_socket_mem * $num_sockets ))

    local total_mem=$(free -g | grep Mem | awk '{print $2}')
    total_mem=$((total_mem + 1))

    #
    # Get the users attention
    #
    if [[ $total_mem_needed -gt $total_mem ]]; then
        sed -e "s/#/${ESC}${BG_RED};${FG_WHITE}m#${RESET}/g" \
            -e "s/$/$_eol/g" >$DEV_TTY_XR_CON << %%

################################################################################
#
# ERROR: $total_mem GB RAM available 
#        but the minimal memory requirement is $min_socket_mem GB per socket
#        and the socket count is $num_sockets
#     
#        Please assign the VM with at least $total_mem_needed GB RAM and reload
#
################################################################################

%%
        platform_log_error "Total mem $total_mem GB, minimial requirement is $total_mem_needed GB"
        exit -1
    else
        platform_log "Total mem $total_mem GB, minimial requirement is $total_mem_needed GB"
    fi
}

function platform_calvados_launch_check_isolcpus
{
    local isolcpus=$(platform_hw_profile_get_isolcpus_cfg)
    if [[ ${isolcpus} = "" ]]; then
        platform_log "No need to check isolcpus setting"
        return
    fi 
    if [[ "isolcpus=$IOS_XRV9K_ISOLATE_CPUS" == "$isolcpus" ]]; then
        platform_log "Boot isolcpus setting is correct"
        
        # To reduce kernel usage of isolcpus
        local init_cpus=$(taskset -pc 1 | cut -d":" -f2)
        for rcu_pid in $(pgrep rcu) ; do 
            taskset -pc ${init_cpus} ${rcu_pid}
        done
        echo 1 > /sys/bus/workqueue/devices/writeback/cpumask  
        echo 0 > /sys/bus/workqueue/devices/writeback/numa
        
        return
    fi
    
    platform_log "Changing boot isolcpus to ${isolcpus}"

    local mountpoint="/tmp/boottmpmnt"
    mkdir -p ${mountpoint}
    if [[ -b /dev/vda1 ]]; then
        mount /dev/vda1 ${mountpoint}
    elif [[ -b /dev/sda1 ]]; then
        mount /dev/sda1 ${mountpoint}
    elif [[ -b /dev/nvme0n1p1 ]]; then
        mount /dev/nvme0n1p1 ${mountpoint}
    else
        platform_log "ERROR: cannot find boot disk partition "
        return
    fi

    local grub_file="${mountpoint}/boot/grub/menu.lst"
    platform_log "grub cfg before:"
    platform_log_exec cat ${grub_file}
    sed -i "s/ isolcpus=[^ ]*//g" ${grub_file}
    sed -i "s/\(platform=xrv9k\)/\1 ${isolcpus}/g" ${grub_file}
    platform_log "grub cfg after:"
    platform_log_exec cat ${grub_file}
    umount ${mountpoint}
    rmdir ${mountpoint}
        
    platform_log_console "Automatically adjust grub isolcpus setting to $isolcpus and reboot"
    initctl emit reset-hugepage

}

#
# Verify that the UDEV rename rules for the 3 host Ethernet interfaces are
# up to date, or update otherwise.
#
function platform_hostos_process_virt_eth_interfaces
{
    #
    # Constants used in this function.
    #
    local    rule_file="/lib/udev/rules.d/76-net-rename.rules"
    local    drivers="(e1000|virtio|vmxnet3)"
    local -a names=( ${PLATFORM_MGMT_ETH} ${PLATFORM_CTRL_ETH} ${PLATFORM_HOST_ETH} )
    local    do_upd=0
    local    num_devices=3

    #
    # If this is baremetal, then we are looking for the first 2 backplane
    # ethernets
    #
    if on_baremetal; then
        num_devices=2
        local xrv9k_platform=$(platform_version)
        case $xrv9k_platform in
        C220M4)
            drivers="(igb)"
            ;;
        C220M5)
            drivers="(ixgbe)"
            ;;
        *)
            platform_log_console "Unsupported platform ($xrv9k_platform), halt system!"
            platform_log_exec_console halt -f;
            ;;
        esac
    elif is_xrv9k_aws ; then
        local aws_init_mgmt_lock="/var/lock/subsys/aws-mgmtnic-init"
        if [[ ! -f $aws_init_mgmt_lock ]]; then
            ip tuntap add dev ${PLATFORM_MGMT_ETH} mode tap >& /dev/null
            touch $aws_init_mgmt_lock
        fi
        local DATA_ETH0="data-eth0"
        drivers="(ixgbevf|e1000|virtio|ena)"
        if [[ "$SUNSTONE_PRODUCTION" = "1" ]]; then
            num_devices=1
            names=( ${DATA_ETH0} )
        else
            num_devices=2
            names=( ${PLATFORM_HOST_ETH} ${DATA_ETH0} )
        fi
    fi


    #
    # AWK script for processing the 'lspci' output below.
    #
    local awk="/^Slot:/ { slot=\$2; } \
               /^Class:/ { if (\$0 !~ /Ethernet controller/) { slot=\"\"; } } \
               /Driver:.*${drivers}/ { if (slot != \"\") { print slot; } }"

    #
    # Find the first 3 devices (sorted in ascending PCI address order) whose
    # driver matches from the ${drivers} list above.
    #
    local -a devices
    read -r -a devices <<< $(lspci -kvmm | awk "${awk}" | \
        sort | head -n ${num_devices})

    platform_log "First ${num_devices} renamed intf: ${devices[@]}"

    #
    # Only bother doing any analysis here if the filter file exists.
    #
    if [ -f ${rule_file} ]; then
        local i
        for ((i=0; i<${#devices[@]}; ++i)); do
            # platform_log "Testing for ${devices[i]} and ${names[i]}"
            local search="${devices[i]}.*${names[i]}"
            if ! grep --quiet ${search} ${rule_file} 2>/dev/null; then
                platform_log "Need new rule file: DEV ${devices[i]} " \
                                                 "NAME ${names[i]} not found"
                do_upd=1
                break
            fi
        done
    else
        platform_log "No virtual interface rename rule file, creating."
        do_upd=1
    fi

    #
    # Produce/update the file if required.
    #
    if [ "${do_upd}" != 0 ]; then
        platform_log "Preparing virtual interface rename rule file"

        echo "# AUTO-GENERATED FILE, DO NOT EDIT!" > ${rule_file}
        echo "# Generated " `date` >> ${rule_file}
        echo "#" >> ${rule_file}

        for ((i=0; i<${#devices[@]}; ++i)); do
            echo "SUBSYSTEM==\"net\"," \
                 "ACTION==\"add\"," \
                 "KERNELS==\"0000:${devices[i]}\"," \
                 "NAME=\"${names[i]}\"" >> ${rule_file}
        done

        #
        # Inform caller to reboot or not, depending on whether the function
        # has arguments or not.
        #
        if [[ $# > 0 ]]; then
            echo "REBOOT"
        fi
        return
    fi

    platform_log "Virtual interface rename rule file is up to date."
    return
}

function platform_calvados_check_swtam_part
{
    local LVG_NAME=panini_vol_grp
    local PART_SIZE="${SWTAM_DISK_CALVADOS_SIZE}"
    local PART_NAME="${SWTAM_DISK_CALVADOS_NAME}"
    local FS_VOL_LBL="${SWTAM_DISK_CALVADOS_LABEL}"
    local PART_NAME_FULL=/dev/${LVG_NAME}/${PART_NAME}
    
    if [[ ! -e "$PART_NAME_FULL" ]]; then
        platform_log $"Creating ${PART_NAME_FULL} as it did not exist"
        lvcreate -L ${PART_SIZE} -n ${PART_NAME} ${LVG_NAME}
        if ! (printf '' 1>&107) 2>&-; then
            # File descriptor 107 was not open. Open one for it
            fh107=/tmp/pdfh107
            touch ${fh107}
            exec 107<${fh107}
        fi
        . /etc/init.d/disk-functions
        check_fs_partition "${PART_NAME_FULL}" "${FS_VOL_LBL}"

    fi
}

#
# Check we have sufficient memory to boot.
#
function platform_calvados_launch_check
{

    local run_once_lock="/var/lock/subsys/xrv9k-host-init"
    if [[ -f $run_once_lock ]]; then
        return 
    fi

    if is_baremetal_platform ; then
        local xrv9k_platform=$(platform_version)
        case $xrv9k_platform in
        C220M4)
            /etc/init.d/x710_firmware_update.sh  $DEV_TTY_XR_CON
            ;;
        C220M5)
            ;;
        *)
            platform_log_console "Unsupported platform ($xrv9k_platform), halt system!"
            platform_log_exec_console halt -f;
            ;;
        esac
    fi

    platform_calvados_check_swtam_part >& /dev/null
    platform_calvados_launch_check_num_cpu
    platform_calvados_launch_check_mem
    platform_calvados_launch_check_isolcpus

    local action=$(platform_hostos_process_virt_eth_interfaces 1)

    #
    # Small delay to allow any output to appear
    #
    sleep 2

    case $action in
        REBOOT)
            /sbin/reboot -f
            ;;
        *)
            ;;
    esac
    
    /etc/rc.d/init.d/host_cloudinit.sh >& /var/log/cloudinit.log
    
    if on_baremetal; then
        modprobe ipmi_devintf
    fi
    
    touch $run_once_lock
}

#
# Enable extra tty ports for host access
#
function platform_enable_vga_tty_help
{
    local CISCO="${RED}Cisco${GREEN}"
    local PLATFORM="${GREEN}Cisco IOS XRv9k${RESET}"

    ln -sf $DEV_TTY_XR_CON /dev/console

    #
    # Let the end user know that the terminal they are looking at is not the
    # XR console
    #
    is_cloud_platform
    if [[ $? -eq 0 ]]; then
        sed -e "s/#/${ESC}${BG_BLUE};${FG_WHITE}m#${RESET}/g" \
            -e "s/$/$_eol/g" >$DEV_TTY_XR_CON << %%

################################################################################
#                                                                              #
#                  Welcome to the $PLATFORM platform                     #
#                                                                              #
#    Please wait for Cisco IOS XR to start.                                    #
#                                                                              #
#    Copyright (c) 2014-2019, 2021 by Cisco Systems, Inc.                      #
#                                                                              #
################################################################################

%%
        sed -e "s/#/${ESC}${BG_BLUE};${FG_WHITE}m#${RESET}/g" \
            -e "s/$/$_eol/g" >$DEV_TTY_XR_AUX << %%

################################################################################
#                                                                              #
#                  Welcome to the $PLATFORM platform                     #
#                                                                              #
#    The primary Cisco IOS XR console will ${RED}NOT${RESET} start in this terminal.         #
#    Please use the ${RED}VGA port${RESET} for configuring Cisco IOS XR.                     #
#                                                                              #
#    Copyright (c) 2014-2019, 2021 by Cisco Systems, Inc.                      #
#                                                                              #
################################################################################

%%
    else
        #
        # Purposefully not echoing to /dev/ttyS0 as this seems to cause the
        # output to get backed up when dealing with ESXI tty
        #
        sed -e "s/#/${ESC}${BG_BLUE};${FG_WHITE}m#${RESET}/g" \
            -e "s/$/$_eol/g" << %%

################################################################################
#                                                                              #
#                  Welcome to the $PLATFORM platform                     #
#                                                                              #
#    Please wait for Cisco IOS XR to start.                                    #
#                                                                              #
#    Copyright (c) 2014-2019, 2021 by Cisco Systems, Inc.                      #
#                                                                              #
################################################################################

%%
        sed -e "s/#/${ESC}${BG_BLUE};${FG_WHITE}m#${RESET}/g" \
            -e "s/$/$_eol/g" >$DEV_TTY_VGA << %%

################################################################################
#                                                                              #
#                  Welcome to the $PLATFORM platform                     #
#                                                                              #
#    The primary Cisco IOS XR console will ${RED}NOT${RESET} start in this terminal.         #
#    Please use the ${RED}first serial port${RESET} for configuring Cisco IOS XR.            #
#                                                                              #
#    Copyright (c) 2014-2019, 2021 by Cisco Systems, Inc.                      #
#                                                                              #
################################################################################

%%
    fi

    tty_help "Cisco IOS XR console"     $TTY_XR_CON
    tty_help "Cisco IOS XR aux console" $TTY_XR_AUX
    tty_help "Cisco Calvados console"   $TTY_CALVADOS_CON
    tty_help "Cisco Calvados aux"       $TTY_CALVADOS_AUX
    tty_help "Linux host access"        $TTY_HOST

    platform_display_ssh_host_help
}

#
# Should we allow the first serial port to have a tty login?
#
function platform_enable_host_login_on_first_serial
{
    if [[ "$ACTIVE_SERIAL" != "" ]]; then
        platform_log "Disable host TTY on $ACTIVE_SERIAL"

        ACTIVE_SERIAL=""
    fi
}

#
# Repeatedly connect to a given port on the host where hopefully an LXC
# container is running.
#
function platform_telnet_to_port
{
    local NAME=$1
    local PORT=$2
    local NOISY=platform_log
    local RECONNECT_DELAY_MIN=1
    local RECONNECT_DELAY_MAX=60
    local RECONNECT_DELAY=$RECONNECT_DELAY_MIN

    platform_log "Waiting for $NAME to start on port $PORT"

    while true
    do
        while true
        do 
            netstat -tl | grep -q ":\<$PORT\>"
            if [[ $? -eq 0 ]]; then
                break
            fi

            sleep 0.5
        done

        $NOISY "$NAME connected on port $PORT"

        local STARTTIME=$(date +%s)
        #
        # Put console connection away from BFD
        #
        taskset $(pd_get_cpu_affinity) nice -n 19 telnet -e ^Q localhost $PORT
        local ENDTIME=$(date +%s)
        local ELAPSED=$(($ENDTIME - $STARTTIME)) 

        platform_log_console "$NAME connection lost to port $PORT"
        sleep 1

        #
        # Once we hit an error, be noisy and log to the console
        #
        NOISY=platform_log_console

        #
        # Check that the connection isn't closing too quickly and we spam
        # the console
        #
        if [[ $ELAPSED -le 60 ]]; then
            platform_log_console \
                "$NAME connection was short-lived to port $PORT. " \
                "Lasted $ELAPSED seconds. " \
                "Will reconnect in $RECONNECT_DELAY seconds"

            sleep $RECONNECT_DELAY

            if [[ $RECONNECT_DELAY -lt $RECONNECT_DELAY_MAX ]]; then
                RECONNECT_DELAY=$(( $RECONNECT_DELAY + $RECONNECT_DELAY / 2))
                if [[ $RECONNECT_DELAY -gt $RECONNECT_DELAY_MAX ]]; then
                    RECONNECT_DELAY=$RECONNECT_DELAY_MAX 
                fi
            fi
        else
            RECONNECT_DELAY=$RECONNECT_DELAY_MIN
        fi
    done
}

#
# Should we allow the first serial port to have a XR login?
#
function platform_enable_xr_login_on_first_serial
{
    platform_get_settings

    #
    # Only applicable on the host VM
    #
    if [[ "$VMTYPE" != "hostos" ]]; then
        return
    fi

    platform_log "Launching XR and calvados console"

    initctl emit host-login-start
}

#
# 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
{
    #
    # We do not want host tty access unless enabled by development mode, 
    # so default to no access.on 
    #
    ACTIVE_SERIAL=

    #
    # Useful point to print the hardware profile information
    #
    if [[ "$VMTYPE" != "hostos" ]]; then
        return
    fi

    platform_hw_profile_get_settings

    #
    # check system total resources (cpu/memory) against requested resources from each domain
    # if not match, reset to use default configuration
    #
    platform_hw_profile_adjust_config

    platform_hw_profile_calvados_info
    platform_hw_profile_xr_info
    platform_hw_profile_uvf_info
    platform_hw_profile_tpa_info
    platform_get_settings
    platform_enable_host_tty

    #
    # Run in the background as this can hang if /dev/ttyS0 is not 
    # available (in ESXi)
    #
    platform_enable_vga_tty_help &

    #
    # Small delay to allow the output to appear
    #
    sleep 2
}

#
# Do we want to enable host access on any TTYs?
#
function platform_starts_tty
{
    ACTIVE_CONSOLES=
}

#
# Hard code the management eth MAC address to match that of the container we 
# are in.
#
function platform_patch_xr_bootstrap
{
    local xr_bootstrap=$1

    GUEST_MGMT_ETH_MAC_ADDR=`ifconfig eth0 | grep HWaddr | awk '{print $5}'`

    platform_log "Set GUEST_MGMT_ETH_MAC_ADDR to $GUEST_MGMT_ETH_MAC_ADDR"

    sed -i "s/\(GUEST_MGMT_ETH_MAC_ADDR=\).*/\1$GUEST_MGMT_ETH_MAC_ADDR/g" $xr_bootstrap
}

function brctl_addbr
{
    local BRIDGE=$1

    if [[ "$BRIDGE" = "" ]]; then
        platform_log_error "No bridge specified in $FUNCNAME"
        return
    fi

    if [[ -d /sys/devices/virtual/net/$BRIDGE ]]; then
        return
    fi

    platform_log "Adding bridge $BRIDGE"
    platform_log_exec brctl addbr $BRIDGE

    if [[ ! -d /sys/devices/virtual/net/$BRIDGE ]]; then
        platform_log_error "Could not add bridge $BRIDGE"
    fi
}

function brctl_delbr
{
    local BRIDGE=$1

    if [[ "$BRIDGE" = "" ]]; then
        platform_log_error "No bridge specified in $FUNCNAME"
        return
    fi

    if [[ ! -d /sys/devices/virtual/net/$BRIDGE ]]; then
        return
    fi

    platform_log "Deleting bridge $BRIDGE"
    platform_log_exec brctl delbr $BRIDGE

    if [[ -d /sys/devices/virtual/net/$BRIDGE ]]; then
        platform_log_error "Could not delete bridge $BRIDGE"
    fi
}

function brctl_addif
{
    local BRIDGE=$1
    local TAP=$2

    if [[ "$BRIDGE" = "" ]]; then
        platform_log_error "No tap specified in $FUNCNAME"
        return
    fi

    if [[ "$TAP" = "" ]]; then
        platform_log_error "No tap specified in $FUNCNAME"
        return
    fi

    platform_log "Adding bridge $BRIDGE interface $TAP"

    platform_log_exec brctl addif $BRIDGE $TAP
    if [[ $? -ne 0 ]]; then
        platform_log_error "Failed to add interface $TAP to bridge $BRIDGE"
    fi
}

function brctl_delif
{
    local BRIDGE=$1
    local TAP=$2

    if [[ "$BRIDGE" = "" ]]; then
        platform_log_error "No tap specified in $FUNCNAME"
        return
    fi

    if [[ "$TAP" = "" ]]; then
        platform_log_error "No tap specified in $FUNCNAME"
        return
    fi

    if [[ ! -d /sys/devices/virtual/net/$TAP ]]; then
        return
    fi

    platform_log "Deleting bridge $BRIDGE interface $TAP"

    platform_log_exec brctl delif $BRIDGE $TAP
    if [[ $? -ne 0 ]]; then
        platform_log_error "Failed to remove interface $TAP from bridge $BRIDGE"
    fi
}

function ifconfig_up
{
    local INTERFACE=$1

    if [[ "$INTERFACE" = "" ]]; then
        platform_log_error "No interface specified in $FUNCNAME"
        return
    fi

    platform_log "Bringing interface $INTERFACE up"

    platform_log_exec ifconfig $INTERFACE up
    ifconfig $INTERFACE | grep -q UP
    if [[ $? -ne 0 ]]; then
        platform_log_error "Failed to bring interface $INTERFACE up"
        platform_log_exec ifconfig -a
    else
        platform_log "Interface $INTERFACE is UP"

        ifconfig $INTERFACE mtu 9000
    fi
}

function ifconfig_down
{
    local INTERFACE=$1

    if [[ "$INTERFACE" = "" ]]; then
        platform_log_error "No interface specified in $FUNCNAME"
        return
    fi

    platform_log "Bringing interface $INTERFACE down"

    platform_log_exec ifconfig $INTERFACE down
    ifconfig $INTERFACE | grep -q UP
    if [[ $? -eq 0 ]]; then
        platform_log_error "Failed to bring interface $INTERFACE down"
        platform_log_exec ifconfig -a
    else
        platform_log "Interface $INTERFACE is DOWN"
    fi
}

#
# BFD and other critical XR processes were being hit by interrupt load on
# CPU1. The following function disables CPU1 for most interrupts. The
# exception are timer and PIC slave IRQs.
#
function platform_isolate_cpu1
{
    #
    # If less 2 cores or less we can't really isolate interrupts from CPU1
    # (for BFD) as we need one dedicated core for the VPE
    #
    local num_cores=`cat /proc/cpuinfo | grep -i rocessor | wc -l`
    if [[ $num_cores -le 2 ]]; then
        return
    fi

    local cpu1mask=$(printf "%x" $(( ~ 0x2 )))
    local current_mask=
    local new_mask=
    local file=

    for file in $(find /proc/irq -name smp_affinity)
    do
        current_mask=$(cat $file)
        new_mask=$(( 0x$cpu1mask & 0x$current_mask ))
        new_mask=$( printf "%x" $new_mask )

        platform_log_console "$i change mask from $current_mask to $new_mask"

        echo $new_mask > $file 2>/dev/null
    done
}

function platform_xr_ifconfig
{
    # Turn on receive offloads on mgmt-eth
    #
    platform_log_exec ethtool --show-offload $GUEST1_MGMT0_ETH
    platform_log_exec ethtool --offload $GUEST1_MGMT0_ETH rx off tx off
    platform_log_exec ethtool -k $GUEST1_MGMT0_ETH
    platform_log_exec ethtool -K $GUEST1_MGMT0_ETH gro off
    platform_log_exec ethtool -K $GUEST1_MGMT0_ETH lro off
}

function platform_post_xr_launch
{
    platform_get_settings

    platform_log "Setup host side management bridging"

    #
    # Needed on openstack as interfaces were down by default
    #
    ifconfig_up $PLATFORM_MGMT_ETH
    if [[ "$CTRL_ETH" = "FALSE" ]]; then
        if is_distributed; then
            platform_log_error "Can't run in distributed mode without CTRL_ETH interface"
            return
        fi                
    fi            

    #
    # host access should not be allowed in production, so keep it down
    #
    platform_log "post_xr_launch: HOST_ETH env is set to: "$HOST_ETH""
    local LOCAL_HOST_ETH
    if [[ "$HOST_ETH" != "FALSE" ]]; then
        if [[ "$CTRL_ETH" = "FALSE" ]]; then
            if is_distributed; then
                platform_log_error "Can't run in distributed mode without CTRL_ETH interface"
                return
            else
                LOCAL_HOST_ETH=$PLATFORM_CTRL_ETH
                platform_log "LOCAL_HOST_ETH is set to "$LOCAL_HOST_ETH""
            fi                
        else
            LOCAL_HOST_ETH=$PLATFORM_HOST_ETH
        fi
        if [[ "$SUNSTONE_PRODUCTION" = "1" ]]; then
            platform_log "Bring host eth down for production"
            ifconfig_down $LOCAL_HOST_ETH
        else
            platform_log "Bring host eth up for development"
            ifconfig_up $LOCAL_HOST_ETH
        fi
    else 
        platform_log "Platform doesn't support HOST_ETH intf: "$HOST_ETH""
    fi

    platform_log "Platform ifindexes:"
    platform_log_exec ip link show

    platform_log "Platform pair ifindexes:"
    for i in `ifconfig -a | grep "Link encap" | sed 's/ .*//g'`
    do
        platform_log_exec ethtool -S $i 2>/dev/null 
    done

    platform_log "Platform interfaces:"
    platform_log_exec ifconfig -a

    platform_isolate_cpu1
}


# App hosting Cgroup settings
function pd_tpa_cgroup_settings
{
    APP_CGROUP_MEM_NUMA_NODES=0
    if is_lite_resource; then
        APP_CGROUP_CPUSET_CPUS=0
        APP_CGROUP_CPU_SHARES=256
        APP_CGROUP_MEM_LIMIT_IN_BYTES=256M
    else
        APP_CGROUP_CPUSET_CPUS=1
        APP_CGROUP_CPU_SHARES=512
        APP_CGROUP_MEM_LIMIT_IN_BYTES=512M
    fi 

    #
    # check if we have custom CGROUP setting through meta-data
    #
    if tpa_can_modify_host_ncpus; then
        APP_CGROUP_CPUSET_CPUS=$APP_HOST_CPUS
        platform_log "overwrite CGROUP CPUSETs setting from meta-data to "$APP_CGROUP_CPUSET_CPUS""

        if [[ "$APP_HOST_CPU_SHARES" != "" ]]; then            
            APP_CGROUP_CPU_SHARES=$APP_HOST_CPU_SHARES
            platform_log "overwrite CGROUP CPU Shares setting from meta-data to "$APP_CGROUP_CPU_SHARES""
        fi
    fi

    if tpa_can_modify_host_memory; then 
        app_host_mem_mb=$(echo | awk "{printf(\"%d\n\", $APP_HOST_MEM_GB * 1024)}")
        APP_CGROUP_MEM_LIMIT_IN_BYTES="${app_host_mem_mb}M"
        platform_log "overwrite CGROUP memory setting from meta-data to "$APP_CGROUP_MEM_LIMIT_IN_BYTES""            
    fi

    if [[ "$APP_HOST_DISK_GB" != "" ]]; then
        DISK_TP_VOL_PART_SIZE=$(( APP_HOST_DISK_GB * 1024 ))
        platform_log "overwrite Thirdparty disk vol from meta-data to "$DISK_TP_VOL_PART_SIZE""            
    fi

    if tpa_can_modify_numa_nodes; then
        APP_CGROUP_MEM_NUMA_NODES=$APP_HOST_CPUSET_MEMS
        platform_log "overwrite CGROUP CPUMEMs setting from meta-data to "$APP_CGROUP_MEM_NUMA_NODES""
    fi
}

#
# get CPU's taskset process's CPU affinity mask
# 
# BFD and other time critical processes run on CPU1.  So run other processes
# on CPU0.  The taskset is a mask, so returning a "1" is the mask for CPU0
#
function pd_get_cpu_affinity
{
    echo 1
    return 0
}

#
# check if we have only one vCPU configured 
#
function platform_has_one_cpu()
{
    local num_cores=`grep -i -c rocessor /proc/cpuinfo`
    if [[ $num_cores -eq 1 ]]; then
        true
    else
        false
    fi
}

#
# Log the first time we are sourced. Helps with script flow debugging.
#
if [ "$sourced_pd_function" = "" ]; then
    platform_log "Sourced PD functions"

    #
    # For more tracing
    #
#    platform_log_backtrace
fi
sourced_pd_function=1


