U-Boot学习整理
近期工作涉及到U-Boot,为了全面认识U-Boot和方便后期回顾,现将自己所学总结记录成文档,其中参考博客已在结尾处注明。
1. 简介
U-Boot 是一个主要用于嵌入式系统的引导加载程序.
全称:Universal Boot Loader
类型:开放源码项目
特点:是一套在GNU通用公共许可证之下发布的自由软件, 遵循GNU条款。支持多种嵌入式操作系统内核。
2. 工作模式
3. 目录结构
目录结构 | 说明 |
---|---|
/arch | Architecture specific files |
/api | Machine/arch independent API for external apps |
/board | Board dependent files |
/cmd | U-Boot commands functions |
/common | Misc architecture independent functions |
/configs | Board default configuration files |
/disk | Code for disk drive partition handling |
/doc | Documentation (don’t expect too much) |
/drivers | Commonly used device drivers |
/dts | Contains Makefile for building internal U-Boot fdt. |
/examples | Example code for standalone applications, etc. |
/fs | Filesystem code (cramfs, ext2, jffs2, etc.) |
/include | Header Files |
/lib | Library routines generic to all architectures |
/Licenses | Various license files |
/net | Networking code |
/post | Power On Self Test |
/scripts | Various build scripts and Makefiles |
/test | Various unit test files |
/tools | Tools to build S-Record or U-Boot images, etc. |
其中/arch有多种处理器架构文件
/arch 【Files generic to ARC architecture】 |
---|
/arc 【Files generic to ARC architecture】 |
/arm 【Files generic to ARM architecture】 |
/m68k 【Files generic to m68k architecture】 |
/microblaze【Files generic to microblaze architecture】 |
/mips 【Files generic to MIPS architecture】 |
/nds32 【Files generic to NDS32 architecture】 |
/nios2 【Files generic to Altera NIOS2 architecture】 |
/openrisc 【Files generic to OpenRISC architecture】 |
/powerpc 【Files generic to PowerPC architecture】 |
/riscv 【Files generic to RISC-V architecture】 |
/sandbox 【Files generic to HW-independent “sandbox”】 |
/sh 【Files generic to SH architecture】 |
/x86: 【Files generic to x86 architectur】 |
4. 初始化流程
4.1 入口start.S
代码运行从依赖于特定CPU体系的start.S开始。
例如
arch/powerpc/cpu/mpc83xx/start.S
arch/mips/cpu/start.S
arch/arm/cpu/armv8/start.S (工作中遇到过)
等等。在start.S中会调用三个函数, lowlevel_init, board_init_f, board_init_r。
- lowlevel_init
:目的是执行到board_init_f, 不提供GD(‘global_data’), stack,BSS,不能设置SDRAM,不能使用串口。 - board_init_f
:目的是执行到board_init_r, 支持GD, RAM中有栈, 没有BSS, 所以不能使用全局/静态变量,只能使用栈变量和GD结构体. - board_init_r
: 运行主程序,进入到main_loop。global_data, SDRAM, BSS全部初始化成功,可以使用全局/静态变量。
start.S 主要做了什么?
- 获取当前异常等级EL(exception level),配置中断向量、MMU、Endian、i/d Cache等。配置ARM的勘误表,具体可参考apply_core_errata函数。
- 跳转指令跳转到弱函数lowlevel_init。(不同CPU可以定义同名函数进行覆盖)
- 判断CPU主从核,主核进行配置,包括中断分组和接口优先级。
- 主核跳转到_main,从核等待主核唤醒进行EL切换。
详细介绍请参考
start.S 跳转至arch/arm/lib/crt0_64.S _main
(crt0 - C-runtime startup Code for AArch64 U-Boot)
_main 做了什么?源代码注释中的流程
- 设置执行board_init_f的初始环境,此时只提供存放GD结构体的栈空间,GD初始化需要早于调用board_init_f。
- 调用board_init_f, 用于执行系统RAM做硬件准备。此时必须使用GD结构体存储后续启动阶段的数据。
- 设置board_init_f()在系统内存中分配的堆栈和GD中间环境,此时BSS 和非常量数据仍然不可用。
- non-SPL, 调用relocate_code()。
SPL, board_init_f() 不调用relocate (返回crt0)。- 设置调用board_init_r 环境。
- 调用board_init_r。
4.2 common/board_f.c & common/board_r.c
board_init_f 主要是调用initcall_run_list(init_sequence_f),执行init_sequence_f定义的初始化。
board_init_r主要是调用initcall_run_list(init_sequence_r),执行init_sequence_r定义的初始化。
其中init_sequence_r最后会调用run_main_loop。
static int run_main_loop(void)
{
……………………
/* main_loop() can return to retry autoboot, if so just run it again */
for (;;)
main_loop();
return 0;
}
common/main.c main_loop 流程
/* We come here after U-Boot is initialised and ready to process commands */
void main_loop(void)
{
const char *s;
bootstage_mark_name(BOOTSTAGE_ID_MAIN_LOOP, "main_loop");
if (IS_ENABLED(CONFIG_VERSION_VARIABLE))
env_set("ver", version_string); /* set version variable */
cli_init();
if (IS_ENABLED(CONFIG_USE_PREBOOT))
run_preboot_environment_command();
if (IS_ENABLED(CONFIG_UPDATE_TFTP))
update_tftp(0UL, NULL, NULL);
s = bootdelay_process();
if (cli_process_fdt(&s))
cli_secure_boot_cmd(s);
autoboot_command(s);
cli_loop();
}
U-Boot 2020.04
Kernel 4.14.180
通过env_get获取环境变量值,然后通过函数run_command()执行。例如:
i)run_preboot_environment_command中env_get(“preboot”),然后执行相应命令。
ii)bootdelay_process中env_get(“bootdelay”),env_get(“bootcmd”),返回bootcmd值,然后通过run_command()函数来执行,至此,uboot启动结束,进入加载启动内核阶段,通过do_bootm来启动内核。
bootcmd中根据具体需求和版型,定制启动参数和命令。