嵌入式linux驱动之路16:U-Boot 启动流程(1)

通过对 uboot 启动流程的梳理,我们就可以掌握一些外设是在哪里被初始化的,这样当我们需要修改这些外设驱动的时候就会心里有数。另外,通过分析 uboot 的启动流程可以了解 Linux 内核是如何被启动的。

二级标题链接脚本 u-boot.lds

要分析 uboot 的启动流程,首先要找到“入口”,找到第一行程序在哪里。程序的链接是由链接脚本来决定的,所以通过链接脚本可以找到程序的入口。
为代码当前入口点:_start,_start 后面就是中断向量表,此代码存放在.vectors 段里面。
打开 u-boot.lds,内容如下:
嵌入式linux驱动之路16:U-Boot 启动流程(1)_第1张图片
第 12 行将 arch/arm/cpu/armv7/start.s 编译出来的代码放到中断向量表后面。
第 13 行为 text 段,其他的代码段就放到这里。
_start 文件 如图 :
嵌入式linux驱动之路16:U-Boot 启动流程(1)_第2张图片
打开 u-boot.map,如图:
嵌入式linux驱动之路16:U-Boot 启动流程(1)_第3张图片
从图可以看出,vectors 段的起始地址也是 0X87800000,说明整个 uboot 的起始地址就是 0X87800000

U-Boot 启动流程详解

reset 函数

reset 函数经过几次跳转跳转到 save_boot_params_ret 函数,读取寄存器 cpsr 中的值并修改,设置处理器进入 SVC 模式并关闭 FIQ 和 IRQ,然后读取 CP15 中 c1 寄存器的值到 r0 寄存器中即读取SCTLR 寄存器的值,清除 SCTLR 寄存器中的 bit13,bit13 为 向量表控制位,当为 0 的时候向量表基地址为 0X00000000,软件可以重定位向量表。为 1 的时候向量表基地址为 0XFFFF0000,软件不能重定位向量表。这里清零,目的就是为了接下来的向量表重定位。然后设置r0寄存器的值为_start,_start就是整个uboot的入口地址,将 r0 寄存器的值(向量表值)写入到 CP15 的 c12 寄存器中,也就是 VBAR 寄存器。接着调用函数 cpu_init_cp15 用来设置 CP15 相关的内容,比如关闭 MMU 啥的,调用函数 cpu_init_crit 内部调用了函数 lowlevel_init(设置SP指针)lowlevel_init函数后面又调用了s_init函数,但什么也没有做;调用流程为:
在这里插入图片描述
最后调用主函数,调用函数 board_init_f_alloc_reserve 主要是留出早期的 malloc 内存区域和 gd 内存区域,使得SP指针指向地址减少一些,然后是设置 gd 所指向的位置,与SP指针指向位置相等;然后调用函数board_init_f_init_reserve,初始化 gd,其实就是清零处理,还设置了gd->malloc_base 为 gd 基地址+gd 大小,这个也就是 early malloc 的起始地址,如图:
嵌入式linux驱动之路16:U-Boot 启动流程(1)_第4张图片
然后调用 board_init_f 函数主要用来初始 化 DDR,定时器,完成代码拷贝等等
在_main 函数里面调用了 board_init_f、relocate_code、
relocate_vectors 和 board_init_r 这 4 个函数
1.board_init_f 函数
①、初始化一系列外设,比如串口、定时器,或者打印一些消息等。
②、初始化 gd 的各个成员变量,uboot 会将自己重定位到 DRAM 最后面的地址区域,也就是将自己拷贝到 DRAM 最后面的内存区域中。这么做的目的是给 Linux 腾出空间,防止 Linuxkernel 覆盖掉 uboot,将 DRAM 前面的区域完整的空出来。在拷贝之前肯定要给 uboot 各部分分配好内存位置和大小,比如 gd 应该存放到哪个位置,malloc 内存池应该存放到哪个位置等等。这些信息都保存在 gd 的成员变量中,因此要对 gd 的这些成员变量做初始化。最终形成一
个完整的内存“分配图
嵌入式linux驱动之路16:U-Boot 启动流程(1)_第5张图片
2. relocate_code 函数
uboot 将自身拷贝到 DRAM 的另一个地放去继续运行(DRAM 的高地址处)。函数对变量的访问没有直接进行,而是使用了一个第三方偏移地址,专业术语叫做 Label。相当于在代码段有一行【 label(地址):要访问的变量地址】知道Label所保存的地址那就可以访问这个数据,所以重定位之后修改Label所保存的值就能正确地访问了, 将重定位后代码段中的Label所保存的地址值更新为对应的新地址。
嵌入式linux驱动之路16:U-Boot 启动流程(1)_第6张图片
3.relocate_vectors 函数
将是重定位后 uboot 的首地址写入到 CP15 的 VBAR 寄存器中,也就是将新的向量表首地址写入到寄存器 VBAR 中,设置向量表偏移。
4.board_init_r 函数
board_init_f 函数,在此函数里面会调用一系列的函数来初始化一些外设和 gd 的成员变量。但是 board_init_f 并没有初始化所有的外设,还需要做一些后续工作,这些后续工作就是由函数 board_init_r 来完成的。
5.run_main_loop 函数
uboot 启动以后会进入 3 秒倒计时,如果在 3 秒倒计时结束之前按下按下回车键,那么就会进入 uboot 的命令模式,如果倒计时结束以后都没有按下回车键,那么就会自动启动 Linux 内核 , 这 个 功 能 就 是 由 run_main_loop 函 数 来 完 成 的 。main_loop函数先获取环境变量bootdelay和bootcmd,bootcmd 里面保存着默认的启动命令,因此 linux 内核启动!如果u-boot配置了CONFIG_OF_CONTROL,则在cli_process_fdt函数里获取设备树的bootcmd和bootsecure属性,如果返回的bootsecure属性不为0,则进入cli_secure_boot_cmd函数解析并执行设备树中的bootcmd属性内容;否则进入到autoboot_command函数里倒计时,倒计时结束后就会解析并执行环境变量bootcmd的内容;如果倒计时过程中检测到串口有输入,就会退出该函数,进入cli_loop函数里面解析命令行的输入。

你可能感兴趣的:(linux,arm,驱动开发)