#!/bin/bash
##########################################################################
# Script : uboot.build
# Purpose: Build u-boot for Slackware AArch64's supported Hardware Models
# Author : Stuart Winter <mozes@slackware.com>
# Date...: 09-Feb-2021
##########################################################################
# Copyright 2021, 2022 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.
##########################################################################
# References:
# These may be helpful for the community when supporting other Hardware Models.
# http://opensource.rock-chips.com/wiki_Partitions
# https://stikonas.eu/wordpress/2019/09/15/blobless-boot-with-rockpro64/
# https://casualhacking.io/blog/2018/7/8/diy-root-of-trust-using-arm-trusted-firmware-on-the-96boards-hikey
# https://wiki.radxa.com/Rock/make_sd_image
# https://wiki.amarulasolutions.com/bsp/rockchip/rk3399/rockpro64.html
# https://forum.armbian.com/topic/12639-mainline-spi-nvme-boot-working-rockpro64-with-armbian/
# https://forum.pine64.org/showthread.php?tid=8174
# https://github.com/ayufan-rock64/linux-build/blob/master/recipes/flash-spi.md
# https://u-boot.readthedocs.io/en/latest/board/rockchip/rockchip.html
#
# The code to build a version of U-Boot for flashing to SPI is taken from:
# https://raw.githubusercontent.com/sigmaris/u-boot/v2020.01-ci/azure-pipelines.yml
# Thanks to sigmaris on the Pine64 forum for the build recipe.
# https://forum.pine64.org/showthread.php?tid=8685
#####################################################################################

source /usr/share/slackdev/buildkit.sh

CWD=$PWD
TMP=/tmp/build-uboot
BINS=$PWD/../bin/

# Set/reset defaults.
# These variables may have been overridden by the Hardware Model build functions
# so we need to reset them before building for each Hardware Model.
function set_default_vars() {
  # U-Boot version:
  # Hardware Model build functions may overwrite this version within their 'hwm_initialise'
  # function.  They'll also want to override function 'hwm_uboot_src_patch' if doing so
  # in case the set of patches don't apply to the other version.
  #UBOOTVER="2022.04" # prev working ver
  UBOOTVER="2023.01"
}

# Unset callout functions from a previous platform's build
# receipe.
# The function names are prefixed by 'hwm_' which stands for
# Hardware Model.
function unset_hwm_buildfunctions() {
  local fname
  for fname in \
    hwm_initialise \
    hwm_uboot_src_unpack_pre \
    hwm_uboot_src_unpack \
    hwm_uboot_src_unpack_post \
    hwm_uboot_src_patch \
    hwm_configure_uboot \
    hwm_build_uboot_pre \
    hwm_build_uboot \
    hwm_build_uboot_post \
    hwm_install_pre \
    hwm_install \
    hwm_install_post \
    hwm_build_spi \
    hwm_build_sdrecovery \
    hwm_build_final ; do
       unset -f  $fname ; done
}

# Build u-boot for the specified device:
function build_bootloader() {

  # Set/reset default variables that may have been changed by a previous
  # build from one of the Hardware Model's build functions:
  set_default_vars

  local devicetype=$1
  local chipset=$2
  local assetname=$3
  local slkportarch=$4

  echo "Device type..: ${devicetype}"
  echo "Chipset name : ${chipset}"
  echo "Asset name...: ${assetname}"
  echo "Architecture : ${slkportarch}"
  echo "**********************************"

  # I want the output file names to be lower case since some are 'Orange' and others 'orange'
  # which would just makes my documentation more complex.
  local lc_devicetype=$( echo $devicetype | tr '[A-Z]' '[a-z]' )

  # Load the build functions if there are any for this chipset.
  unset_hwm_buildfunctions
  [ -s $CWD/platform/${slkportarch}/${chipset}/uboot.build-functions ] && \
     { echo "Loading build functions for ${chipset}"
       . $CWD/platform/${slkportarch}/${chipset}/uboot.build-functions ;}

  # Call a Hardware Model initialisation function.
  # The purpose of this is to override any settings such as the version
  # of U-Boot, should your Hardware Model be using a different version.
  fnexists hwm_initialise && { hwm_initialise || \
    { echo "*** Failed Hardware Model Initialisation function for device $devicetype" ; exit 1 ;} ;}

  ############################################################################
  # U-Boot src unpack
  ############################################################################

  rm -rf $TMP
  mkdir -p $TMP

  # Call a Hardware Model pre- build function, if present:
  # The purpose of this is to perform any hacks prior to unpacking the source
  # such as changing the version of U-Boot from that within this caller script.
  fnexists hwm_uboot_src_unpack_pre && { hwm_uboot_src_unpack_pre || \
    { echo "*** Failed pre U-Boot src extraction function for device $devicetype" ; exit 1 ;} ;}

  echo "U-Boot version: ${UBOOTVER}"
  sleep 10

  # Call a Hardware Model installation function if present, otherwise
  # build using the standard method:
  errmsg="*** Failed U-Boot src extraction for device: $devicetype"
  fnexists hwm_uboot_src_unpack && { hwm_uboot_src_unpack || { echo "$errmsg" ; exit 1 ;} ;} || \
      { # Unpack U-boot source
        echo "*** Unpacking U-Boot src using standard routine ***"
        cd $TMP
        tar xf $CWD/sources/u-boot-$UBOOTVER.tar.?z*
        cd u-boot*/ || exit 1
        make mrproper || { echo "$errmsg" ; exit 1 ;} ;}

  # Call a Hardware Model installation function if present, otherwise
  # build using the standard method:
  errmsg="*** Failed to apply patches to U-Boot for : $devicetype"
  fnexists hwm_uboot_src_patch && { hwm_uboot_src_patch || { echo "$errmsg" ; exit 1 ;} ;} || \
      # Note that we're already in the U-Boot directory.
      { # Patch U-Boot source:
        echo "*** Patching U-Boot src using standard routine ***"
        # Apply patches:
        for pf in \
         0001-PBP-Fix-panel-reset.patch \
         0003-Turn-power-and-standby-LEDs-on-early.patch \
         0004-mmc-sdhci-allow-disabling-sdma-in-spl.patch \
         0005-arm-dts-Work-around-daughterboard-issues.patch ; do
           auto_apply_patch $CWD/sources/patches/uboot/$pf || failpatch
         done || { echo "$errmsg" ; exit 1 ;} ;}
#        for pf in $CWD/sources/patches/uboot/* ; do
#            auto_apply_patch $pf || failpatch
#        done || { echo "$errmsg" ; exit 1 ;} ;}

  # Call a Hardware Model post src-extraction function, if present:
  # The purpose of this is to apply any patches for a particular Hardware Model.
  fnexists hwm_uboot_src_unpack_post && { hwm_uboot_src_unpack_post || \
    { echo "*** Failed post U-Boot src extraction function for device $devicetype" ; exit 1 ;} ;}

  ############################################################################
  # U-Boot configuration
  ############################################################################
  echo "*** Configuration - installing config file***"
  rm -f .config
  # If we had a separate U-Boot config file for two or more Hardware Models that
  # shared a chipset, we would name our configs thusly:
  # platform/aarch64/rk3399/uboot.config_rockpro64-rk3399
  # platform/aarch64/rk3399/uboot.config_pinebook-pro-rk3399
  if [ -s $CWD/platform/${slkportarch}/${chipset}/uboot.config_${devicetype} ]; then
     echo "Using custom U-Boot config: $CWD/platform/${slkportarch}/${chipset}/uboot.config_${devicetype}"
     cp -fa $CWD/platform/${slkportarch}/${chipset}/uboot.config_${devicetype} .config
     # Should it need aligning:
     make V=1 oldconfig
   else
     # Take the default board config:
     echo "*** Building for ${devicetype} using default U-Boot config ***"
#     make BL31=$BINS/$chipset/bl31.elf V=1 ${devicetype}_defconfig oldconfig
     make V=1 ${devicetype}_defconfig oldconfig
    # Enable boot logo:
    # The U-Boot logo is enabled by default, so it's easier to overwrite
    # the default with Slackware's:
    cp -fav $CWD/sources/slacklogo/slack.bmp ./drivers/video/u_boot_logo.bmp
    # The proper way, required for pre 2022 release:
    # Can't get it to work on the 2021 release. oh well. roll on 22.
    #sed -i 's?# CONFIG_CMD_BMP is not set?CONFIG_CMD_BMP=y?g' .config
    #sed -i '\?^# CONFIG_SPLASH_SCREEN is not set.*? a\# CONFIG_SPLASH_SCREEN_ALIGN is not set' .config
    #sed -i '\?^# CONFIG_SPLASH_SCREEN_ALIGN is not set.*? a\# CONFIG_VIDEO_BMP_GZIP is not set' .config
    #cp -fav $CWD/sources/slacklogo/slack.bmp ./tools/logos/u-boot_logo.bmp
    #cp -fav $CWD/sources/slacklogo/slack.bmp ./tools/logos/denx.bmp
  fi
  #
  echo "*** Configuration - Slackware customisations***"
  # Brand it:
  sed -i 's?^CONFIG_IDENT_STRING=.*?CONFIG_IDENT_STRING="  Slackware"?g' .config
  #
  # We use a compressed Kernel:
  #sed -i 's?#define CONFIG_SYS_BOOTM_LEN.*?#define CONFIG_SYS_BOOTM_LEN 0x8000000?g' common/bootm.c
  #
  # Increase the logging:
  #sed -i 's?LEVEL=4?LEVEL=9?g' .config
  #
  # Call the chipset's U-Boot configuration function (if set):
  fnexists hwm_configure_uboot && { hwm_configure_uboot || \
    { echo "*** Failed to configure for device $devicetype" ; exit 1 ;} ;}

  ############################################################################
  # Build U-Boot
  ############################################################################

  # Call a Hardware Model pre- build function, if present:
  # The purpose of this is to perform any hacks prior to the build.
  fnexists hwm_build_uboot_pre && { hwm_build_uboot_pre || \
    { echo "*** Failed pre U-Boot build function for device $devicetype" ; exit 1 ;} ;}

  # Call a Hardware Model build function if present, otherwise
  # build using the standard method:
  errmsg="*** Failed to build for device: $devicetype"
  fnexists hwm_build_uboot && { hwm_build_uboot || { echo "$errmsg" ; exit 1 ;} ;} || \
      { echo "*** Building using standard options ***"
        make $NUMJOBS V=1 BL31=$BINS/$chipset/bl31.elf all u-boot.itb || { echo "$errmsg" ; exit 1 ;} ;}
##
   ### This is useful but needs to be in the proper place
   ### At the moment this fixes only a cosmetic issue with u-boot which
   ### can be corrected by 'saveenv'.
   ### Needs a bit more work to blend this in completely.
   # Copy built-in env object and extract env data
   cp env/built-in.o built_in_env.o
   objcopy -O binary -j ".rodata.default_environment" built_in_env.o
   # Replace null terminator in built-in env with newlines
   tr '\0' '\n' < built_in_env.o | sed '/^$/d' > built_in_env.txt

  # Make built-in env image with correct CRC
#  tools/mkenvimage -s 0x8000 -o $TMP/spi/spi_default_env.img built_in_env.txt
  tools/mkenvimage -s 0x8000 -o built_in_env.o built_in_env.txt
####

  # Call a Hardware Model post build function, if present:
  # The purpose of this is to perform any hacks post build.
  fnexists hwm_build_uboot_post && { hwm_build_uboot_post || \
    { echo "*** Failed post U-Boot build function for device $devicetype" ; exit 1 ;} ;}

  # Notes for making images:
  # Combined for U-Boot on SPI flash:
  #mkimage -n rk3399 -T ${ubootimgtype} -d tpl/u-boot-tpl.bin:spl/u-boot-spl.bin ${devicetype}_uboot.bin
  # U-Boot for SD card booting:
  ## TEMPORARY::::
#  if [ "$devicetype" = "rockpro64-rk3399" ]; then
#     mkimage -n rk3399 -T rksd -d ./tpl/u-boot-tpl-dtb.bin /tmp/${devicetype}_uboot.bin || exit 1
#     cat ./spl/u-boot-spl-dtb.bin >> /tmp/${devicetype}_uboot.bin || exit 1
#  fi

  ############################################################################
  # Install U-Boot
  ############################################################################

  # Call a Hardware Model post build function, if present:
  # The purpose of this is to perform any hacks prior to completing the build for
  # this Hardware Model.
  fnexists hwm_install_pre && { hwm_install_pre || \
    { echo "*** Failed pre-installation function for device $devicetype" ; exit 1 ;} ;}

  # Install the U-boot components ready for the 'inst_dskimg.build' script:
  # Wipe the previous versions of U-Boot:
  rm -rfv $BINS/$chipset/u-boot-*/${assetname}*
  mkdir -p $BINS/$chipset/u-boot-${UBOOTVER}

  # Call a Hardware Model installation function if present, otherwise
  # build using the standard method:
  errmsg="*** Failed installation for device: $devicetype"
  fnexists hwm_install && { hwm_install || { echo "$errmsg" ; exit 1 ;} ;} || \
      { # These generally aren't required as users will only need the final U-Boot binary, but
        # we include them should users want to experiment.
        echo "*** Installing U-Boot binaries into tree using standard routine ***"
        install -vpm644 idbloader.img $BINS/$chipset/u-boot-${UBOOTVER}/${assetname}-idbloader.img
        install -vpm644 u-boot.itb $BINS/$chipset/u-boot-${UBOOTVER}/${assetname}-u-boot.itb || \
        { echo "$errmsg" ; exit 1 ;} ;}

  # Call a Hardware Model post build function, if present:
  # The purpose of this is to perform any hacks prior to completing the build for
  # this Hardware Model.
  fnexists hwm_install_post && { hwm_install_post || \
    { echo "*** Failed post installation function for device $devicetype" ; exit 1 ;} ;}

  # For boards to which we'll write Slackware AArch64's U-Boot to SPI,
  # let's build the SPI versions:
  rm -rf $TMP/spi
  mkdir -p $TMP/spi
  fnexists hwm_build_spi && { hwm_build_spi || { echo "*** Failed SPI U-Boot build for device $devicetype" ; exit 1 ;} ;}

  # Build U-Boot binaries for the SD card Recovery/Initialisation Images.
  # These are required on some Hardware Models to install a version of U-Boot capable of
  # booting the Slackware Installer, where a newer version of U-Boot can be installed.
  # They're also to cater for the situation where the SPI Flash goes bad (which happened
  # on numerous occasions during the development of the Slackware AArch64 port!), where
  # one can insert the MicroSD card and have it reflash.
  #
  # The SD card images use the 'rk3399_rockpro64-sd-idbloader.img'
  # binaries.
  # The actual SD card image is created by 'inst_dskimg.build' which uses the binaries
  # created by this script ('uboot.build'):
  rm -rf $TMP/sdrecovery
  mkdir -p $TMP/sdrecovery
  # There is no standard for this, since it depends on each Hardware Model.
  # The Rpi4 does not have nor need one since our U-Boot is deployed to the Micro SD card boot Storage device
  # on the primary partition.
  fnexists hwm_build_sdrecovery && { hwm_build_sdrecovery || { echo "*** Failed SD U-Boot build for device $devicetype" ; exit 1 ;} ;}

  # Call a Hardware Model post build function, if present:
  # The purpose of this is to perform any hacks prior to completing the build for
  # this Hardware Model.
  fnexists hwm_build_final && { hwm_build_final || \
    { echo "*** Failed finalisation build function for device $devicetype" ; exit 1 ;} ;}

  echo "*** Build for $devicetype complete ***"
}

# Build the known devices:
# <uboot-config-name>:<chip set name>:<asset output file name> <Slackware port architecture name>
# Note: for the Pinebook Pro it'll use the RockPro64 assets.
# If Hardware Models using the same SoC require customisations, you'd change
# the asset name.
# These asset names are used by 'inst_dskimg.build'

# We no longer use U-Boot on the Raspberry Pi - we use its native BL.
#build_bootloader rpi_arm64 bcm2711 bcm2711_rpi4 aarch64 || \
#   { echo "Failed build for RPi4 (rpi_arm64)" ; exit 1 ;}
#build_bootloader rpi_arm64 rpi_generic rpi_generic aarch64 || \
#   { echo "Failed build for RPi4 (rpi_arm64)" ; exit 1 ;}
# This is if the RPi3 requires a different U-Boot config than the RPi4.
# To be determined.
#build_bootloader rpi_arm64 bcm2837 bcm2837_rpi3 aarch64 || \
#   { echo "Failed build for RPi3 (rpi_arm64)" ; exit 1 ;}

build_bootloader rockpro64-rk3399 rk3399 rk3399_rockpro64 aarch64 || \
   { echo "Failed build for rockpro64-rk3399" ; exit 1 ;}

# Whilst Slackware ships a 'generic' rk3399 Slackware installer, the
# U-Boot binaries are configured/built for each Hardware Model.
# The only point of divergence presently is the U-Boot config:
# 'pinebook-pro-rk3399_defconfig' vs 'rockpro64-rk3399_defconfig'
build_bootloader pinebook-pro-rk3399 rk3399 rk3399_pinebookpro aarch64 || \
   { echo "Failed build for pinebook-pro-rk3399" ; exit 1 ;}

echo "*** uboot.build : complete for all Hardware Models"
