09_Uboot启动流程_1

目录

链接脚本u-boot.lds详解

U-Boot启动流程详解

 reset函数源码详解

lowlevel_init函数详解

s_init函数详解


链接脚本u-boot.lds详解

要分析uboot的启动流程,首先要找到“入口”,找到第一行程序在哪里。程序的链接是由链接脚本来决定的,所以通过链接脚本可以找到程序的入口。如果没有编译过uboot的话链接脚本为arch/arm/cpu/u-boot.lds。但是这个不是最终使用的链接脚本,最终的链接脚本是在这个链接脚本的基础上生成的。编译一下 uboot,编译完成以后就会在 uboot 根目录下生成 u-boot.lds文件,如图所示: 

 打开u-boot.lds内容如下:

 第3行为代码当前入口点:_start, _start在文件arch/arm/lib/vectors.S中有定义,如图所示:

 

从图中的代码可以看出, start后面就是中断向量表,从图中的".section ".vectors","ax"可以得到,此代码存放在.vectors段里面。

使用如下命令在uboot中查找“_image_copy_start”:

grep -nR"_image_copy_start"

 打开u-boot.map,找到如图所示位置:

 u-boot.map是uboot的映射文件,可以从此文件看到某个文件或者函数链接到了哪个地址,从图932行可以看到_image_copy_start为0X87800000,而.text的起始地址也是0X87800000

继续回到u-boot.lds示例代码中,第11行是vectors段,vectors段保存中断向量表,从图_start文件示例代码中我们知道了vectors.S的代码是存在vectors段中的。从图u-boot.map文件示例代码可以看出,vectors段的起始地址也是0X87800000,说明整个uboot的起始地址就是0X87800000。

第12行将arch/arm/cpu/armv7/start.s编译出来的代码放到中断向量表后面。

第13行为text段,其他的代码段就放到这里

在u-boot.lds中有一些跟地址有关的“变量”需要我们注意一下,后面分析u-boot源码的时候会用到,这些变量要最终编译完成才能确定的!!!比如我编译完成以后这些“变量”的值如表所示:

 表中的“变量”值可以在u-boot.map文件中查找,表中除了_image_copy_start,以外,其他的变量值每次编译的时候可能会变化,如果修改了uboot代码、修改了uboot配置、选用不同的优化等级等等都会影响到这些值。所以,一切以实际值为准!

 

U-Boot启动流程详解

 reset函数源码详解

u-boot.lds中我们已经知道了入口点是arch/arm/lib/vectors.S文件中的_start,代码如下: 

第35行就是reset 函数。

第37行从reset函数跳转到save_bootparams函数,而save_boot_params函数同样定义在start.S里面,定义如下:

第43行,读取寄存器cpsr中的值,并保存到r0寄存器中。

第44行,将寄存器r0中的值与Ox1F进行与运算,结果保存到r1寄存器中,目的就是提取cpsr的bit0-bit4这5位,这5位为M4 M3 M2 M1 M0, M[4:0]这五位用来设置处理器的工作模式,如表所示:

 

第45行,判断r1寄存器的值是否等于 0X1A(0b11010),也就是判断当前处理器模式是否处于 Hyp模式。

第46 行,如果r1和Ox1A不相等,也就是CPU不处于Hyp模式的话就将r0寄存器的bit0-5进行清零,其实就是清除模式位

第47行,如果处理器不处于Hyp模式的话就将r0的寄存器的值与0x13进行或运算,0x13=0b10011,也就是设置处理器进入SVC模式。

第48行, r0寄存器的值再与0xC0进行或运算,那么r0寄存器此时的值就是0xD3, cpsr的I为和F位分别控制IRQ和FIQ这两个中断的开关,设置为1就关闭了FIQ和IRQ!

第49行,将r0寄存器写回到cpsr寄存器中。完成设置CPU处于SVC模式,并且关闭FIQ和IRQ这两个中断。

继续执行执行下面的代码:

 

第56行,如果没有定义CONFIG_OMAP44XX和CONFIG_SPL_BUILD的话条件成立,此处条件成立。

第58行读取CP15中c1寄存器的值到r0寄存器中,这里是读取SCTLR 寄存器的值。

第59行,CR V在arch/arm/include/asm/system.h中有如下所示定义:

#define CR_V(1 << 13)/* Vectors relocated to 0xffff0000 */

因此这一行的目的就是清除SCTLR寄存器中的bit13, SCTLR寄存器结构如图所示:

 

从图可以看出,bit13为V位,此位是向量表控制位,当为0的时候向量表基地址为0X00000000,软件可以重定位向量表。为1的时候向量表基地址为0XFFFF0000,软件不能重定位向量表。这里将V清零,目的就是为了接下来的向量表重定位

第60行将r0寄存器的值重写写入到寄存器SCTLR 中。

第63行设置r0寄存器的值为_start,_start就是整个uboot的入口地址,其值为0X87800000,相当于uboot的起始地址,因此0x87800000也是向量表的起始地址。

第64行将r0寄存器的值(向量表值)写入到CP15的c12寄存器中,也就是VBAR寄存器。因此第58~64行就是设置向量表重定位的。

代码继续往下执行:

 

第68行如果没有定义CONFIG_SKIP_LOWLEVEL_INIT的话条件成立。我们没有定义CONFIG_SKIP_LOWLEVEL_INIT,因此条件成立,执行下面的语句。

示例代码中的内容比较简单,就是分别调用函数cpu_init_cp15、 cpu_init_crit和_main。

函数cpu_init_ep15用来设置CP15相关的内容,比如关闭MMU啥的,此函数同样在start.S文件中定义的,代码如下:

函数cpu_init_cp15都是一些和CP15有关的内容,我们不用关心,有兴趣的可以详细的看一下。

函数cpu init crit也在是定义在start.S文件中,函数内容如下:

可以看出函数cpu_init_crit内部仅仅是调用了函数lowlevel_init,接下来就是详细的分析一下lowlevel_init和_main这两个函数。 

lowlevel_init函数详解

函数lowlevel_init在文件arch/arm/cpu/armv7/lowlevel_init.S中定义,内容如下: 

 第22行设置sp指向CONFIG_SYS_INIT_SP_ADDR,CONFIG_SYS_INIT_SP_ADDR在include/configs/mx6ullevk.h文件中,在 mx6ullevk.h中有如下所示定义:

示例代码中的IRAM-BASE-ADDR和IRAM-SIZE在文

件.arch/arm/include/asm/arch-mx6/imx-regs.h中有定义,如下所示,其实就是IMX6UL/IM6ULL内部ocram的首地址和大小。

 

如果408行的条件成立的话IRAM_SIZE=0X40000,当定义了CONFIG_MX6SX、CONFIG_MX6U、CONFIG_MX6SLL和CONFIG_MX6SL中的任意一个的话条件就不成立,在.config中定义了CONFIG MX6UL,所以条件不成立,因此IRAM SIZE=0X20000-128KB,

结合示例代码,可以得到如下值:

CONFIG_SYS_INIT_RAM_ADDR = IRAM_BASE_ADDR = 0x00900000.

CONFIG_SYS_INIT_RAM_SIZE= 0x00020000 = 128KB

 还需要知道GENERATED_GBL_DATA_SIZE的值,在文件include/generated/generic-asm-offsets.h中有定义,如下:

 GENERATED_GBL_DATA_SIZE-256, GENERATED_GBL_DATA_SIZE的含义为(sizeof(struct global_data) + 15) & ~15。

综上所述,CONFIG_SYS_INIT_SP_ADDR值如下: 

CONFIG_SYS_INIT_SP_OFFSET= 0x00020000 - 256 = 0x1FF00

CONFIG_SYS_INIT_SP_ADDR = 0x00900000 + 0X1FF00 = 0x0091FF00,

 结果如下图所示:

 

此时sp指向0X91FF00,这属于IMX6UL/IMX6ULL的内部ram。

继续回到文件lowlevel_init.S,第23行对sp指针做8字节对齐处理!

第34行, sp指针减去GD_SIZE, GDSIZE同样在generic-asm-offsets.h中定了,大小为248,见示例代码generic-asm-offsets.h第11行。

第35行对sp做8字节对齐,此时sp的地址为0x0091FF00-248-0X0091FE08,此时sp位置如图所示:

第36行将sp地址保存在r9寄存器中。

第42行将ip和Ir压栈

第57行调用函数s_init,得,又来了一个函数。

第58行将第36行入栈的ip和Ir进行出栈,并将Ir赋给pc

s_init函数详解

知道lowlevel_init函数后面会调用s_init函数, s_init函数定义在文件arch/arm/cpu/army7/mx6/soc.c中,如下所示:

 在第816行会判断当前CPU类型,如果CPU为MX6SX、MX6UL、MX6ULL或MX6SLL中的任意一种,那么就会直接返回,相当于s_init函数什么都没做。所以对于I.MX6UL/I.MX6ULL来说,s_init就是个空函数。从s_init函数退出以后进入函数lowlevel_init,但是lowlevel_init函数也执行完成了,返回到了函数cpu_init_crit,函数cpu_init_crit 也执行完成了,最终返回到save_boot_params_ret,函数调用路径如图所示:

 从图可知,接下来要执行的是save_boot_params_ret中的_main函数,下一章分析_main函数。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值