u-boot启动流程分析(一)从上电到命令行
这里使用之前移植的配置文件和设备树
157使用的是myboard_defconfig配置文件,详情请看这篇文章
首先执行下面的命令生成对应的文件
make myboard_defconfig
make DEVICE_TREE=stm32mp157c-myboard all -j64
编译结束后生成过程文件以及最终镜像
由于当前(2022.12.5)对于u-boot的Makefile分析还不熟练,所以先采用比较取巧的方式来学习u-boot的启动流程
配置文件
首先打开.config文件,搜索lds,可以看到我们的157使用的链接文件是arch/arm/cpu/u-boot-spl.lds
,因为我们的157是arm架构的处理器
链接脚本
我们需要通过这个链接文件去找到我们的启动文件在哪里
_start函数
在这里可以看到,入口函数是_start
函数,这个函数不是定义在start.S里面,而是它定义在arch/arm/lib/vectors.S
这个文件里。我们打开这个文件,找到_start
函数
中断向量表
这里可以看到,它首先设置了中断向量表,这里是我们的ARM_VECTORS
,这个ARM_VECTORS
是这样子定义的,就在vectors.S里面,_start函数前面的地方
.macro ARM_VECTORS
#ifdef CONFIG_ARCH_K3
ldr pc, _reset
#else
b reset
#endif
ldr pc, _undefined_instruction
ldr pc, _software_interrupt
ldr pc, _prefetch_abort
ldr pc, _data_abort
ldr pc, _not_used
ldr pc, _irq
ldr pc, _fiq
.endm
可以看到复位异常会跳转到我们的reset
函数去执行,也就是我们摁下开发板复位键的一刻,在157的fsbl
执行完毕之后就会根据引脚配置的启动方式加载我们的u-boot
,然后跳转到reset
函数执行,而这个reset
函数就定义在start.S
文件里头
reset函数
看到reset
函数,它首先执行跳转,跳转到save_boot_params
处执行
然后发现这个save_boot_params
啥也没干就直接返回到了reset
函数继续执行
不过,这里我们的157重写了save_boot_params函数,不过内容也很简单
保存了r2的一个值,这里先不管这个变量的具体含义,不过以后肯定是需要了解157的fsbl阶段发生过什么的。
接着往下看
注释这里,注释也写的很清楚,禁用中断,设置处理器到SVC模式
然后是设置中断向量表的地址
这里的CONFIG_OMAP44XX是没有定义的,并且定义了CONFIG_SPL_BUILD,在我们的spl/u-boot.cfg
里面可以找到它的定义
然后这一整段就是设置中断向量表为我们_start函数的地址
接着往下看
这里会执行cpu_init_cp15函数,下面的CONFIG_SKIP_LOWLEVEL_INIT_ONLY编译选项没有找到定义
cpu_init_cp15
做了关闭I-Cache、关闭D-Cache和关闭MMU等一系列工作,然后就跳转回来继续执行了
我们接着往下看
reset
函数的最后一步就是跳转到_main
函数运行,这个函数定义在arch/arm/lib/crt0.S
里面
_main函数
我们把目光聚焦到这个_main
函数
这个函数首先设置栈指针,然后调用俩函数
首先是board_init_f_alloc_reserve
函数,它定义在common\init\board_init.c
文件中
这里我猜是为global_data
预留空间,然后在下面的一行更新了栈指针(我猜是这样)
然后用r9
寄存器保存了global_data
的地址,然后调用board_init_f_init_reserve
函数,这个函数和前面的函数定义的位置一样,这个函数负责初始化工作
接着往下看
这里没有找到CONFIG_SPL_EARLY_BSS
的定义,这里应该不会执行
然后是跳转到board_init_f
函数
这个函数定义在common\board_f.c
中
init_sequence_f函数列表
设置了gd的一些标志之后,它就去执行init_sequence_f
里面的函数了
这个列表有一点长,初步筛选之后如下
static const init_fnc_t init_sequence_f[] = {
setup_mon_len,
fdtdec_setup,
initf_malloc,
log_init,
initf_bootstage, /* uses its own timer, so does not need DM */
setup_spl_handoff,
initf_console_record,
arch_cpu_init, /* basic arch cpu dependent setup */
mach_cpu_init, /* SoC/machine dependent CPU setup */
initf_dm,
arch_cpu_init_dm,
board_early_init_f,
timer_init, /* initialize timer */
env_init, /* initialize environment */
init_baud_rate, /* initialze baudrate settings */
serial_init, /* serial communications setup */
console_init_f, /* stage 1 init of console */
display_options, /* say that we are here */
display_text_info, /* show debugging info if required */
checkcpu,
print_resetinfo,
print_cpuinfo, /* display cpu info (and speed) */
show_board_info,
INIT_FUNC_WATCHDOG_INIT
INIT_FUNC_WATCHDOG_RESET
announce_dram_init,
dram_init, /* configure available RAM banks */
INIT_FUNC_WATCHDOG_RESET
INIT_FUNC_WATCHDOG_RESET
INIT_FUNC_WATCHDOG_RESET
/*
* Now that we have DRAM mapped and working, we can
* relocate the code and continue running from DRAM.
*
* Reserve memory at end of RAM for (top down in that order):
* - area that won't get touched by U-Boot and Linux (optional)
* - kernel log buffer
* - protected RAM
* - LCD framebuffer
* - monitor code
* - board info struct
*/
setup_dest_addr,
reserve_round_4k,
arch_reserve_mmu,
reserve_video,
reserve_trace,
reserve_uboot,
reserve_malloc,
reserve_board,
setup_machine,
reserve_global_data,
reserve_fdt,
reserve_bootstage,
reserve_bloblist,
reserve_arch,
reserve_stacks,
dram_init_banksize,
show_dram_config,
INIT_FUNC_WATCHDOG_RESET
setup_bdinfo,
display_new_sp,
INIT_FUNC_WATCHDOG_RESET
reloc_fdt,
reloc_bootstage,
reloc_bloblist,
setup_reloc,
clear_bss,
NULL,
};
最后我们的ARM架构的u-boot不会执行jump_to_copy函数,也就是说会返回_main
函数继续执行
我们继续看到_main
函数,它在完成一系列初始化之后,会清除BSS段
在前面的init_sequence_f
函数列表里面有一个clear_bss
函数,不过它是定义成这样的
也就是CLEAR_BSS会覆盖这个函数,调用这个函数实际调用的就是CLEAR_BSS
最后调用board_init_r
函数
init_sequence_r函数列表
board_init_r
函数去调用init_sequnence_r
函数列表,最后进入到u-boot的命令行界面
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,
initr_noncached,
initr_of_live,
initr_dm,
board_init, /* Setup chipselects */
efi_memory_init,
initr_binman,
initr_dm_devices,
stdio_init_tables,
serial_initialize,
initr_announce,
initr_watchdog,
INIT_FUNC_WATCHDOG_RESET
INIT_FUNC_WATCHDOG_RESET
INIT_FUNC_WATCHDOG_RESET
power_init_board,
INIT_FUNC_WATCHDOG_RESET
initr_nand,
initr_mmc,
initr_env,
INIT_FUNC_WATCHDOG_RESET
initr_secondary_cpu,
INIT_FUNC_WATCHDOG_RESET
stdio_add_devices,
initr_jumptable,
console_init_r, /* fully init console as a device */
arch_misc_init, /* miscellaneous arch-dependent init */
INIT_FUNC_WATCHDOG_RESET
interrupt_init,
/* PPC has a udelay(20) here dating from 2002. Why? */
initr_ethaddr,
gpio_hog_probe_all,
board_late_init,
INIT_FUNC_WATCHDOG_RESET
initr_net,
run_main_loop,
};
实验 在启动时添加打印信息
有了前面的铺垫,我们修改board/st/stm32mp1/stm32mp1.c
里面的checkboard
函数,在显示Model
之后输出一句Welcome to MyBoard
,这个函数由init_sequence_f
里的show_board_info
函数里面调用的checkboard
函数,这个函数实际会调用到我们stm32mp1.c
里面的checkboard
函数
修改如下:
然后重新编译烧录到SD卡,启动效果如下所示
成功显示信息!!!
这里输出了两次U-Boot的版本信息,我猜是u-boot-spl和u-boot的缘故,u-boot-spl简单初始化之后,把u-boot.img加载进内存,然后跳转执行