#!/bin/sh
#
# Copyright (c) 2015-2016, 2018-2020 by Cisco Systems, Inc.
# All rights reserved.
#
# This script runs on host and Calvados
# On Host it creates a ubifs type volume and device
# On Calvados it simply mounts this volume on /mnt/plog for obfl to use
#
# Check the onboard flashes.
# There are two MTD flashes
# 1. Bootflash 128MB on FC and SC 
# 2. OBFL 64MB on all the cards
# We use UBIFS on them. 
# Check if they have been formatted already. 
# If not, format them accordingly.
#
# mtdparts from /proc/cmdline
# FOR Fretta RP 
#mtdparts=physmap-flash.0:256k(SM_LOG),256k(RR_LOG),256k(KLOG),18m(PLOG),512k(mtdoops)
# FOR Pizza Box RPs and Fretta LC
#mtdparts=physmap-flash.0:256k(SM_LOG),256k(RR_LOG),256k(KLOG),18m(PLOG),512k(mtdoops),4m(shmoo)
# FOR SC AND FC
# mtdparts=physmap-flash.0:64m(bootflash);physmap-flash.1:256k(SM_LOG),256k(RR_LOG),18m(PLOG),256k(KLOG),1m(mtdoops),16m(KTRACES)
# /dev/mtd3 represents the OBFL PLOG
# /dev/mtd5 represents SHMOO 
#
# NOTE:!!!!
# Please check the cmdline param definition in the imaging file for the
# definition of mtdparts. This cmdline param will dictate the number of mtd
# devices we see and the sizes of it in kernel.
#
# After UBI formatting, 
# /dev/ubi3_0 will be /mnt/plog
# /dev/ubi5_0 will be /mnt/shmoo 

# import helpers
source /etc/init.d/spirit_pd.sh

PLOG_MTD_NUM=3
SHMOO_MTD_NUM=5
BOOTFLASH_MTD_NUM=0
HDD_PART_NUM=5
EXT2_FS_MAGIC_NUMBER=0xEF53

CHECKFLASH=/var/log/check-flash.log
#CHECKFLASH=/dev/null
CHECK_OBFL_FLASH=/var/run/check_obfl_flash
fs_sb_output=/tmp/fs_sb_output

check_mtd() {

    local mtdnum=$1
    local mtdname=$2
    local my_mtdname

    local mtddev="mtd${mtdnum}"

    if [ ! -e /sys/class/mtd/${mtddev} ]; then
        echo "$(date): /dev/${mtddev} not found." >> $CHECKFLASH
        exit -1
    fi

    my_mtdname=$(cat /sys/class/mtd/${mtddev}/name | tr '[:upper:]' '[:lower:]')

    if [ "$my_mtdname" != "$mtdname" ]; then
        echo "$(date): /dev/${mtddev} $mtdname not found." >> $CHECKFLASH
        exit -1
    fi
}

check_block() {

    local partnum=$1
    local partname=$2
    local my_partname

    if [ ! -e /sys/class/block/${partname} ]; then
        echo "$(date): /dev/sda${partnum} not found." >> $CHECKFLASH
        exit -1
    fi

    my_partname=$(cat /sys/class/block/${partname}/uevent | grep DEVNAME= | sed 's/^.*DEVNAME=//')

    if [ "$my_partname" != "$partname" ]; then
        echo "$(date): /dev/sda${partnum} not found." >> $CHECKFLASH
        exit -1
    fi
}

format_ubi_on_mtd() {

    local mtd_num=$1
    local vol_name=$2
    local dev_mtd="/dev/mtd${mtd_num}"
    local dev_ubi="/dev/ubi${mtd_num}"

    ubidetach -m ${mtd_num}

    # punch the watchdog
    pd_punch_watchdog

    # Send post code to indicate MTD device format has started
    pd_notify_host_mtd_format_started

    ubiformat ${dev_mtd} -y
    ret=$?
    if [ $ret -ne 0 ]; then
        echo "$(date): UBI Formating of ${dev_mtd} failed with $ret. ${dev_ubi}" >> $CHECKFLASH
        # Send post code to indicate MTD device format has failed
        pd_notify_host_mtd_format_failed
        exit -1
    fi

    ubiattach -m ${mtd_num} -d ${mtd_num}

    # -m for use maximum size of the mtd for the volume
    ubimkvol ${dev_ubi} -N ${vol_name} -m
    ret=$?
    if [ $ret -ne 0 ]; then
        echo "$(date): UBI Volume [${vol_name}] creation on ${dev_ubi} failed with $ret." >> $CHECKFLASH
        # Send post code to indicate MTD device volume creation has failed
        pd_notify_host_mtd_mkvol_failed
        exit -1
    fi

    # Send post code to indicate MTD device format and volume creation
    #  has ended
    pd_notify_host_mtd_format_ended
}

mount_ubifs() {

    local ret
    local UBI_PART=$1
    local MOUNT_DIR=$2

    # The mouting takes time if chk_fs is enabled
    # punch the watchdog
    pd_punch_watchdog

    mount /dev/${UBI_PART} ${MOUNT_DIR} -t ubifs

    ret=$?

    return $ret
}

mount_ext4() {

    local ret
    local HDD_PART=$1
    local MOUNT_DIR=$2

    # The mouting takes time if chk_fs is enabled
    # punch the watchdog
    pd_punch_watchdog

    mount /dev/${HDD_PART} ${MOUNT_DIR} -t ext4 -o offset=1048576

    ret=$?
    return $ret
}

check_flash() {

    local MTD_NUM=$1
    local MTD_NAME=$2
    local MOUNT_DIR=$3
    local UBI_PART="ubi${MTD_NUM}_0"
    local FORCE_FORMAT=$4

    check_mtd $MTD_NUM $MTD_NAME

    if [ -n "$FORCE_FORMAT" ]; then
        umount $MOUNT_DIR
        format_ubi_on_mtd ${MTD_NUM} ${MTD_NAME}
    fi

    ubiattach -m ${MTD_NUM} -d ${MTD_NUM}

    #ubi attach failed
    if [ ! -e /sys/class/ubi/${UBI_PART} ]; then
        format_ubi_on_mtd ${MTD_NUM} ${MTD_NAME}
    fi

    [ -e ${MOUNT_DIR} ] || mkdir ${MOUNT_DIR}
    mount_ubifs ${UBI_PART} ${MOUNT_DIR}

    # mount fails
    if [ $? -ne 0 ]; then
        # try format
        format_ubi_on_mtd ${MTD_NUM} ${MTD_NAME}

        #try mounting again
        mount_ubifs ${UBI_PART} ${MOUNT_DIR}
        if [ $? -ne 0 ]; then
            echo "$(date): Mounting ${MOUNT_DIR} failed." >> $CHECKFLASH
            echo "$(date): Mounting ${MOUNT_DIR} failed." >> $CHECK_OBFL_FLASH
            exit -1
        fi
    fi
}

function check_fs_sb {
    local return_code=0
    local fs_mn=0
    local part=${1}

    local loop=$(get_loop_device ${part})
    if [ -n $loop ]; then
        dumpe2fs -h ${loop}  > $fs_sb_output 2>&1
        if (( $? == 1 )); then
            return_code=1
        else
            fs_mn=$(grep "magic number" $fs_sb_output | awk '{print $4}')
            if (( $fs_mn != $EXT2_FS_MAGIC_NUMBER )); then 
                return_code=2
            fi
        fi

        losetup -d ${loop}
    fi

    return $return_code
}

function format_partition {
    local dev_name=${1} label=${2}


    # During bake time, obfl partition (/dev/sda5) file system is created
    # at sector offset 2048. Hence we have to loop mount the obfl partition 
    # to access the file system. 
    local loop=$(get_loop_device ${dev_name})
    if [ -n $loop ]; then
        
        # Reformat partition with an ext4 file system
        #we disable huge_file feature beacuse,
        #we need huge file support when files are bigger than 2TB.
        /sbin/mkfs.ext4 -L ${label} -b 4096 -O ^huge_file -vv ${loop} >> $CHECKFLASH 2>&1
    
        if [ $? -ne 0 ]; then
            # Send post code to indicate hard disk partition format has failed
            pd_notify_host_disk_partition_format_failed 
        fi
    
        /sbin/tune2fs -c 0 -i 0 ${loop} >> $CHECKFLASH 2>&1

        if [ $? -ne 0 ]; then
            # Send post code to indicate hard disk partition format has failed
            pd_notify_host_disk_partition_format_failed 
        fi

        losetup -d ${loop}
    fi

    sync
    if [ $? -ne 0 ]; then
        # Send post code to indicate disk partition's data synchronization to memory failed 
        pd_notify_host_disk_partition_sync_failed
    fi
}

function check_fs_partition {
    local dev_name=${1} label=${2}

    # Send post code to indicate hard disk partition format has started
    pd_notify_host_disk_partition_format_started

    check_fs_sb ${dev_name}
    # Even though super block is EXT4, redo mkfs.
    # The Partition label may have changed
    format_partition ${dev_name} ${label}

    # Send post code to indicate hard disk partition format has completed
    pd_notify_host_disk_partition_format_ended
}

check_hdd_partition() {

    local PART_NUM=$1
    local HDD_PART=$2
    local MOUNT_DIR=$3
    local FORCE_FORMAT=$4

    check_block $PART_NUM sda5
    
    if [ -n "$FORCE_FORMAT" ]; then
        umount $MOUNT_DIR
        check_fs_partition /dev/${HDD_PART} "obfl"         
    fi

    [ -e ${MOUNT_DIR} ] || mkdir ${MOUNT_DIR}    
    mount_ext4 ${HDD_PART} ${MOUNT_DIR}

    # mount fails
    if [ $? -ne 0 ]; then
        # try format
        check_fs_partition /dev/${HDD_PART} "obfl" 

        #try mounting again
        mount_ext4 ${HDD_PART} ${MOUNT_DIR}
        if [ $? -ne 0 ]; then
            echo "$(date): Mounting ${MOUNT_DIR} failed." >> $CHECKFLASH
            echo "$(date): Mounting ${MOUNT_DIR} failed." >> $CHECK_OBFL_FLASH
            exit -1
        fi
    fi
}

check_bad_files() {
    local mount_dir=$1

    # Check you can list the files.
    ls -R $mount_dir/ >/dev/null
    if [ $? -ne 0 ]; then
        echo "$(date): Cannot ls directory. The filesystem looks bad" >> $CHECKFLASH
        echo "1"
        return
    fi

    # Check you can read all files.
    if [ -n "$(ls $mount_dir/)" ]; then
        for file in `ls $mount_dir`
        do
            if [ ! -f $mount_dir/$file ]; then
                continue
            fi
            cksum $mount_dir/$file > /dev/null
            if [ $? -ne 0 ]; then
                echo "$(date): Can not read file, The filesystem looks bad" >> $CHECKFLASH
                echo "1"
                return
            fi
        done
    fi

    # Check you can write a file
    touch $mount_dir/test_sanity
    echo "BADBEEF" > $mount_dir/test_sanity
    if [ $? -ne 0 ]; then
        echo "$(date): Can not write a file, The filesystem looks bad" >> $CHECKFLASH
        echo "1"
    fi
    rm -rf $mount_dir/test_sanity
}

check_partition() {

    local name=$1
    
    local mtd_num=$2

    local force_format=$3

    check_flash $mtd_num $name /mnt/${name} $force_format
        
    if [ -n "$(check_bad_files /mnt/${name})" ]; then
        echo "$(date): check_flash /mnt/${name} failed, force_format" >> $CHECKFLASH
        check_flash ${mtd_num} $name /mnt/${name} "force_format" 
    else
        echo "$(date): Partition /mnt/${name} looks good." >> $CHECKFLASH
    fi
}

unmount_partition() {

    local name=$1

    mountpoint -q /mnt/${name}

    if [ $? == 0 ]; then
        umount /mnt/${name}
    fi
}

check_bootflash() {

    local force_format=$1

    check_flash ${BOOTFLASH_MTD_NUM} "bootflash" /mnt/bootflash $force_format
}


unprotect_flash() {
    local reg_val
    local reg_off=0x308

    echo "$(date): Unprotect_flash, start from here" >> $CHECKFLASH

    # MISC_CNTL_REG (0x0000_0308) bit 4, OBFL flash WP (write protect)
    # 1 = Enable
    # 0 = Disable
    reg_val=$(iofpga_reg_read_common 0 ${reg_off})

    echo "$(date): Unprotecting flash get reg_val from read_common, $reg_val" >> $CHECKFLASH
    reg_val=$((reg_val&0xffffffef))
    reg_val=$(printf "0x%x" $reg_val)

    boot_debug "Unprotecting flash reg_off ${reg_off}, reg_val $reg_val"
    echo "$(date): Unprotecting flash reg_off ${reg_off}, reg_val $reg_val" >> $CHECKFLASH
    iofpga_reg_write_common 0 ${reg_off} $reg_val
}

enable_ubifs_fs_check() {
    echo 1 > /sys/kernel/debug/ubifs/chk_fs
}

get_loop_device() {

    local dev=$1
    local offset=$(get_fs_offset "${dev}" "p1")
    local sizelimit=$(get_fs_sizelimit "${dev}" "p1")

    loop=$(losetup -f --show ${dev} -o ${offset} --sizelimit ${sizelimit})
    if [ $? != 0 ]; then
        echo "Failed to create loop device for $dev" >> $CHECKFLASH
    else
        echo "Successfully created loop device $loop" >> $CHECKFLASH
    fi 
    echo $loop
}

main() {

    # mtd formatting takes sometime.
    # inform to SUP via SC One-Wire about our status,
    # to extend any normal boot timeouts.
    #send_ow_err_code ${CHECK_FLASH_BEGIN}

    echo "$(date): Unprotecting flashes .." >> $CHECKFLASH
    unprotect_flash

    # Enable ubifs fs check
    enable_ubifs_fs_check

    echo "$(date): Check_flash for plog partition in host " >> $CHECKFLASH
    check_flash $PLOG_MTD_NUM plog /mnt/plog
    unmount_partition plog

    pd_is_shmoo_partition_needed
    shmoo_needed=$? 

    if [ $shmoo_needed -eq 1 ];then
        echo "$(date): Check_flash for shmoo partition in host" >> $CHECKFLASH
        check_flash $SHMOO_MTD_NUM shmoo /mnt/shmoo 
        unmount_partition shmoo
    fi

     #Enable bootflash when needed on FC and SC
     #boot_debug "Checking bootflash ..."
     #check_bootflash
    
     #Unmount /mnt/bootflash on host os    
     #mountpoint -q /mnt/bootflash
     #if [ $? == 0 ]; then
     #   umount /mnt/bootflash
     #fi

     #send_ow_err_code ${CHECK_FLASH_OK}

}

case "$1" in
    start)
        # Populate BOARDTYPE variable
        get_board_type

        VMTYPE=`cat /proc/cmdline | grep vmtype=`
        CARDINDEX=`cat /proc/cmdline | grep card_index=`

        if [ -n "$VMTYPE" ]; then
            VMTYPE=`cat /proc/cmdline | sed 's/^.*vmtype=//' | cut -d" " -f1`
        fi

        if [ -n "$CARDINDEX" ]; then
            CARDINDEX=`cat /proc/cmdline | sed 's/^.*card_index=//' | cut -d" " -f1`
        else
            echo "$(date): Card_index is not available." >> $CHECKFLASH
        fi

        if [ "$VMTYPE" == "hostos" ]; then
#            echo "$(date): in hostos !!!!!!!!! " >> $CHECKFLASH
            echo "$(date): in hostos !!!!!!!!! " 

            if  pd_is_hdd_partition_needed ;then
                echo "$(date): Check_hdd_partition for harddisk partition in host" >> $CHECKFLASH
                dev=/dev/sda5

                # Run fsck on the obfl partition created on SSD.
                # As it is vulnerable to file system corruption
                loop=$(get_loop_device $dev)
                if [ -n $loop ]; then
                    fsck -fy $loop >> $CHECKFLASH
                    status=$?
                    if [ $status -eq 1 ]; then
                        echo "Filesystem error detected and repaired on obfl partition $dev" >> $CHECKFLASH
                    elif [ $status -gt 1 ]; then
                        echo "Filesystem errors detected on obfl partition $dev" >> $CHECKFLASH
                    fi
                    losetup -d ${loop}
                fi

                check_hdd_partition ${HDD_PART_NUM} sda5 /mnt/plog 
                unmount_partition plog
            else
                if [ ! -e /sys/kernel/debug/ubifs ]; then
                    modprobe ubifs
                fi
                main
            fi

        elif [ "$VMTYPE" == "sysadmin-vm" ];then
            if [ -f $CHECK_OBFL_FLASH ]; then
                echo "$(date): sysadmin : OBFL is bad ? check and mount it here" >> $CHECKFLASH 
                mkdir -p /mnt/plog
                if [ "$CARDINDEX" == "27037" -o "$CARDINDEX" == "27042" ]; then
                    # check bad file in sysadmin, after sysadmin come up
                    if [ -n "$(check_bad_files /mnt/plog)" ]; then
                        echo "$(date): check_hdd_partition /mnt/plog and force_format in sysadmin-vm ." >> $CHECKFLASH
                        check_hdd_partition ${HDD_PART_NUM} vdf /mnt/plog "force_format"
                    else
                        echo "$(date): Partition /mnt/plog looks good in sysadmin-vm ." >> $CHECKFLASH
                    fi

                    echo "$(date): sysadmin-vm, mount_ubifs, ubi${PLOG_MTD_NUM}_0" >> $CHECKFLASH
                    mount_ext4 "vdf" /mnt/plog
                else
                    # check bad file in sysadmin, after sysadmin come up
                    if [ -n "$(check_bad_files /mnt/plog)" ]; then
                        echo "$(date): check_flash /mnt/plog and force_format in sysadmin-vm ." >> $CHECKFLASH
                        check_flash ${PLOG_MTD_NUM} plog /mnt/plog "force_format"
                    else
                        echo "$(date): Partition /mnt/plog looks good in sysadmin-vm ." >> $CHECKFLASH
                    fi

                    echo "$(date): sysadmin-vm, mount_ubifs, ubi${PLOG_MTD_NUM}_0" >> $CHECKFLASH
                    mount_ubifs "ubi${PLOG_MTD_NUM}_0" /mnt/plog                  
                fi
                rm $CHECK_OBFL_FLASH
            else
                echo "$(date): in sysadmin-vm, don't do anything here" >> $CHECKFLASH 
            fi
        elif [ "$VMTYPE" == "xr-vm" ] && [ "$BOARDTYPE" == "LC" ];then
            echo "$(date): XR : check OBFL flash here" >> $CHECKFLASH
            mkdir -p /mnt/shmoo
            if [ -n "$(check_bad_files /mnt/shmoo)" ]; then
                echo "$(date): check_flash /mnt/shmoo and force_format in xr-vm ." >> $CHECKFLASH
                check_flash ${SHMOO_MTD_NUM} plog /mnt/shmoo "force_format"
            else
                echo "$(date): Partition /mnt/shmoo looks good in xr-vm ." >> $CHECKFLASH
            fi

            echo "$(date): in xr-vm, LC, mount_ubifs, ubi${SHMOO_MTD_NUM}_0" >> $CHECKFLASH
            mount_ubifs "ubi${SHMOO_MTD_NUM}_0" /mnt/shmoo
        fi
        ;;

    obfl)
        if [ "$VMTYPE" == "sysadmin-vm" ];then
            CARDINDEX=`cat /proc/cmdline | grep card_index=`

            if [ -n "$CARDINDEX" ]; then
                CARDINDEX=`cat /proc/cmdline | sed 's/^.*card_index=//' | cut -d" " -f1`
            else
                echo "$(date): Card_index is not available." >> $CHECKFLASH
            fi

            echo "$(date): OBFL : from sysadmin-vm obfl process" >> $CHECKFLASH 
            if [ -f $CHECK_OBFL_FLASH ]; then
                echo "$(date): OBFL : FLASH is bad, skip both OBFL flash check and mount " >> $CHECKFLASH 
                exit 1
            fi

            mkdir -p /mnt/plog

            if [ "$CARDINDEX" == "27037" -o "$CARDINDEX" == "27042" ]; then
                # check bad file in sysadmin, after sysadmin come up
                if [ -n "$(check_bad_files /mnt/plog)" ]; then
                    echo "$(date): OBFL : check_hdd_partition /mnt/plog and force_format in obfl ." >> $CHECKFLASH
                    check_hdd_partition ${HDD_PART_NUM} vdf /mnt/plog "force_format"
                else
                    echo "$(date): OBFL : Partition /mnt/plog looks good, mount ext4 ." >> $CHECKFLASH
                fi

                echo "$(date): OBFL : sysadmin-vm, mount_ext4" >> $CHECKFLASH
                mount_ext4 "vdf" /mnt/plog
            else
                # check bad file in sysadmin, after sysadmin come up
                if [ -n "$(check_bad_files /mnt/plog)" ]; then
                    echo "$(date): OBFL : check_flash /mnt/plog and force_format in obfl ." >> $CHECKFLASH
                    check_flash ${PLOG_MTD_NUM} plog /mnt/plog "force_format"
                else
                    echo "$(date): OBFL : Partition /mnt/plog looks good, mount ubifs ." >> $CHECKFLASH
                fi

                echo "$(date): OBFL : sysadmin-vm, mount_ubifs, ubi${PLOG_MTD_NUM}_0" >> $CHECKFLASH
                mount_ubifs "ubi${PLOG_MTD_NUM}_0" /mnt/plog                  
            fi
        fi
        ;;

    stop)
        ;;

    restart | reload | force-reload | status | condrestart | try-restart)
        ;;

    *)
        NAME=/etc/init.d/check-flash
        echo "Usage: $NAME {start}" >&2
        exit 1
        ;;
esac

exit 0
