uboot的最终目的是引导启动内核加载系统,根据这个线索我们可以首先找到uboot引导内核的main函数,查看系统引导的执行跳转的函数 main_loop。
下面对uboot函数的调用关系和主要调用函数进行分析。
一、uboot函数调用关系梳理
函数调用如下:
main.c common
void main_loop(void)
{
cli_init();
run_preboot_environment_command();
s = bootdelay_process();
autoboot_command(s);
}
当用户没有输入时,通常会在bootdealy延时时间到后通过autoboot_command(s)函数,自动执行uboot配置的bootcmd命令,引导启动内核。
这个main_loop(void)函数会循环执行,通过索引:run_main_loop调用main_loop()函数如下。
board_r.c (common)
static int run_main_loop(void)
{
#ifdef CONFIG_SANDBOX
sandbox_main_loop_init();
#endif
/* main_loop() can return to retry autoboot, if so just run it again */
for (;;)
main_loop();
return 0;
}
在执行 run_main_loop前,uboot的所有初始化工作都在 init_sequence_r[] 初始化函数数组中逐一列出。
static init_fnc_t init_sequence_r[] = {
initr_trace,
initr_reloc,
initr_caches,
initr_reloc_global_data,
initr_barrier,
initr_malloc,
log_init,
initr_bootstage, /* Needs malloc() but has its own timer */
initr_console_record,
board_init, /* Setup chipselects */
set_cpu_clk_info, /* Setup clock information */
efi_memory_init,
stdio_init_tables,
initr_serial,
initr_announce,
board_early_init_r,
initr_env,
initr_secondary_cpu,
initr_pci,
stdio_add_devices,
initr_jumptable,
console_init_r, /* fully init console as a device */
console_announce_r,
show_board_info,
misc_init_r, /* miscellaneous platform-dependent init */
interrupt_init,
initr_enable_interrupts,
initr_ethaddr,
board_late_init,
initr_scsi,
initr_net,
run_main_loop,
}
void board_init_r(gd_t *new_gd, ulong dest_addr)
{
if (initcall_run_list(init_sequence_r))
/* NOTREACHED - run_main_loop() does not return */
hang();
}
uboot会根据对应的函数指针逐一执行初始化工作。
static inline int initcall_run_list(const init_fnc_t init_sequence[])
{
const init_fnc_t *init_fnc_ptr;
for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr)
ret = (*init_fnc_ptr)();
}
在调用board_init_r之前,通常MCU会做一些与CPU密切相关的初始化工作,即Uboot第一阶段,通常由汇编语言完成。
64位的调用文件:
crt0_64.S (arch\arm\lib) line 152 : b board_init_r /* PC relative jump */
b board_init_r /* PC relative jump */
/* NOTREACHED - board_init_r() does not return */
efi_main in efi_app.c (lib\efi) : board_init_r(NULL, 0);
ENTRY(_main)
{
}
arm64为调用位置:
start.S arch\arm\cpu\armv8 8533 2023/3/16
.globl _start
_start:
bl lowlevel_init
bl _main
efi_status_t EFIAPI efi_main(efi_handle_t image,
struct efi_system_table *sys_table)
{
board_init_r(NULL, 0);
}
//uboot已支持uefi
crt0_aarch64_efi.S arch\arm\lib 3817 2023/3/16
_start:
bl efi_main
二、主要调用函数分析
uboot命令执行时的调用关系
static int run_main_loop(void)
{
#ifdef CONFIG_SANDBOX
sandbox_main_loop_init();
#endif
/* main_loop() can return to retry autoboot, if so just run it again */
for (;;)
//当程序没有跳转运行kernel时(即按下空格按键时),系统将循环执行接收用户输入的命令。
main_loop();
return 0;
}
void main_loop(void)
{
const char *s;
bootstage_mark_name(BOOTSTAGE_ID_MAIN_LOOP, "ma