本文从代码角度,分析uboot如何使用环境变量以及加载命令,启动kernel的。
本文以armV8 CPU为例子(例如RK1808或者7ev),其他架构类型基本一样。
1. uboot启动流程简介
为了让读者知道本文介绍的知识点在uboot所在位置,在介绍之前,先简单回顾下uboot的启动流程。
从uboot链接脚本(u-boot\arch\arm\cpu\armv8\u-boot.lds)可知,uboot的启动入口符号 _start:
.......
ENTRY(_start)
SECTIONS
{
#ifdef CONFIG_ARMV8_SECURE_BASE
/DISCARD/ : { *(.rela._secure*) }
#endif
. = 0x00000000;
. = ALIGN(8);
.text :
{
*(.__image_copy_start)
CPUDIR/start.o (.text*)
*(.text*)
}
........
而_start符号定义在 u-boot\arch\arm\cpu\armv8\start.S 文件里:
.globl _start
_start:
#ifdef CONFIG_ENABLE_ARM_SOC_BOOT0_HOOK
/*
* Various SoCs need something special and SoC-specific up front in
* order to boot, allow them to set that in their boot0.h file and then
* use it here.
*/
#include <asm/arch/boot0.h>
#else
b reset
#endif
start.S主要对CPU环境初始化,最后跳转到_main符号地址,_main定义在u-boot\arch\arm\lib\crt0_64.S:
ENTRY(_main)
分析该文件,做了一些列初始化,包括为C环境做的准备等,主要跳转了两个函数:board_init_f 和 board_init_r。
其中board_init_f定义在uboot/common/board_f.c里,该函数主要执行了init_sequence_f定义的函数结构体,至于具体做了什么,留着专题分析,本文重点不在这里。
void board_init_f(ulong boot_flags)
{
gd->flags = boot_flags;
gd->have_console = 0;
if (initcall_run_list(init_sequence_f))
hang();
#if !defined(CONFIG_ARM) && !defined(CONFIG_SANDBOX) && \
!defined(CONFIG_EFI_APP) && !CONFIG_IS_ENABLED(X86_64)
/* NOTREACHED - jump_to_copy() does not return */
hang();
#endif
}
crt0_64.S最后跳转至board_init_r函数,该函数定义在u-boot\common\board_r.c,该函数也是执行了init_sequence_r定义的函数结构体,而init_sequence_r(u-boot\common\board_r.c里定义)定义的函数集里,最后执行的函数是run_main_loop,查看run_main_loop(u-boot\common\board_r.c里定义)函数定义知道最后跳转到main_loop。
void board_init_r(gd_t *new_gd, ulong dest_addr)
{
/*
* Set up the new global data pointer. So far only x86 does this
* here.
* TODO(sjg@chromium.org): Consider doing this for all archs, or
* dropping the new_gd parameter.
*/
.............
if (initcall_run_list(init_sequence_r))
hang();
/* NOTREACHED - run_main_loop() does not return */
hang();
}
static int run_main_loop(void)
{
.....
for (;;)
main_loop();
return 0;
}
main_loop定义在u-boot\common\main.c,main_loop是uboot最后进入的一个函数,主要执行如下动作:初始化跳转kernel前环境,响应看客户按键,读取环境变量并执行命令跳转至kernel,跳转失败后续处理等。
本文主要分析uboot如何利用环境变量执行命令跳转至kernel,主要在main_loop函数里。
2. uboot使用环境变量跳转kernel
在main_loop里,可以看到uboot在执行了bootdelay_process(u-boot\common\autoboot.c)函数,读取包括bootdelay、bootcmd等环境变量值,其中bootcmd是启动kernel的命令:
const char *bootdelay_process(void)
{
char *s;
int bootdelay;
........
s = env_get("bootdelay");
bootdelay = s ? (int)simple_strtol(s, NULL, 10) : CONFIG_BOOTDELAY;
.......
debug("### main_loop entered: bootdelay=%d\n\n", bootdelay);
.......
s = env_get("bootcmd");
process_fdt_options(gd->fdt_blob);
stored_bootdelay = bootdelay;
return s;
}
在读取到bootcmd命令内容后,main_loop又执行了autoboot_command(u-boot\common\autoboot.c)函数,来执行bootcmd的命令内容。所以接下来看下bootcmd内容从来哪里来,内容是什么,以及过程中执行了哪些命令才能加载kernel并跳转的。
3. bootcmd的命令内容分析
bootcmd命令定义在u-boot\include\env_default.h文件,放到default_environment这个大的结构体,该结构体会在uboot启动的时候,根据内容被加载成可识别环境变量内容。从中可可看到bootcmd内容来自CONFIG_BOOTCOMMAND,说明用户可自定义bootcmd的内容。
env_t environment __UBOOT_ENV_SECTION__ = {
.......
{
#elif defined(DEFAULT_ENV_INSTANCE_STATIC)
static char default_environment[] = {
#else
const uchar default_environment[] = {

最低0.47元/天 解锁文章
857

被折叠的 条评论
为什么被折叠?



