Finding countdown timer in Grub 2.02 source code
Asked Answered
B

2

6

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.

Bobbybobbye answered 14/7, 2018 at 17:39 Comment(6)
A good start is to grep for GRUB_TIMEOUT in the source code. This allows you to find where the variable is handled. Then, read the code that handles it and proceed from there.Interactive
If you do make this change I would suggest either excepting a floating point value or changing the name of the configuration value so that your changes might actually get incorporated back in to Grub. For example GRUB_TIMEOUT=2.5 or GRUB_TIMEOUT_MS=2500.Frigorific
I don't see a URL for your changes. If you make your repo accessible somewhere, that would potentially be useful for the next person that wants this change.Ague
@PeterCordes There is only one line to change. I'm not sure how to gitclone all of grub and the ramifications for synchronizing future grub updates. Could I perhaps add more instructions above?Bobbybobbye
Oh nvm then; I only casually skimmed this Q&A, didn't realize it ended up being a 1-line change that you already describe in the question. (BTW, post answers as answers, not edits to the question.) Then the first line of your answer can be the line that needs to change (preferably with some diff context)Ague
@PeterCordes I'll work on a proper answer for X86 EFI boot. The solution so far is only i386-pc which I used in Virtual Machine. I need help getting grub to compile for different targets...Bobbybobbye
I
7

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.

Interactive answered 14/7, 2018 at 18:0 Comment(6)
Fantastic! But "add a new option to sleep" sounds difficult. Perhaps in gfxmenu module I could put in #def SLEEP_IN_MS (micro-seconds) and then in sleep.c I could put in #ifdef SLEEP_IN_MS. Where I would then call sleep with: grub_interruptible_millisleep (10) and a couple lines below: grub_millisleep (10). I presume I'll only be compiling /grub-core/gfxmenu/gfxmenu.c into /boot/grub/x86_64-efi/gfxmenu.mod which hopefully means it doesn't impact the rest of grub bootloader.Bobbybobbye
@Bobbybobbye If you do it this way, all calls of sleep will be changed. Adding an option isn't too hard. Indeed, I see it as a good exercise.Interactive
It does sounds like a great first-time learning exercise. What would take a seasoned grub veteran 15 minutes to do might take me 15+ hours (still have to learn cc + link) but I consider it a good education investment. It's kind of poetic? starting first C project in grub which is first program run after BIOS POST.Bobbybobbye
@Bobbybobbye It's definitely going to help you! Adding a new option involves changing the options array. Read the code in include/grub/lib/arg.h and grub-core/lib/arg.c to understand how this works.Interactive
@Bobbybobbye careful with confusing milli and micro though :)Izzard
Well this weekend project is finished. I learned alot about C and grub. Now I think I'll go back to bash coding for awhile :) I've updated my question with the solution but am accepting your answer because you guided me in the right direction :)Bobbybobbye
B
2

Thanks to the help of the accepted answer I was able to achieve the goal using a different method. After successful Grub 2.02 source code change and recompile, /etc/default/grub was changed to a 3.5 second countdown with GRUB_TIMEOUT=35.

Notice how the circular progress is now smooth with no "chunks":

New Grub Boot


The code to change in:

/grub-2.02/grub-core/normal/menu.c

Line 546:

/* Check whether a second has elapsed since the last tick.  If so, adjust
   the timer and return 1; otherwise, return 0.  */
static int
has_second_elapsed (grub_uint64_t *saved_time)
{
  grub_uint64_t current_time;

  current_time = grub_get_time_ms ();
  /* July 14, 2018 Use deciseconds - change 1000 to 100 */
  if (current_time - *saved_time >= 100)
    {
      *saved_time = current_time;
      return 1;
    }
  else
    return 0;
}

Change the line:

if (current_time - *saved_time >= 1000)

to:

if (current_time - *saved_time >= 100)

Voila! one line of code to change. Plus two comment lines added for good measure.

How to compile grub 2.02

Before following instructions on Grub's website:

sudo apt install bison
sudo apt install flex

Then follow grub's website instructions:

cd grub-2.02
./configure

Run next command on Grub's website:

make install

Files are created in /usr/local/bin (surprise!!!) along with .../grub-2.02 directory which is to be expected.


Miscellaneous problems compiling grub

I ended up cloning source to VM (Lubuntu 16.04) and recompiling there. Using the newly compiled grub-install mucked things up and I had to use sudo apt install grub2 to get fresh install. Then manually copying newly compiled files to /boot/grub/i386-pc

My terminal-box got all skewed up so I'll have to create a new grub background image. In the image below I changed GRUB_TIMEOUT=35 for 3.5 second countdown.


July 16, 2018 update

Figured out to one parameter to use in order to get X86, EFI support:

./configure –with-platform=efi

*******************************************************
GRUB2 will be compiled with following components:
Platform: x86_64-efi
With devmapper support: No (need libdevmapper header)
With memory debugging: No
With disk cache statistics: No
With boot time statistics: No
efiemu runtime: No (not available on efi)
grub-mkfont: No (need freetype2 library)
grub-mount: No (need FUSE library)
starfield theme: No (No build-time grub-mkfont)
With libzfs support: No (need zfs library)
Build-time grub-mkfont: No (need freetype2 library)
Without unifont (no build-time grub-mkfont)
Without liblzma (no support for XZ-compressed mips images) (need lzma library)
*******************************************************

However, after make install there is an error:

Making install in grub-core
make[2]: Entering directory '/home/rick/src/grub-2.02/grub-core'
gcc -E -DHAVE_CONFIG_H  -Wall -W  -DGRUB_MACHINE_EFI=1 -DGRUB_MACHINE=X86_64_EFI -m64 -nostdinc -isystem /usr/lib/gcc/x86_64-linux-gnu/5/include -I../include -I../include -DGRUB_FILE=\"symlist.h\" -I. -I. -I.. -I.. -I../include -I../include -I../grub-core/lib/libgcrypt-grub/src/   -DGRUB_KERNEL=1 -D_FILE_OFFSET_BITS=64 -DGRUB_SYMBOL_GENERATOR=1 symlist.h > symlist.p || (rm -f symlist.p; exit 1)
symlist.h:25:44: fatal error: ../include/grub/machine/kernel.h: No such file or directory
compilation terminated.
Makefile:42544: recipe for target 'symlist.c' failed
make[2]: *** [symlist.c] Error 1
make[2]: Leaving directory '/home/rick/src/grub-2.02/grub-core'
Makefile:10904: recipe for target 'install-recursive' failed
make[1]: *** [install-recursive] Error 1
make[1]: Leaving directory '/home/rick/src/grub-2.02'
Makefile:11927: recipe for target 'install' failed
make: *** [install] Error 2

I filed a bug report with Grub folks (July 2018) but haven't heard a thing back. The next step for EFI system is to download the source code on fresh install using Ubuntu's repositories instead of Grub's website instructions.


Bobbybobbye answered 2/9, 2018 at 16:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.