在 Grub 2.02 源代码中查找倒数计时器

Posted

技术标签:

【中文标题】在 Grub 2.02 源代码中查找倒数计时器【英文标题】:Finding countdown timer in Grub 2.02 source code 【发布时间】:2018-12-22 19:45:50 【问题描述】:

将 Grub 的超时更改为 1/10 或 1/100 秒间隔

在 AMD64 架构的 UEFI 系统上使用 Grub 2.02。我想将 grub 的超时计数器从 1 秒间隔更改为 1/10 秒或 1/100 秒间隔。原因是让gfxmenu 循环进度倒计时不那么“断断续续”。下面的启动 GIF 显示循环 1 秒“块”中的 5 秒倒计时:

修改源代码成功并重新编译后,/etc/default/grub会修改如下:

如果间隔为 1/10 秒,则 2.5 秒倒计时将是 GRUB_TIMEOUT=25。 如果间隔为 1/100 秒,则 2.5 秒倒计时将是 GRUB_TIMEOUT=250

Grub 2.02 源是 1/2 百万行

我已按照此处所述下载源代码:how to build grub2 bootloader from it's source and test it with qemu emulator,并花时间浏览源文件。但是有 477k 行要搜索:

~/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

我在 Ask Ubuntu 中完成了许多 bash 项目,但这将是我的第一个 C/Assembler Linux 项目。作为一个“新手”,我的想法是:

哪个文件包含倒计时源代码? 如何将时间间隔更改为 1/10 秒或 1/100 秒? 将源代码放在我的主目录下是一种传统方法吗? 任何有关在 Virtualbox 中编译和测试的提示都会有所帮助。

请注意只有第一个问题是相关的。其他问题是作者选择的更详细的答案。

【问题讨论】:

一个好的开始是在源代码中使用 grep for GRUB_TIMEOUT。这使您可以找到处理变量的位置。然后,阅读处理它的代码并从那里继续。 如果您确实进行了此更改,我建议您将浮点值除外或更改配置值的名称,以便您的更改实际上可能会重新合并到 Grub 中。例如GRUB_TIMEOUT=2.5GRUB_TIMEOUT_MS=2500 我没有看到您所做更改的 URL。如果你让你的 repo 可以在某个地方访问,那可能对下一个想要这个改变的人有用。 哦,那么 nvm;我只是随便浏览了这个问答,没有意识到它最终变成了您在问题中已经描述的 1 行更改。 (顺便说一句,将答案作为答案发布,而不是对问题进行编辑。)然后答案的第一行可能是需要更改的行(最好带有一些差异上下文) @PeterCordes 我将为 X86 EFI 启动提供正确的答案。到目前为止的解决方案只是我在虚拟机中使用的 i386-pc。我需要帮助让 grub 为不同的目标进行编译... 【参考方案1】:

变量GRUB_TIMEOUTutil/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

请注意,这是一个生成脚本的脚本,这就是它看起来很奇怪的原因。 make_timeout 看起来像这样(同上):

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

如您所见,它只是调用sleep,最后带有一些选项。此命令在grub-core/commands/sleep.c 中定义。虽然sleep 命令只能以整秒为增量休眠,但底层函数grub_millisleep 可以做得更好。

通过将所有grub_millisleep(1000) 调用更改为grub_millisleep(100) 应该很容易修补此功能,但请记住,这会破坏sleep 的所有用途。一个更简洁的选项是向sleep 添加一个新选项,以便可以根据具体情况选择行为。

【讨论】:

太棒了!但是“给sleep添加一个新选项”听起来很难。也许在 gfxmenu 模块中我可以输入#def SLEEP_IN_MS(微秒),然后在sleep.c 中我可以输入#ifdef SLEEP_IN_MS。然后我会打电话给 sleep :grub_interruptible_millisleep (10) 和下面几行:grub_millisleep (10)。我想我只会将/grub-core/gfxmenu/gfxmenu.c 编译成/boot/grub/x86_64-efi/gfxmenu.mod,希望这意味着它不会影响 grub 引导加载程序的其余部分。 @WinEunuuchs2Unix 如果你这样做,sleep 的所有调用都会改变。添加一个选项并不难。事实上,我认为这是一个很好的练习。 这听起来确实是一个很棒的第一次学习练习。一个经验丰富的 grub 资深人士需要 15 分钟才能完成的事情可能需要我 15 多个小时(仍然需要学习 cc + link),但我认为这是一项很好的教育投资。有点诗意?在 grub 中启动第一个 C 项目,这是 Bios POST 之后运行的第一个程序。 @WinEunuuchs2Unix 它肯定会帮助你!添加新选项涉及更改options 数组。阅读include/grub/lib/arg.hgrub-core/lib/arg.c 中的代码以了解其工作原理。 @WinEunuuchs2Unix 小心混淆毫微和微 :)【参考方案2】:

感谢接受的答案的帮助,我能够使用不同的方法实现目标。在 Grub 2.02 源代码更改成功并重新编译后,/etc/default/grub 更改为 3.5 秒倒计时GRUB_TIMEOUT=35

注意现在循环进度是如何顺利的,没有“块”:


要更改的代码:

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

第 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;

换行:

if (current_time - *saved_time >= 1000)

到:

if (current_time - *saved_time >= 100)

瞧! 只需更改一行代码。再加上两条注释行,以便更好地衡量。

如何编译grub 2.02

在遵循 Grub 的website 上的说明之前:

sudo apt install bison
sudo apt install flex

然后按照 grub 的网站说明进行操作:

cd grub-2.02
./configure

在 Grub 的网站上运行下一个命令:

make install

文件是在/usr/local/bin(惊喜!!!)以及预期的.../grub-2.02 目录中创建的。


编译 grub 的其他问题

我最终将源代码克隆到 VM (Lubuntu 16.04) 并在那里重新编译。使用新编译的grub-install 搞砸了,我不得不使用sudo apt install grub2 来重新安装。然后手动将新编译的文件复制到/boot/grub/i386-pc

我的终端盒都歪了,所以我必须创建一个新的 grub 背景图像。在下图中,我将 GRUB_TIMEOUT=35 更改为 3.5 秒倒计时。


2018 年 7 月 16 日更新

想出一个参数来获得 X86、EFI 支持:

./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)
*******************************************************

但是make install之后出现错误:

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

我向 Grub 人员提交了一份错误报告(2018 年 7 月),但没有收到任何回复。 EFI 系统的下一步是使用 Ubuntu 的存储库而不是 Grub 的网站说明下载全新安装的源代码。


【讨论】:

以上是关于在 Grub 2.02 源代码中查找倒数计时器的主要内容,如果未能解决你的问题,请参考以下文章

在 asp.net 中维护基于 ajax 的倒数计时器的状态

在按钮上显示倒数计时器

如何在 kivy 中创建 60 秒倒数计时器

带有 CircularProgressIndicator 的倒数计时器

让秒针在倒数计时器上旋转而不是滴答作响

如何通过在倒数计时器中按住按钮来添加值