Search code examples
cassemblygrub

Finding countdown timer in Grub 2.02 source code


Change Grub's timeout to 1/10th or 1/100th second intervals

Using Grub 2.02 on UEFI system with AMD64 architecture. I'd like to change grub's timeout counter from 1 second intervals to 1/10th second or 1/100th second intervals. The reason is to make gfxmenu circular progress countdown less "choppy". Boot GIF below shows 5 second count down in circular 1 second "chunks":

Grub Boot

After successful source code change and recompile, /etc/default/grub would be changed as follows:

  • If 1/10th second interval, a 2.5 second countdown would be GRUB_TIMEOUT=25.
  • if 1/100th second interval, a 2.5 second countdown would be GRUB_TIMEOUT=250.

Grub 2.02 Source is 1/2 million lines

I've downloaded the source as described here: how to build grub2 bootloader from it's source and test it with qemu emulator and spent time browsing source files. However there are 477k lines to search:

~/src/grub-2.02$ wc -l **/*

      20 asm-tests/arm.S
      18 asm-tests/i386-pc.S
       4 asm-tests/i386.S
      11 asm-tests/mips.S
       8 asm-tests/powerpc.S
            (... SNIP ...)
     115 util/spkmodem-recv.c
  477316 total

I've done many bash projects in Ask Ubuntu but this will be my first C/Assembler Linux project. As a "newbie" my thoughts are:

  • Which file contains the countdown timer source code?
  • How do I change the interval to 1/10th or 1/100th of a second?
  • Was putting source code under my home directory a conventional method?
  • Any tips on compiling and testing in Virtualbox would be helpful.

Please note only the first question is relevant. The other questions are for answers where the author chooses be more detailed.


Solution

  • The variable GRUB_TIMEOUT is evaluated in util/grub.d/00_header.in.

    if [ "x$GRUB_BUTTON_CMOS_ADDRESS" != "x" ]; then
        cat <<EOF
    if cmostest $GRUB_BUTTON_CMOS_ADDRESS ; then
    EOF
    make_timeout "${GRUB_HIDDEN_TIMEOUT_BUTTON}" "${GRUB_TIMEOUT_BUTTON}" "${GRUB_TIMEOUT_STYLE_BUTTON}"
    echo else
    make_timeout "${GRUB_HIDDEN_TIMEOUT}" "${GRUB_TIMEOUT}" "${GRUB_TIMEOUT_STYLE}"
    echo fi
    else
    make_timeout "${GRUB_HIDDEN_TIMEOUT}" "${GRUB_TIMEOUT}" "${GRUB_TIMEOUT_STYLE}"
    fi
    

    Note that this is a script that generates a script which is why it looks rather weird. make_timeout looks like this (ibid.):

    make_timeout ()
    {
        if [ "x${3}" != "x" ] ; then
            timeout="${2}"
            style="${3}"
        elif [ "x${1}" != "x" ] && [ "x${1}" != "x0" ] ; then
            # Handle the deprecated GRUB_HIDDEN_TIMEOUT scheme.
            timeout="${1}"
            if [ "x${2}" != "x0" ] ; then
                grub_warn "$(gettext "Setting GRUB_TIMEOUT to a non-zero value when GRUB_HIDDEN_TIMEOUT is set is no longer supported.")"
            fi
            if [ "x${GRUB_HIDDEN_TIMEOUT_QUIET}" = "xtrue" ] ; then
                style="hidden"
                verbose=
            else
                style="countdown"
                verbose=" --verbose"
            fi
        else
            # No hidden timeout, so treat as GRUB_TIMEOUT_STYLE=menu
            timeout="${2}"
            style="menu"
        fi
        cat << EOF
    if [ x\$feature_timeout_style = xy ] ; then
      set timeout_style=${style}
      set timeout=${timeout}
    EOF
        if [ "x${style}" = "xmenu" ] ; then
            cat << EOF
    # Fallback normal timeout code in case the timeout_style feature is
    # unavailable.
    else
      set timeout=${timeout}
    EOF
        else
            cat << EOF
    # Fallback hidden-timeout code in case the timeout_style feature is
    # unavailable.
    elif sleep${verbose} --interruptible ${timeout} ; then
      set timeout=0
    EOF
        fi
        cat << EOF
    fi
    EOF
    }
    

    As you can see, it just calls sleep with some options at the end. This command is defined in grub-core/commands/sleep.c. While the sleep command can only sleep in increments of whole seconds, the underlying function grub_millisleep can do better.

    It should be easy to patch this function by changing all the grub_millisleep(1000) calls to grub_millisleep(100), but keep in mind that this breaks all uses of sleep. A cleaner option is to add a new option to sleep so the behaviour can be selected on a case-by-case basis.