正点原子[第三期]Arm(iMX6U)Linux移植学习笔记-6.2uboot启动流程-lowlevel_init,s_init,_main函数执行
前言:
本文是根据哔哩哔哩网站上“Arm(iMX6U)Linux系统移植和根文件系统构键篇”视频的学习笔记,在这里会记录下正点原子 I.MX6ULL 开发板的配套视频教程所作的实验和学习笔记内容。本文大量引用了正点原子教学视频和链接中的内容。
引用:
正点原子IMX6U仓库 (GuangzhouXingyi) - Gitee.com
《【正点原子】I.MX6U嵌入式Linux驱动开发指南V1.5.2.pdf》
正点原子资料下载中心 — 正点原子资料下载中心 1.0.0 文档
正点原子imx6ull-mini-Linux驱动之Linux I2C 驱动实验(21)-CSDN博客
正文:
本文是 “Arm(iMX6U)Linux系统移植和根文件系统构键篇--6.2 讲uboot顶层Makefile分析创建VSCode工程。本节将参考正点原子的视频教程和配套的正点原子开发指南文档进行学习。
0. 概述
1 reset函数源码详解
从u-boot.lds中我们已经知道了入口点是 arch/arm/lib/vectors.S文件中的 _start,代码如下
第48行 _start开始的是中断向量表,其中 54~61行就是中断向量表,和我们裸机例程里面一样。54行跳转到 reset函数里面, reset函数在 arch/arm/cpu/armv7/start.S里面,代码如下:
arch/arm/cpu/armv7/start.S
第 35行就是 reset函数。
第 37行从 reset函数跳转到了 save_boot_params函数,而 save_boot_params函数同样定义在start.S里面,定义如下:
save_boot_params函数也是只有一句跳转语句,跳转到 save_boot_params_ret函数,save_boot_params_ret函数代码如下:
第 43行,读取寄存器 cpsr中的值,并保存到 r0寄存器中。
第 44行,将寄存器 r0中的值与 0X1F进行与运算,结果保存到 r1寄存器中,目的就是提取 cpsr的 bit0~bit4这 5位,这 5位为 M4 M3 M2 M1 M0 M[4:0]这五位用来设置处理器的工作模式,如表32.2.1.1所示:
第 45行,判断 r1寄存器的值是否等于 0X1A(0b11010),也就是判断当前处理器模式是否处于 Hyp模式。
第 46行,如果 r1和 0X1A不相等,也就是 CPU不处于 Hyp模式的话就将 r0寄存器的bit0~5进行清零,其实就是清除模式位
bicne 指令是 bic + ne
bic 是bitclean 位清除指令,加上条件 ne (non-equal)不相等条件满足时执行 bic 指令
第 47行,如果处理器不处于 Hyp模式的话就将 r0的寄存器的值与 0x13进行或运算,0x13=0b10011,也就是设置处理器进入 SVC模式。
orrne 指令时 orr + ne
orr 时或运算指令,加上条件 ne (non-equal)不相等条件满足时执行 orr 指令
第 48行, r0寄存器的值再与 0xC0进行或运算,那么 r0寄存器此时的值就是 0xD3 cpsr的 I为和 位分别控制 IRQ和 FIQ这两个中断的开关,设置为 1就关闭了 FIQ和 IRQ
第 49行,将 r0寄存器写回到 cpsr寄存器中。完成设置 CPU处于 SVC32模式,并且关闭
FIQ和 IRQ这两个中断。
ARMv7 CPSR寄存器
ARMv7 的CPSR 寄存器的定义,bit[0-4] 这5位控制ARMv7 CPU的工作模式,bit[6]控制FIQ中断,开关bit[7]控制FIQ中断开关。
继续执行执行下面的代码:
第56行,如果没有定义 CONFIG_OMAP44XX和 CONFIG_SPL_BUILD的话条件成立,此处条件成立。
第58行读取 CP15中 c1寄存器的值到 r0寄存器中,根据 17.1.4小节可知,这里是读取SCTLR寄存器的值。
第59行, CR_V在 arch/arm/include/asm/system.h中有如下所示定义:
arch/arm/include/asm/system.h
#define CR_V (1 << 13) /* Vectors relocated to 0xffff0000 */
因此这一行的目的就是清除SCTLR寄存器中的 bit13,SCTLR寄存器结构如图 32.2.1.1所示:
从图 32.2.1.1可以看出,
- 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,因此条件成立,执行下面的语句。
示例代码 32.2.1.6 中的内容比较简单,
- 就是分别调用函数 cpu_init_cp15、
- cpu_init_crit
- 和_main。
函数 cpu_init_cp15 用来设置 CP15 相关的内容,比如关闭 MMU 啥的,此函数同样在 arch/arm/cpu/armv7/start.S文件中定义的,代码如下:
arch/arm/cpu/armv7/start.S
函数 cpu_init_cp15 都是一些和 CP15 有关的内容,我们不用关心,有兴趣的可以详细的看一下。
函数 cpu_init_crit 也在是定义在 start.S 文件中,函数内容如下:
arch/arm/cpu/armv7/start.S
可以看出函数 cpu_init_crit 内部仅仅是调用了函数 lowlevel_init,接下来就是详细的分析一下 lowlevel_init 和_main 这两个函数。
2 lowlevel_init 函数详解
函数 lowlevel_init 在文件 arch/arm/cpu/armv7/lowlevel_init.S 中定义,内容如下:
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 中有如下所示定义:
include/configs/mx6ullevk.h
示 例 代 码 32.2.2.2 中 的 IRAM_BASE_ADDR 和 IRAM_SIZE 在 文 件arch/arm/include/asm/arch-mx6/imx-regs.h 中有定义,如下所示,其实就是 IMX6UL/IM6ULL 内部 ocram (On-Chip-RAM,芯片片上RAM) 的首地址和大小。
arch/arm/include/asm/arch-mx6/imx-regs.h
如果 408 行的条件成立的话 IRAM_SIZE=0X40000,当定义了 CONFIG_MX6SX、CONFIG_MX6U、 CONFIG_MX6SLL 和 CONFIG_MX6SL 中的任意一个的话条件就不成立,在.config 中定义了 CONFIG_MX6UL,所以条件不成立,因此 IRAM_SIZE=0X20000=128KB。
结合示例代码 32.2.2.2,可以得到如下值:
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中有定义,如下:
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,