grub/grub-core/kern/arm64/efi中的startup.s 是grub2的入口函数
FUNCTION(_start)
/*
* EFI_SYSTEM_TABLE and EFI_HANDLE are passed in x1/x0.
*/
ldr x2, efi_image_handle_val
str x0, [x2]
ldr x2, efi_system_table_val
str x1, [x2]
ldr x2, grub_main_val
br x2
grub_main_val:
.quad EXT_C(grub_main)
一般uefi->grub2的时候会把uefi中的efi_system_table_val传递过来,这样
grub2就可以通过uefi的efi_system_table_val来boot kernel
这个函数最后会调用grub_main_val,也就是grub_main。grub_main -> grub_load_normal_mode ();这样就又跳回uefi中boot kernel,如果失败就进入救援模式grub_rescue_run ()
我们先看正常的flow
static void
grub_load_normal_mode (void)
{
/* Load the module. */
grub_dl_load ("normal");
/* Print errors if any. */
grub_print_error ();
grub_errno = 0;
grub_command_execute ("normal", 0, 0);
}
grub_load_normal_mode 这个函数首先会通过grub_dl_load ("normal");load normal这个命令
然后调用grub_command_execute来执行,即grub2是命令驱动型,这点和uboot有点类似.
这个命令的注册在grub-core/normal中实现
GRUB_MOD_FINI(normal)
{
grub_context_fini ();
grub_script_fini ();
grub_menu_fini ();
grub_normal_auth_fini ();
grub_xputs = grub_xputs_saved;
grub_set_history (0);
grub_register_variable_hook ("pager", 0, 0);
grub_fs_autoload_hook = 0;
grub_unregister_command (cmd_clear);
}
通过GRUB_MOD_FINI(normal)来注册normal命令
grub_register_command ("normal", grub_cmd_normal, 0, N_("Enter normal mode."));
normal命令的回调函数是grub_cmd_normal。
static grub_err_t
grub_cmd_normal (struct grub_command *cmd __attribute__ ((unused)),
int argc, char *argv[])
{
if (argc == 0)
{
/* Guess the config filename. It is necessary to make CONFIG static,
so that it won't get broken by longjmp. */
char *config;
const char *prefix;
prefix = grub_env_get ("prefix");
if (prefix)
{
grub_size_t config_len;
config_len = grub_strlen (prefix) +
sizeof ("/grub.cfg-XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX");
config = grub_malloc (config_len);
if (! config)
goto quit;
grub_snprintf (config, config_len, "%s/grub.cfg", prefix);
if (grub_strncmp (prefix + 1, "tftp", sizeof ("tftp") - 1) == 0)
grub_net_search_configfile (config);
grub_enter_normal_mode (config);
grub_free (config);
}
else
grub_enter_normal_mode (0);
}
else
grub_enter_normal_mode (argv[0]);
quit:
return 0;
}
从这个函数中可以看到主要是parse grub.cfg 后,将grub.cfg传入grub_enter_normal_mode
void
grub_enter_normal_mode (const char *config)
{
grub_boot_time ("Entering normal mode");
nested_level++;
grub_normal_execute (config, 0, 0);
grub_boot_time ("Entering shell");
grub_cmdline_run (0, 1);
nested_level--;
if (grub_normal_exit_level)
grub_normal_exit_level--;
grub_boot_time ("Exiting normal mode");
}
grub_enter_normal_mode->grub_normal_execute来显示grub的启动菜单
void
grub_normal_execute (const char *config, int nested, int batch)
{
grub_show_menu (menu, nested, 0);
}
grub_show_menu->show_menu
show_menu (grub_menu_t menu, int nested, int autobooted)
{
while (1)
{
int boot_entry;
grub_menu_entry_t e;
int auto_boot;
boot_entry = run_menu (menu, nested, &auto_boot);
if (boot_entry < 0)
break;
e = grub_menu_get_entry (menu, boot_entry);
if (! e)
continue; /* Menu is empty. */
grub_cls ();
if (auto_boot)
grub_menu_execute_with_fallback (menu, e, autobooted,
&execution_callback, 0);
else
grub_menu_execute_entry (e, 0);
if (autobooted)
break;
}
return GRUB_ERR_NONE;
}
show_menu 首先通过run_menu显示menu,然后通过grub_menu_get_entry得到用户选择的开机启动项。最后通过grub_menu_execute_entry执行用户选择的启动项
在grub.cfg中一定要定义linux命令,这个命令是在grub中定义的linux 命令,执行这个
menuentry "test" --id test {
linux /1122/Image
}
linux命令后,grub就会通过ueif的boot service将kernel image load到dram中,然后在grub_menu_execute_entry 中执行boot 命令,就返回到uefi中,再从uefi->kernelstub->kernel
grub_menu_execute_entry(grub_menu_entry_t entry, int auto_boot)
{
if (grub_errno == GRUB_ERR_NONE && grub_loader_is_loaded ())
/* Implicit execution of boot, only if something is loaded. */
grub_command_execute ("boot", 0, 0);
}
FUNCTION(_start)
/*
* EFI_SYSTEM_TABLE and EFI_HANDLE are passed in x1/x0.
*/
ldr x2, efi_image_handle_val
str x0, [x2]
ldr x2, efi_system_table_val
str x1, [x2]
ldr x2, grub_main_val
br x2
grub_main_val:
.quad EXT_C(grub_main)
一般uefi->grub2的时候会把uefi中的efi_system_table_val传递过来,这样
grub2就可以通过uefi的efi_system_table_val来boot kernel
这个函数最后会调用grub_main_val,也就是grub_main。grub_main -> grub_load_normal_mode ();这样就又跳回uefi中boot kernel,如果失败就进入救援模式grub_rescue_run ()
我们先看正常的flow
static void
grub_load_normal_mode (void)
{
/* Load the module. */
grub_dl_load ("normal");
/* Print errors if any. */
grub_print_error ();
grub_errno = 0;
grub_command_execute ("normal", 0, 0);
}
grub_load_normal_mode 这个函数首先会通过grub_dl_load ("normal");load normal这个命令
然后调用grub_command_execute来执行,即grub2是命令驱动型,这点和uboot有点类似.
这个命令的注册在grub-core/normal中实现
GRUB_MOD_FINI(normal)
{
grub_context_fini ();
grub_script_fini ();
grub_menu_fini ();
grub_normal_auth_fini ();
grub_xputs = grub_xputs_saved;
grub_set_history (0);
grub_register_variable_hook ("pager", 0, 0);
grub_fs_autoload_hook = 0;
grub_unregister_command (cmd_clear);
}
通过GRUB_MOD_FINI(normal)来注册normal命令
grub_register_command ("normal", grub_cmd_normal, 0, N_("Enter normal mode."));
normal命令的回调函数是grub_cmd_normal。
static grub_err_t
grub_cmd_normal (struct grub_command *cmd __attribute__ ((unused)),
int argc, char *argv[])
{
if (argc == 0)
{
/* Guess the config filename. It is necessary to make CONFIG static,
so that it won't get broken by longjmp. */
char *config;
const char *prefix;
prefix = grub_env_get ("prefix");
if (prefix)
{
grub_size_t config_len;
config_len = grub_strlen (prefix) +
sizeof ("/grub.cfg-XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX");
config = grub_malloc (config_len);
if (! config)
goto quit;
grub_snprintf (config, config_len, "%s/grub.cfg", prefix);
if (grub_strncmp (prefix + 1, "tftp", sizeof ("tftp") - 1) == 0)
grub_net_search_configfile (config);
grub_enter_normal_mode (config);
grub_free (config);
}
else
grub_enter_normal_mode (0);
}
else
grub_enter_normal_mode (argv[0]);
quit:
return 0;
}
从这个函数中可以看到主要是parse grub.cfg 后,将grub.cfg传入grub_enter_normal_mode
void
grub_enter_normal_mode (const char *config)
{
grub_boot_time ("Entering normal mode");
nested_level++;
grub_normal_execute (config, 0, 0);
grub_boot_time ("Entering shell");
grub_cmdline_run (0, 1);
nested_level--;
if (grub_normal_exit_level)
grub_normal_exit_level--;
grub_boot_time ("Exiting normal mode");
}
grub_enter_normal_mode->grub_normal_execute来显示grub的启动菜单
void
grub_normal_execute (const char *config, int nested, int batch)
{
grub_show_menu (menu, nested, 0);
}
grub_show_menu->show_menu
show_menu (grub_menu_t menu, int nested, int autobooted)
{
while (1)
{
int boot_entry;
grub_menu_entry_t e;
int auto_boot;
boot_entry = run_menu (menu, nested, &auto_boot);
if (boot_entry < 0)
break;
e = grub_menu_get_entry (menu, boot_entry);
if (! e)
continue; /* Menu is empty. */
grub_cls ();
if (auto_boot)
grub_menu_execute_with_fallback (menu, e, autobooted,
&execution_callback, 0);
else
grub_menu_execute_entry (e, 0);
if (autobooted)
break;
}
return GRUB_ERR_NONE;
}
show_menu 首先通过run_menu显示menu,然后通过grub_menu_get_entry得到用户选择的开机启动项。最后通过grub_menu_execute_entry执行用户选择的启动项
在grub.cfg中一定要定义linux命令,这个命令是在grub中定义的linux 命令,执行这个
menuentry "test" --id test {
linux /1122/Image
}
linux命令后,grub就会通过ueif的boot service将kernel image load到dram中,然后在grub_menu_execute_entry 中执行boot 命令,就返回到uefi中,再从uefi->kernelstub->kernel
grub_menu_execute_entry(grub_menu_entry_t entry, int auto_boot)
{
if (grub_errno == GRUB_ERR_NONE && grub_loader_is_loaded ())
/* Implicit execution of boot, only if something is loaded. */
grub_command_execute ("boot", 0, 0);
}