#!/bin/bash

# Copyright 2021 Stuart Winter, Donostia, Spain.
# All rights reserved.
#
# Redistribution and use of this script, with or without modification, is
# permitted provided that the following conditions are met:
#
# 1. Redistributions of this script must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
#
#  THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.
#
# Script.....: /usr/lib/setup/armedslack-bootloader-flash
# Called from: /usr/lib/setup/SeTconfig as one of the post-installation
#              scripts within the new OS's /var/lib/pkgtools/setup
#              and subsequently removed before the installer completes.
# Purpose....: Write U-Boot to SPI flash of supported devices within
#              the Slackware installer.
# Version....: 1.00
# Date.......: 18-May-2021
# Authors....: Stuart Winter <mozes@slackware.com>
################################################################################
# References and Credits:
# Kamil Trzciński's RockPro64 repository:
# https://github.com/ayufan-rock64
# https://github.com/ayufan-rock64/linux-u-boot/releases
################################################################################
# Todo:
# a)Potentially verify signatures of flash, but this isn't so useful since
# the binaries are shipped within an SD card image only.
# This just needs some supplemental data to be created to support it.
# b) verify the flash interface name, as these are hard coded
#    e.g. 'spi0.0' on RockPro64.
#    Not worth it though - this is a SoC - the identity alone is enough
#    to be certain of the hardware layout and availability.
################################################################################

# This is only supported on AArch64.  Exit silently on other archs:
[[ ! "$( uname -m )" =~ (aarch64) ]] && exit 0

# Default settings:
PLATFORMDIR=${T_PX}/boot/platform/aarch64/
# $T_PX is where /usr/lib/setup/SeTpartitions stores the mount point
# for the new OS' root fs:
# /boot is the SD card.  These u-boot SPI flash binaries are not packaged in
# a Slackware package, but are added by sdcards.build.
# Naming convention for SPI versions of U-Boot:
UBOOTSPIBIN=spi-idbloader.img # this is a suffix
platform_detected=0

################ Functions ################################

# Flash management:
#
# Note: for Hardware Models that use 'NAND' flash, we need to
# update this function to support both.
# The code is commented for NAND.
# Note: The RK3399 has 'NOR' flash not 'NAND'.
function write_flash() {
   local flash_dev=${1}
   local flash_bin=${2}

# Example for Hardware Models that use 'NAND' flash:
#   echo "Flash device: ${flash_dev}  Operation: ERASING"
#   echo "This will take a few minutes..."
#   flash_erase ${flash_dev} 0 0
#   estat=$?
#   if [ $estat -gt 0 ]; then
#      read -p "An error occurred.  Press ENTER to continue"
#      return $estat
#   fi
#
#   echo
#   echo "Flash device: ${flash_dev}  Operation: WRITING"
#   echo "Image file: $( basename ${flash_bin} )"
#   echo
#   echo "This can take between 5 to 30 minutes during which"
#   echo "time you will not see any output here."
#   echo
#   echo "Please wait..."

#   echo "This will take approximately two (2) minutes"

   # For Hardware Models with 'NAND', you'd use nandwrite
   # to install the Boot Loader:
   #nandwrite -qp ${flash_dev} < ${flash_bin}

   # flashcp takes care of erase & write directly:
   printf "\nFlash device: ${flash_dev}  Operation: FLASHING\n"
   printf "Image file: $( basename ${flash_bin} )\n\n"
   printf "This will take approximately two (2) minutes\n\n"
   printf "Please wait...\n\n"
   flashcp -vA ${flash_bin} ${flash_dev}
   estat=$?
   if [ $estat -gt 0 ]; then
      read -p "An error occurred.  Press ENTER to continue"
      return $estat
    else
      return 0
   fi
}

function offerretry() {
   dialog \
      --backtitle "Slackware ARM / AArch64 installer" \
      --title "INSTALL BOOT LOADER INTO ONBOARD FLASH (SPI FLASH)" --yesno \
"\nA failure was encountered during the flashing process\n\n
Without the Boot Loader being successfully installed to the onboard
flash of this Hardware Model, it may fail to boot.\n\n

\n\nDo you want to retry? (recommendation is 'Yes')\n \

\n" 15 69
   # If they selected no, exit now.
   return $?
}

function offerready() {
   dialog \
      --backtitle "INSTALL BOOT LOADER " \
      --title "INSTALL BOOT LOADER INTO ONBOARD FLASH (SPI FLASH)" --ok-button "OK" \
      --msgbox "\n
\nYour machine must remain powered up during the process.\n

\n\nPress ENTER to begin\n" 12 60
   clear
   return 0
}

function offersuccess() {
   dialog \
      --backtitle "INSTALL BOOT LOADER " \
      --title "INSTALL BOOT LOADER INTO ONBOARD FLASH (SPI FLASH)" --ok-button "OK" \
      --msgbox "\n\nFlashing was successful.\n" 9 60
   clear
   return 0
}

################ Functions ################################

# Each Hardware Model should expose its model via this
# interface.
# If we cannot find it, this might constitute an error condition
# if we _are_ supposed to flash the SPI on this Hardware Model.
#
# I suppose this could be a dialog, but this should never happen
# with the supported Hardware Models for which this Kernel is
# aligned.
if [ -f /proc/device-tree/model ]; then
   export DEVTYPE=$( strings /proc/device-tree/model 2>/dev/null )
#   for platformscr in /load_kernel_modules.scr/platform/$ARCH/* ; do
#      . $platformscr ;
#   done
 else
   echo "${0}: ERROR: unable to detect system type from /proc/device-tree/model"
   echo "If your system does not require a boot loader to be installed"
   echo "to your hardware's onboard flash storage, you can ignore this message."
   echo "Exiting"
   sleep 4
   exit 1
fi

# Set the variables on a per-device basis.
# If you need to add your hardware model here, see the instructions at:
# http://arm.slackware.com/slackwarearm-devel/
#
# If your Hardware Model uses the same SoC, and has a different
# SPI flash binary file, you should have a separate stanza.
# For example: the Pinebook Pro and the RockPro64 share the SoC 'rk3399',
# but diverge at the U-Boot binary.
# The names of these assets are within the Slackware ARM source tree:
# /platform/aarch64/src/uboot.build
#
# TODO:
#
# To support other Hardware Models in the future, we'll break
# this code out in the same manner as the Slackware ARM Linux Kernel Module Loader.
case "$DEVTYPE" in
   ###########################################
   # RK3399-based Hardware Models:
   ###########################################
   #
   # Hardware Model-specific settings:
   "Pine64 Pinebook Pro"*) HWMODEL=pinebookpro ;&
   "Pine64 RockPro64"*|"Pine64 Pinebook Pro"*)
      HWMODEL=${HWMODEL:-"rockpro64"}
      platform_detected=1
      SOC=rk3399
      BOARDFLASHDEV=/dev/mtd0
      # On the RockPro64:
      #root@bladswede:~# cat /proc/mtd
      #dev:    size   erasesize  name
      #mtd0: 01000000 00001000 "spi0.0"
      BOARDFLASHNAME=spi0.0
      UBOOTSPIBIN=${SOC}_${HWMODEL}-${UBOOTSPIBIN} ;;
esac

# Sanity check:
if [ $platform_detected -ne 1 ]; then
   echo "$0 : platform not detected"
   echo "No boot loader flashing required."
   # Writing a new boot loader to the SPI flash is not a requirement
   # for Slackware ARM / AArch64.
   exit 0
fi

# Ensure that we can find the boot loader:
if [ ! -s ${PLATFORMDIR}/${SOC}/${UBOOTSPIBIN} ]; then
   echo "$0 : ERROR - unable to find boot loader image at"
   echo "${PLATFORMDIR}/${soc}/${UBOOTSPIBIN}"
   exit 4
fi

# Ensure that the flash interface exists:
if [ ! -c ${BOARDFLASHDEV} ]; then
   echo "$0 : ERROR - flash device interface ${BOARDFLASHDEV}"
   echo "     does not exist or is not of the type expected."
   exit 1
fi

dialog \
   --backtitle "Slackware ARM / AArch64 installer" \
   --title "INSTALL BOOT LOADER INTO ONBOARD FLASH (SPI FLASH)" --yesno \
"\nHardware Model: ${DEVTYPE}\n
System On Chip: ${SOC}\n

\nThis Hardware Model requires that the Boot Loader be flashed to the
onboard flash storage.\n
\nHowever, if you have already installed it using this tool, or you are
happy with the one you are using, you can skip this step.

\n\nDo you want to flash it now? (recommendation is 'Yes')\n \

\n" 17 69

# If they selected no, exit now.
[ $? = 1 ] && { clear ; exit 0 ;}

# Loop until either flashing was successful or the user
# abandons it (after being offered to retry):
while true; do
   offerready
   write_flash $BOARDFLASHDEV ${PLATFORMDIR}/${SOC}/${UBOOTSPIBIN}
   estat=$?
   if [ $estat -gt 0 ]; then
      offerretry || { clear ; exit 1 ;} # Failed to flash, user didn't want to retry
    else
      # Flashing was successful, and user acknowledged:
      offersuccess
      break;
   fi
done


# Old notes - to be removed#####################
#
# Let's do a sanity check first:
# On the RockPro64, given that we're doing this from the installer
# it means that we got here, so let's ensure that we've found the
# correct storage device to which we'll write our new U-Boot:
# Probably want to check for SPI version first, infact to verify that we're targeting
# the right one as a extra sanity check.
# This won't help - it's possible that the flash is corrupt.
# All that matters is whether we can write a new one to it successfully.
#head -n4000 /dev/mtd0 2>/dev/null | strings | grep "^U-Boot "

# Idea is:
# loop thru a sub dir, in the same way as load_kernel_modules
# use same logic as platform loaders: detect hw, check spi is present;
# ensure name=spi0.0
# display current version - it's possible for UB to be absent, so null is acceptable
# offer user to re-flash
