笔者把Uboot启动流程分为了七个阶段:
第一阶段:CPU的关键初始化(中断、权限、MMU等)
第二阶段:初始化关键外设+打印信息+设置global_data信息
第三阶段:内存分配(用于uboot重定位)
第四阶段:重定位
第五阶段:继续初始化其他外设
第六阶段:开始主循环
第七阶段:bootz启动流程
6.6 第六阶段:开始主循环
uboot启动以后会进入3秒倒计时,如果在3秒倒计时结束之前按下回车键,那么就会进入uboot的命令模式,如果倒计时结束以后都没有按下回车键,那么就会自动启动Linux内核,这个功能就是由run_main_loop函数来完成的。
run_main_loop函数定义在common/board_r.c中
run_main_loop函数的流程如下:
1、在一个死循环中调用main_loop
main_loop函数的流程如下:
1、调用 bootstage_mark_name 函数,用于打印出启动进度
2、执行函数 setenv,设置版本号环境变量 ver 的值为 version_string
3、调用cli_init函数,该函数跟命令初始化有关,用于初始化hush shell相关的变量
4、调用bootdelay_process函数,此函数会读取环境变量bootdelay和bootcmd的内容, 然后将bootdelay的值赋值给全局变量stored_bootdelay,返回值为环境变量bootcmd的值。
5、autoboot_command函数,用于判断倒计时结束前有没有被打断,无则调用默认bootcmd,有则返回调用函数
-
abortboot函数用于处理倒计时
-
如果倒计时自然结束,就执行函数run_command_list,此函数会执行参数 s 指定的一系列命令,也就是环境变量 bootcmd 的命令,bootcmd 里面保存着默认的启动命令,因此 linux 内核启动!
- 这个就是 uboot 中倒计时结束以后自动启动 linux 内核的原理。
-
如果倒计时结束之前按下了键盘上的按键,那么 run_command_list函数就不会执行,相当于 autoboot_command 是个空函数。就会回到main_loop函数执行 cli_loop 函数。该函数是命令处理函数,负责接收和处理输入的命令。
cli_loop 函数的流程如下:
cli_loop函数是uboot的命令行处理函数,负责接收和处理输入的命令
cli_loop->parse_file_outer->setup_file_in_str
->parse_stream_outer->parse_stream
->run_list->cmd_process
uboot 中命令是如何定义的
- boot使用宏U_BOOT_CMD 来定义命令,宏U_BOOT_CMD定义在文件include/command.h中。
- 定义这个宏的目的是为了定义一个 cmd_tbl_t 类型的变量,并初始化这个变量的各个成员。
- uboot 中的每个命令都存储在.u_boot_list 段中,每个命令都有一个名为 do_xxx(xxx 为具体的命令名)的函数,这个 do_xxx 函数就是具体的命令处理函数。命令处理函数就是cmd_tbl_t的cmd成员。
- uboot 中的命令表其实就是 cmd_tbl_t 结构体数组
cmd_process 函数的流程如下:
uboot 中的命令表其实就是 cmd_tbl_t 结构体数组
cmd_process->find_cmd->ll_entry_start 通过该函数得到得到数组第一个元素,也即uboot中命令表的起始地址
->ll_entry_count 通过该函数得到得到数组长度,也即uboot中命令表的长度
->find_cmd_tbl 通过该函数在命令表中找到所需的命令。找到则返回这个命令
->cmd_call->(cmdtp->cmd) cmd成员就是具体的命令处理函数,返回值为命令的执行结果
6.7 第七阶段:bootz启动流程
本节要解决三个问题
bootz是如何启动Linux内核?
uboot的生命是怎么终止的呢?
linux又是怎么启动的呢?
image全局变量
-
不管是bootz还是bootm命令,在启动Linux内核的时候都会用到一个重要的全局变量: images,images在文件cmd/bootm.c定义
bootm_headers_t images; -
bootm_headers_t类型定义在include/image.h中
typedef struct bootm_headers { ...... /* os 成员变量是 image_info_t 类型的,为系统镜像信息 */ image_info_t os; /* OS 镜像信息 */ ...... /* 下面这 11 个宏定义表示 BOOT 的不同阶段 */ #define BOOTM_STATE_START (0x00000001) #define BOOTM_STATE_FINDOS (0x00000002) #define BOOTM_STATE_FINDOTHER (0x00000004) #define BOOTM_STATE_LOADOS (0x00000008) #define BOOTM_STATE_RAMDISK (0x00000010) #define BOOTM_STATE_FDT (0x00000020) #define BOOTM_STATE_OS_CMDLINE (0x00000040) #define BOOTM_STATE_OS_BD_T (0x00000080) #define BOOTM_STATE_OS_PREP (0x00000100) #define BOOTM_STATE_OS_FAKE_GO (0x00000200)/*'Almost' run the OS*/ #define BOOTM_STATE_OS_GO (0x00000400) ...... } -
类型image_info_t,也就是系统镜像信息结构体,定义在include/image.h中
do_bootz函数
- bootz 命令的执行函数为 do_bootz,定义在文件 cmd/bootm.c 中
do_bootz函数的流程如下:
1、调用cmd/bootm.c中的函数bootz_start(cmdtp, flag, argc, argv, &images)
- 调用函数do_bootm_states,执行BOOTM_STATE_START阶段:bootm_start函数
- bootm_start函数会对images变量进行清零、设置images.state = BOOTM_STATE_START
- 通过images的ep成员变量,设置系统镜像的入口点,
images->ep = load_addr;。- 使用 bootz 命令启动系统的时候就会设置系统在 DRAM 中的存储位置,这个存储位置就是系统镜像的入口点
- 调用 bootz_setup 函数,用于获取zImage信息,判断zImage是否正确
- 调用函数bootm_find_images查找设备树文件,找到以后就将设备树的起始地址和长度分别写到images 的 ft_addr 和 ft_len 成员变量中
2、调用函数 bootm_disable_interrupts,关闭中断
3、设置 images.os.os 为 IH_OS_LINUX,也就是设置系统镜像为 Linux,表示我们要启动的是 Linux 系统!后面会用到 images.os.os 来挑选具体的启动函数。
4、调用函数 do_bootm_states 来执行不同的 BOOT 阶段,这里要执行的 BOOT 阶段有:BOOTM_STATE_OS_PREP 、BOOTM_STATE_OS_FAKE_GO 和BOOTM_STATE_OS_GO
-
调用bootm_os_get_boot_func,用于根据images->os.os找到系统对应的启动函数。此时就是boot_fn=do_bootm_linux
-
接着,根据阶段的不同,会执行不同的逻辑
-
对于BOOTM_STATE_OS_PREP阶段:调用boot_selected_os->boot_fn()->boot_prep_linux:该函数完成启动Linux之前的一些工作,对于使用设备树来说,他会将bootargs传递给Linux内核。也就是向Linux内核传参。
-
对于BOOTM_STATE_OS_GO和BOOTM_STATE_OS_FAKE_GO阶段:调用boot_selected_os->boot_fn()->boot_jump_linux
-
kernel_entry(0, machid, r2);传入三个参数:第一个参数zero同样为0;第二个参数为机器ID;第三个参数ATAGS或者设备树(DTB)首地址。至此进入到内核函数,uboot执行完毕
- Linux内核镜像文件的第一行代码就是函数kernel_entry,而images->ep保存着Linux 内核镜像的起始地址,起始地址保存的正是Linux内核第一行代码
-
-
2134

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



