菜单

kaoqi
kaoqi
发布于 2026-08-20 / 12 阅读
0
0

GRUB2主函数系统调用流程

之前介绍过一些有关GRUB2的文章:

http://kaoqi.xyz/archives/grub2zhu-han-shu-liu-cheng

http://kaoqi.xyz/archives/grub2de-mod_initji-zhi

但还是缺乏一个完整介绍GRUB2启动初始化流程的教程,在这篇文章里补全。

GRUB2的efi主函数在/grub-core/kern/main.c中的grub_main函数:

/* The main routine.  */
void __attribute__ ((noreturn))
grub_main (void)
{
#ifdef GRUB_STACK_PROTECTOR
  /*
   * This call should only be made from a function that does not return because
   * functions that return will get instrumented to check that the stack cookie
   * does not change and this call will change the stack cookie. Thus a stack
   * guard failure will be triggered.
   */
  grub_update_stack_guard ();
#endif

  /* First of all, initialize the machine.  */
  grub_machine_init ();

  grub_boot_time ("After machine init.");

  /* This breaks flicker-free boot on EFI systems, so disable it there. */
#ifndef GRUB_MACHINE_EFI
  /* Hello.  */
  grub_setcolorstate (GRUB_TERM_COLOR_HIGHLIGHT);
  grub_printf ("Welcome to GRUB!\n\n");
  grub_setcolorstate (GRUB_TERM_COLOR_STANDARD);
#endif

  /* Init verifiers API. */
  grub_verifiers_init ();

  grub_load_config ();

  grub_boot_time ("Before loading embedded modules.");

  /* Load pre-loaded modules and free the space.  */
  grub_register_exported_symbols ();
#ifdef GRUB_LINKER_HAVE_INIT
  grub_arch_dl_init_linker ();
#endif
  grub_load_modules ();

  grub_boot_time ("After loading embedded modules.");

  /* Check if the CLI should be disabled */
  check_is_cli_disabled ();

  /* It is better to set the root device as soon as possible,
     for convenience.  */
  grub_set_prefix_and_root ();
  grub_env_export ("root");
  grub_env_export ("prefix");

  /* Reclaim space used for modules.  */
  reclaim_module_space ();

  grub_boot_time ("After reclaiming module space.");

  grub_register_core_commands ();

  grub_boot_time ("Before execution of embedded config.");

  if (load_config)
    grub_parser_execute (load_config);

  grub_boot_time ("After execution of embedded config. Attempt to go to normal mode");

  grub_load_normal_mode ();
  grub_rescue_run ();
}

这其实是一个完整的流程,包含了最开始的设备初始化、启动、进入grub命令行、加载grub描述的模块等。

grub_main函数的主要流程:

1、进行设备初始化——grub_machine_init函数

2、进行文件校验——grub_verifiers_init

3、进行config读取——grub_load_config

4、进行模块加载——grub_load_modules

5、进行环境变量设置——grub_set_prefix_and_root

6、重定位模块空间——reclaim_module_space

7、初始化core命令——grub_register_core_commands

8、加载normal模块——grub_load_normal_mode

9、如果normal模块失败,则进入救援模式——grub_rescue_run

本文重点讨论的是其中grub_machine_init函数下,调用系统表,把输入输出调试正确的那一部分。

init函数

grub_machine_init函数的具体实现在/grub-core/kern/arm64/efi/init.c文件中。

然后,跳转到了grub_efi_init函数中。可以确定,machine初始化要干的第一件事情是把vedio的conout输出给他调对咯。

所以跳转到/grub-core/kern/efi/init.c文件中实现具体的grub_efi_init

void
grub_efi_init (void)
{
  grub_modbase = grub_efi_section_addr ("mods");
  /* First of all, initialize the console so that GRUB can display
     messages.  */
  grub_console_init ();

  /* Initialize the memory management system.  */
  grub_efi_mm_init ();

  /*
   * Lockdown the GRUB and register the shim_lock verifier
   * if the UEFI Secure Boot is enabled.
   */
  if (grub_efi_get_secureboot () == GRUB_EFI_SECUREBOOT_MODE_ENABLED)
    {
      grub_lockdown ();
      grub_shim_lock_verifier_setup ();
    }

  grub_efi_system_table->boot_services->set_watchdog_timer (0, 0, 0, NULL);

  grub_efidisk_init ();

  grub_efi_register_debug_commands ();
}

可以看到,最先开始的是加载mods,然后就是进行consolo的初始化。

在主函数中,grub_console_init这样的函数并不会在主函数文件中直接写含义。这个函数实际上属于/grub-core/term模块中的一部分,在init函数中,首先由grub_efi_section_addr函数完成基本的模块加载后,再调用这个consolo模块中的init函数的。

他妈的,再次被Grub2复杂的项目结构搞晕。他妈的Grub2的很多模块都是统一由image进行加载的,有就有没有就没有,而且最终实现GOP的过程也非常的蜿蜒曲折,真难看。看个con_out->outputstring的调用给我闹麻了。

GRUB2的主函数依然只负责console的内容,可以通过grub.cfg的配置实现图形界面的功能调用。

在GRUB2中,实现图形界面输出的具体流程为:

1、由main----》grub config load----》grub normal execute,完成gfxterm的模块加载。

2、在gfxterm的模块加载时,会加载图形终端模块,该模块的GRUB MOD INIT会初始化视频驱动GOP或VBE,分配帧缓冲区内存,加载字体文件。

3、terminal output gfxterm调用grub term register output将gfxterm注册为当前输出终端,并切换全局指针grub term cur output指向gfxterm

4、在GRUB2的源文件中,/grub-core/term/gfxterm.c文件,调用grub printf函数或菜单渲染代码,将数据流向gfxterm,然后再使用视频驱动grub video API将字符渲染为位图,并写入帧缓冲区。此时,屏幕上才真正出现图形化的菜单或背景图片。


评论