#!/usr/bin/sh -e

# Copyright (c) 2017, Mellanox Technologies
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this
#    list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright notice,
#    this list of conditions and the following disclaimer in the documentation
#    and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
# The views and conclusions contained in the software and documentation are those
# of the authors and should not be interpreted as representing official policies,
# either expressed or implied, of the FreeBSD Project.

usage()
{
  cat <<EOF
Usage: $0 [--help] [--unloadmods] [--minifs] [--fullfs path] [--fmtpersist]
    [--persistcfg] [--skip-boot-update]
EOF
}

ECHO=${ECHO:-echo}

${ECHO} "WARNING: This script is no longer supported and will eventually be removed."

PARSED_OPTIONS=$(getopt -n "$0" -o h \
    --long "help unloadmods minifs fullfs fmtpersist persistcfg skip-boot-update" -- "$@")

eval set -- "$PARSED_OPTIONS"

while true
do
  case $1 in
      -h | --help)
          usage
          exit 0
          ;;
      --unloadmods)
          unload_modules=1
          shift
          ;;
      --minifs)
          mini_root=1
          shift
          ;;
      --fullfs)
          full_root=1
          shift
          ;;
      --fmtpersist)
          format_persist=1
          shift
          ;;
      --persistcfg)
          persist_config=1
          shift
          ;;
      --skip-boot-update)
          skip_boot_update=1
          shift
          ;;
      --)
          shift
          break
          ;;
  esac
done

# Default efi and root partition
EFI_PART=/dev/mmcblk0p1
ROOT_PART=/dev/mmcblk0p2
PERSIST_PART=/dev/mmcblk0p8

# Load bf.cfg if exists, which could re-define the EFI_PART & ROOT_PART.
if [ -e /etc/bf.cfg ]; then
  eval "$(bfcfg --part-info)"
fi

# If both full_root and mini_root are set to a non-empty string
# we consider that an error.  One important thing to remember is
# that if we install the mini-fs and then install the full root fs
# OVER the mini-fs (without wiping the partition) we will end up
# with a strange hybrid root file system.  This happens, for example,
# when some utility is found on the initramfs... but NOT on the full
# root fs.

if [ -n "${full_root}" ] && [ -n "${mini_root}" ]; then
cat <<EOF

**********************************************************************
***                                                                ***
***  You cannot install both the mini AND full root file systems.  ***
***                                                                ***
**********************************************************************
EOF
    exit 1
fi

# Check to make sure the user really wants to format their persistent
# partition

if [ -n "${format_persist}" ]; then
    cat <<EOF

**********************************************************************
***                                                                ***
***  You are about to format your persistent partition! Are you    ***
***  sure you want to delete all data on your /data partition?     ***
***                                                                ***
**********************************************************************

EOF
    # Keep asking until we get a straight answer
    # valid answers are y, yes, n, no, and abort
    while true; do
        printf 'format /data? (y/n/abort) '
        read -r confirm

        case $confirm in
            [yY]|[yY][eE][sS])
                break
                ;;
            [nN]|[nN][oO])
                ${ECHO} "Continuing install without formatting /data."
                format_persist=
                break
                ;;
            [aA]|abort)
                ${ECHO} "Abort."
                exit 0
                ;;
            *)
                ${ECHO} "Invalid choice."
                ;;
        esac
    done
fi

#set boot arguments: Read current 'console' and 'earlycon'
# parameters, and append the root filesystem parameters.
bootarg="$(sed 's/initrd=initramfs//' /proc/cmdline)"
bootarg="$bootarg root=$ROOT_PART rootwait"

rescue_bootarg="$(sed 's/initrd=initramfs//' /proc/cmdline)"
rescue_bootarg="$bootarg root=$ROOT_PART"

# Older versions of systemd-gpt-auto-generator can't parse
# the eMMC boot partitions properly.  BlueField does not
# rely on this service so we disable it to prevent seeing
# error messages in the log (which are harmless).
bootarg="$bootarg rd.systemd.gpt_auto=0 systemd.gpt_auto=0 fsck.mode=force fsck.repair=yes"

# This function installs grub and sets it as the default boot option
install_grub()
{
    efivars=/sys/firmware/efi/efivars
    efidir=/mnt/boot
    bootdir=/mnt/boot
    localedir=/mnt/usr/share/locale
    grubdir=/mnt/boot/grub
    grubcfg=$grubdir/grub.cfg

    mount $ROOT_PART /mnt
    mount $EFI_PART /mnt/boot

    test "$(ls -A $efivars)" || mount -t efivarfs none $efivars

    mkdir -p $grubdir
    if [ -f "/mnt/boot/EFI/BOOT/grubaa64.efi" ]; then
        bfbootmgr --add "grub" "/EFI/BOOT/grubaa64.efi" ""
    else
        mkdir -p $localedir
        grub-install $EFI_PART --locale-directory=$localedir --efi-directory=$efidir --boot-directory=$bootdir
    fi

    touch $grubcfg
    cat > $grubcfg <<EOF
#
# /boot/grub/grub.cfg
#

# See the official grub documentation for more information.

# Set menu colors
set menu_color_normal=white/green
set menu_color_highlight=light-green/black

# When the user logs into the system for the first time they
# will be prompted to change this default password.

set superusers="admin"
password_pbkdf2 admin grub.pbkdf2.sha512.10000.5EB1FF92FDD89BDAF3395174282C77430656A6DBEC1F9289D5F5DAD17811AD0E2196D0E49B49EF31C21972669D180713E265BB2D1D4452B2EA9C7413C3471C53.F533423479EE7465785CC2C79B637BDF77004B5CC16C1DDE806BCEA50BF411DE04DFCCE42279E2E1F605459F1ABA3A0928CE9271F2C84E7FE7BF575DC22935B1

# Set menu display time
set timeout=10

# Set the default boot entry (first is 0)
set default=0

# Boot entries:
# Yocto
menuentry "Yocto from eMMC" --unrestricted {
        linux (\$root)/Image $bootarg
}

menuentry "Yocto from eMMC (kdump)" --unrestricted {
        linux (\$root)/Image $bootarg crashkernel=0M-2G:128M,2G-6G:256M,6G-8G:512M,8G-:768M systemd.wants=kdump
}

# Yocto rescue
menuentry "Rescue from eMMC" --users "admin" {
        linux (\$root)/Image $rescue_bootarg
        initrd (\$root)/core-image-rescue-initramfs-bluefield.cpio.xz
}
EOF

    umount /mnt/boot
    umount /mnt
}

if [ -n "${full_root}" ] && [ $# -eq 1 ]; then
    fspath=$1
    if [ ! -f $fspath ]; then
        ${ECHO} "Error - file does not exist"
        exit 1
    fi
elif [ -n "${full_root}" ] && [ $# -ne 1 ]; then
    ${ECHO} "Too few arguments"
    exit 1
elif [ $# -ne 0 ]; then
    ${ECHO} "Too many arguments"
    exit 1
fi

if [ -n "${format_persist}" ]; then
    bfpart_args="-o"
fi

# Create the yocto partitions.
partresult="$(bfpart $bfpart_args)"
persist_status=$(echo "$partresult" | grep PERSIST: | awk '{print $2}')

# Cleans up the actual boot options.
bfbootmgr --cleanall

mkdosfs $EFI_PART

yes | mkfs.ext4 -O 64bit $ROOT_PART

if [ -n "${format_persist}" ] || [ "$persist_status" = "create" ]; then
    yes | mkfs.ext4 -O 64bit $PERSIST_PART
    mount $PERSIST_PART /mnt
    mkdir /mnt/etc
    mkdir /mnt/.etc.work

    umount /mnt
    ${ECHO} Created persistent data partition.
else
    ${ECHO} Using existing persistent partition.
fi

if [ -n "${mini_root}" ]; then
    # Copy the boot Image.
    mount $EFI_PART /mnt; cp /boot/Image /mnt; umount /mnt

    # Copy the initramfs.
    mount $ROOT_PART /mnt
    cd /
    cp -ar bin boot etc home init lib lib64 media opt root sbin usr var /mnt
    cd /mnt
    mkdir -p dev mnt sys tmp proc run data

    cd /
    umount /mnt

    install_grub

cat <<EOF

**********************************************************************
***                                                                ***
***   YOU HAVE NOT INSTALLED THE FULL ROOT FILE SYSTEM             ***
***                                                                ***
***   The "minifs" you have installed is simply a copy of the      ***
***   initramfs.                                                   ***
***                                                                ***
***   Note that copying the initramfs to your eMMC parition is     ***
***   easy and fast but does not include many Linux packages you   ***
***   most likely want installed on your system.  For example,     ***
***   Mellanox OFED is NOT on the initramfs (minifs).              ***
***   The initramfs is normally used during installation of        ***
***   non-Yocto linux distros and very basic system testing        ***
***   only.                                                        ***
***                                                                ***
***   You should install the full root fs or the full root dev     ***
***   fs (contains binutils and kernel source) for a fully         ***
***   functional system.                                           ***
***                                                                ***
***   Copy one of the following images to /tmp on the initramfs    ***
***   and then specify that file as an argument to bfinst as       ***
***   shown below.                                                 ***
***                                                                ***
***     core-image-full-BlueField-<version>.tar.xz                 ***
***     core-image-full-dev-BlueField-<version>.tar.xz             ***
***                                                                ***
***   bfinst --fullfs /tmp/<core-image>                            ***
***                                                                ***
**********************************************************************
EOF
fi

sync

if [ -n "${unload_modules}" ]; then
    # Unload various kernel modules before continuing.
    if bffamily | grep Sella; then
        rmmod mlx_cpld
    fi

    rmmod ipmb_dev_int i2c_mlx gpio_mlxbf nfit libnvdimm
    rmmod mlxbf_livefish mlx_bootctl mlx5_ib ib_core mlx5_core mlxfw
    rmmod mlx_compat
fi

tar_version=$(ls -l /bin/tar)

if [ "${tar_version#*busybox}" != "$tar_version" ]; then
    tar_options=""
else
    # The non-busybox version of tar will complain about timestamps
    # in the future because the clock isn't set during the installation
    # process.
    tar_options="--warning=no-timestamp"
fi

if [ -n "${full_root}" ]; then
    mount $ROOT_PART /mnt

    bootfifo_rootfs_file=/tmp/rootfs.tar
    if [ "${fspath}" = "${bootfifo_rootfs_file}" ]; then
      ${ECHO} "boot: wait for decompression"
      # shellcheck disable=SC2034
      for i in $(seq -s" " 900); do
        [ -f "${bootfifo_rootfs_file}_done" ] && break
        sleep 1
      done
      if [ -f "${bootfifo_rootfs_file}_done" ]; then
        ${ECHO} "boot: decompression done"
        rm "${bootfifo_rootfs_file}_done"
      else
        ${ECHO} "boot: decompression timeout"
        exit 1
      fi
      ${ECHO} "Installing root file system. This will take a few minutes."
      tar xf "$fspath" -C /mnt "$tar_options"
    else
      ${ECHO} "Installing root file system. This will take a few minutes."
      # busybox version of tar requires EXTRACT_UNSAFE_SYMLINKS?
      XZ_OPT="--threads=0 -9 --verbose" EXTRACT_UNSAFE_SYMLINKS=1 tar Jxf "$fspath" -C /mnt "$tar_options"
    fi

    sync

    mkdir -p /tmp/bootpart
    mount $EFI_PART /tmp/bootpart
    rsync -a -L /mnt/boot/ /tmp/bootpart

    # Create a read-only etc image for persistent config feature
    mkdir -p /tmp/etcimg
    touch /mnt/etc.img
    truncate --size=25M /mnt/etc.img
    mkfs.ext4 -O 64bit /mnt/etc.img
    mount /mnt/etc.img /tmp/etcimg
    rsync -a /mnt/etc/ /tmp/etcimg
    mkdir /mnt/etc.lower

    umount /tmp/etcimg
    umount /tmp/bootpart
    umount /mnt

    # If the user specified turning on persist cfg by default, do so,
    # otherwise leave it to whatever the user had it on. Note that
    # this will activate the feature whether we're using the old persistent
    # data or a newly formatted partition.
    if [ -n "${persist_config}" ]; then
        mount $PERSIST_PART /mnt
        touch /mnt/persistconfig
        umount /mnt
    fi

    # Add preinit to kernel args
    bootarg="$bootarg init=/sbin/preinit"
    install_grub
fi

if [ -z $full_root ] && [ -z $mini_root ]; then
    mount $EFI_PART /mnt; cp /boot/Image /mnt; cp /boot/core-image-rescue-initramfs-bluefield.cpio.xz /mnt; umount /mnt

cat <<EOF

**********************************************************************
***                                                                ***
***   YOUR SYSTEM MAY CRASH AFTER REBOOT                           ***
***                                                                ***
***   Missing root file system in $ROOT_PART !!                    ***
***                                                                ***
***   Please make sure that you install a root file system prior   ***
***   to reboot. You may copy the initramfs file system to         ***
***   $ROOT_PART by doing the following:                           ***
***                                                                ***
***   bfinst --minifs                                              ***
***                                                                ***
***   Note that copying the initramfs to your eMMC parition is     ***
***   easy and fast but does not include many Linux packages you   ***
***   most likely want on your system.  For example, Mellanox OFED ***
***   is NOT on the initramfs (minifs).  The initramfs is normally ***
***   used for installation of non-Yocto linux distros and very    ***
***   basic system testing only.                                   ***
***                                                                ***
***   You should install the full root fs or the full root dev     ***
***   rootfs for a fully functional system.                        ***
***                                                                ***
***   Copy one of the following images to /tmp on the initramfs    ***
***   and then specify that file as an argument to bfinst as       ***
***   shown below.                                                 ***
***                                                                ***
***     core-image-full-BlueField-<version>.tar.xz                 ***
***     core-image-full-dev-BlueField-<version>.tar.xz             ***
***                                                                ***
***   bfinst --fullfs /tmp/<core-image>                            ***
***                                                                ***
**********************************************************************
EOF
fi

if [ -z "${skip_boot_update}" ]; then
    # Update eMMC boot partitions. Update via either capsule
    # path or default path (i.e., mlxbf-bootctl). This command
    # MUST be executed after 'install_grub', otherwise the newly
    # created boot option would be either cleaned up or unset
    # from default option.
    bfrec --policy dual
fi

# Update configuration in case it's overwritten by bfinst
bfcfg
