boot命令的调用flow如下:
grub_enter_normal_mode ->grub_normal_execute->grub_show_menu->show_menu->grub_menu_execute_entry
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);
}
如果前面执行没有错误,且kennel已经被装载到dram中,这个是通过grub_loader_is_loaded来判断的
grub_loader_is_loaded (void)
{
return grub_loader_loaded;
}
既判断是否为1,而是在执行linux命令的时候调用grub_loader_set来设定的
grub_loader_set (grub_err_t (*boot) (void),
grub_err_t (*unload) (void),
int flags)
{
grub_loader_loaded = 1;
}
如果都ok就调用grub_command_execute来执行boot命令
GRUB_MOD_INIT(boot)
{
cmd_boot =
grub_register_command ("boot", grub_cmd_boot,
0, N_("Boot an operating system."));
}
可见boot的回调函数是grub_cmd_boot
grub_cmd_boot (struct grub_command *cmd __attribute__ ((unused)),
int argc __attribute__ ((unused)),
char *argv[] __attribute__ ((unused)))
{
return grub_loader_boot ();
}
grub_loader_boot (void)
{
err = (grub_loader_boot_func) ();
for (cur = preboots_tail; cur; cur = cur->prev)
if (! err)
err = cur->preboot_rest_func ();
else
cur->preboot_rest_func ();
return err;
}
grub_loader_boot 最重要是调用grub_loader_boot_func来boot kernel,如果failed的话,就会执行preboots_tail
list中注册的函数,但是正常的flow是调用grub_loader_boot_func后就不会再返回了
grub_loader_boot_func函数也是在linux 命令中通过grub_loader_set来指定的
grub_loader_boot_func = grub_linux_boot
static grub_err_t
grub_linux_boot (void)
{
if (finalize_params_linux () != GRUB_ERR_NONE)
return grub_errno;
return (grub_arm64_uefi_boot_image((grub_addr_t)kernel_addr,
kernel_size, linux_args));
}
直接调用grub_arm64_uefi_boot_image起形参就是kernel在dram中的地址和size,以及要传给kernel的cmdline
grub_arm64_uefi_boot_image (grub_addr_t addr, grub_size_t size, char *args)
{
grub_efi_memory_mapped_device_path_t *mempath;
grub_efi_handle_t image_handle;
grub_efi_boot_services_t *b;
grub_efi_status_t status;
grub_efi_loaded_image_t *loaded_image;
int len;
b = grub_efi_system_table->boot_services;
status = b->load_image (0, grub_efi_image_handle,
(grub_efi_device_path_t *) mempath,
(void *) addr, size, &image_handle);
grub_dprintf ("linux", "linux command line: '%s'\n", args);
/* Convert command line to UCS-2 */
loaded_image = grub_efi_get_loaded_image (image_handle);
loaded_image->load_options_size = len =
(grub_strlen (args) + 1) * sizeof (grub_efi_char16_t);
loaded_image->load_options =
grub_efi_allocate_pages (0,
GRUB_EFI_BYTES_TO_PAGES (loaded_image->load_options_size));
if (!loaded_image->load_options)
return grub_errno;
loaded_image->load_options_size =
2 * grub_utf8_to_utf16 (loaded_image->load_options, len,
(grub_uint8_t *) args, len, NULL);
grub_dprintf ("linux", "starting image %p\n", image_handle);
status = b->start_image (image_handle, 0, NULL);
}
这个函数首先通过uefi的system_table找到boot_services
然后通过load_image来讲kernel image装载到dram中,在linux命令中其实病没有
真正将kernel image从flash等中copy到dram,而这里是真正调用bios的 boot_services
来做这件事情。
最后还是调用boot_services的start_image返回到bios中继续执行启动kernel的工作.
grub_enter_normal_mode ->grub_normal_execute->grub_show_menu->show_menu->grub_menu_execute_entry
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);
}
如果前面执行没有错误,且kennel已经被装载到dram中,这个是通过grub_loader_is_loaded来判断的
grub_loader_is_loaded (void)
{
return grub_loader_loaded;
}
既判断是否为1,而是在执行linux命令的时候调用grub_loader_set来设定的
grub_loader_set (grub_err_t (*boot) (void),
grub_err_t (*unload) (void),
int flags)
{
grub_loader_loaded = 1;
}
如果都ok就调用grub_command_execute来执行boot命令
GRUB_MOD_INIT(boot)
{
cmd_boot =
grub_register_command ("boot", grub_cmd_boot,
0, N_("Boot an operating system."));
}
可见boot的回调函数是grub_cmd_boot
grub_cmd_boot (struct grub_command *cmd __attribute__ ((unused)),
int argc __attribute__ ((unused)),
char *argv[] __attribute__ ((unused)))
{
return grub_loader_boot ();
}
grub_loader_boot (void)
{
err = (grub_loader_boot_func) ();
for (cur = preboots_tail; cur; cur = cur->prev)
if (! err)
err = cur->preboot_rest_func ();
else
cur->preboot_rest_func ();
return err;
}
grub_loader_boot 最重要是调用grub_loader_boot_func来boot kernel,如果failed的话,就会执行preboots_tail
list中注册的函数,但是正常的flow是调用grub_loader_boot_func后就不会再返回了
grub_loader_boot_func函数也是在linux 命令中通过grub_loader_set来指定的
grub_loader_boot_func = grub_linux_boot
static grub_err_t
grub_linux_boot (void)
{
if (finalize_params_linux () != GRUB_ERR_NONE)
return grub_errno;
return (grub_arm64_uefi_boot_image((grub_addr_t)kernel_addr,
kernel_size, linux_args));
}
直接调用grub_arm64_uefi_boot_image起形参就是kernel在dram中的地址和size,以及要传给kernel的cmdline
grub_arm64_uefi_boot_image (grub_addr_t addr, grub_size_t size, char *args)
{
grub_efi_memory_mapped_device_path_t *mempath;
grub_efi_handle_t image_handle;
grub_efi_boot_services_t *b;
grub_efi_status_t status;
grub_efi_loaded_image_t *loaded_image;
int len;
b = grub_efi_system_table->boot_services;
status = b->load_image (0, grub_efi_image_handle,
(grub_efi_device_path_t *) mempath,
(void *) addr, size, &image_handle);
grub_dprintf ("linux", "linux command line: '%s'\n", args);
/* Convert command line to UCS-2 */
loaded_image = grub_efi_get_loaded_image (image_handle);
loaded_image->load_options_size = len =
(grub_strlen (args) + 1) * sizeof (grub_efi_char16_t);
loaded_image->load_options =
grub_efi_allocate_pages (0,
GRUB_EFI_BYTES_TO_PAGES (loaded_image->load_options_size));
if (!loaded_image->load_options)
return grub_errno;
loaded_image->load_options_size =
2 * grub_utf8_to_utf16 (loaded_image->load_options, len,
(grub_uint8_t *) args, len, NULL);
grub_dprintf ("linux", "starting image %p\n", image_handle);
status = b->start_image (image_handle, 0, NULL);
}
这个函数首先通过uefi的system_table找到boot_services
然后通过load_image来讲kernel image装载到dram中,在linux命令中其实病没有
真正将kernel image从flash等中copy到dram,而这里是真正调用bios的 boot_services
来做这件事情。
最后还是调用boot_services的start_image返回到bios中继续执行启动kernel的工作.