# vim: filetype=sh

# grub configuration
# ------------------

prepare_rootfs()
{
    # if update-grub is called as part of the package installation
    # it should properly find our virtual device.
    # (we will properly install the bootloader on the final device
    # anyway, this is only useful to avoid warnings)
    update_grup_device_map
}

cleanup_rootfs()
{
    rm boot/grub/device.map
}

# let grub find our virtual device
update_grup_device_map()
{
    cd /
    mkdir -p boot/grub
    cat > boot/grub/device.map << END_MAP
(hd0) $loop_device
END_MAP
    cd - >/dev/null
}

# * customize boot parameters
# * fix obsolete options in /etc/default/grub
#   (https://bugs.launchpad.net/ubuntu/+source/grub2/+bug/1258597)
update_grub_conf()
{
    . /etc/default/grub
    existing_bootargs="$GRUB_CMDLINE_LINUX"
    recommended_bootargs="rootdelay=3"
    user_bootargs="$@"

    # order of precedence is:
    # user_bootargs > recommended_bootargs > existing_bootargs

    # In the case of grub, we add bootargs to grub's GRUB_CMDLINE_LINUX variable.
    # However, when deleting a bootarg, it may actually be in GRUB_CMDLINE_LINUX_DEFAULT too.

    GRUB_CMDLINE_LINUX="$(aggregate_kernel_cmdline $existing_bootargs $recommended_bootargs $user_bootargs)"

    only_minus_modifiers="$(echo "$user_bootargs" | tr ' ' '\n' | happy_grep '^-')"
    GRUB_CMDLINE_LINUX_DEFAULT="$(aggregate_kernel_cmdline $GRUB_CMDLINE_LINUX_DEFAULT $only_minus_modifiers)"

    sed -i -e "s/GRUB_CMDLINE_LINUX=.*/GRUB_CMDLINE_LINUX=\"$GRUB_CMDLINE_LINUX\"/" \
           -e "s/GRUB_CMDLINE_LINUX_DEFAULT=.*/GRUB_CMDLINE_LINUX_DEFAULT=\"$GRUB_CMDLINE_LINUX_DEFAULT\"/" \
           -e "s/^GRUB_HIDDEN/#GRUB_HIDDEN/g" \
            /etc/default/grub

    # let the user know which bootargs where finally selected
    applied_kernel_cmdline=$(aggregate_kernel_cmdline $GRUB_CMDLINE_LINUX $GRUB_CMDLINE_LINUX_DEFAULT)
}

# display the grub interface on serial line
update_grub_conf_serial_line()
{
    cat >> ./etc/default/grub << EOF
GRUB_TERMINAL=serial
GRUB_SERIAL_COMMAND="serial --speed=115200 --unit=0 --word=8 --parity=no --stop=1"
EOF
}

quiet_grub_install()
{
    device=$1

    update_grup_device_map $device

    # grub-install & update-grub print messages to standard
    # error stream although most of these are just
    # informational (or minor bugs). Let's discard them.
    output="$(
        grub-install $device 2>&1   && \
        update-initramfs -u 2>&1    && \
        update-grub 2>&1
    )" || return_code=$?

    echo "$output" |    happy_grep -v "No error"          | \
                        happy_grep -v "Installing"        | \
                        happy_grep -v "Generating"        | \
                        happy_grep -v "Found .* image:"   | \
                        happy_grep -v "lvmetad"           | \
                        happy_grep -v "etc.modprobe.d"    | \
                        happy_grep -v "^done$" 1>&2

    # the return value we want is the one we caught
    # earlier (or none if all went well):
    return $return_code
}

configure_bootloader()
{
    # tune grub conf
    update_grub_conf $kernel_bootargs
    if [ "$config_grub_on_serial_line" -gt 0 ]
    then
        update_grub_conf_serial_line
    fi

    # disable quickboot:
    # work around grub displaying error message with our LVM setup
    # disable vt_handoff:
    # the linux console should be visible during startup (especially
    # if we must enter the root password, or in installer-mode), do
    # not switch to vt7.
    # note: even if the file etc/grub.d/10_linux is re-created
    # after an upgrade of the package grub-common, our script
    # 09_linux_custom will be executed first and take precedence.
    sed -i -e 's/quick_boot=.*/quick_boot=0/' \
           -e 's/vt_handoff=.*/vt_handoff=0/' etc/grub.d/10_linux
    mv etc/grub.d/10_linux etc/grub.d/09_linux_custom
}

install_bootloader()
{
    quiet_grub_install $loop_device
}

# UEFI support
# ------------

# we generate a standalone UEFI binary, used
# for booting on UEFI systems.
# actually, since we cannot install both grub-pc
# and grub-efi on the embedded system (conflict),
# we install grub-pc only. However, this binary
# image just look for the configuration file generated
# by the grub-pc installation and loads it. Thus
# this UEFI configuration also stays up-to-date.

uefi_binary_image_info() {
    case "$(get_target_cpu "$ORIG_TREE")" in
        "amd64")
            grub_arch="x86_64-efi"
            img_name="BOOTX64.efi"
            ;;
        "i386")
            grub_arch="i386-efi"
            img_name="BOOTIA32.efi"
            ;;
    esac
    echo $grub_arch $img_name
}

build_uefi_binary_image()
{
    stick_os_id="$1"
    rootfs_label="$(get_rootfs_label $stick_os_id)"
    set -- $(uefi_binary_image_info)
    grub_arch="$1"
    img_name="$2"
    out_binary_path="$(abspath "$img_name")"
    mkdir -p $DBSTCK_TMPDIR/efi/boot/grub
    cd $DBSTCK_TMPDIR/efi
    cat > boot/grub/grub.cfg << EOF
insmod part_gpt
insmod lvm
insmod efi_gop
insmod efi_uga
search --set rootfs --label $rootfs_label
configfile (\$rootfs)/boot/grub/grub.cfg
EOF
    grub-mkstandalone \
            --directory="/usr/lib/grub/$grub_arch/" --format="$grub_arch"   \
            --compress="gz" --output="$out_binary_path"            \
            "boot/grub/grub.cfg"
    cd - >/dev/null # return to previous dir
}
