主体内容阅读
以我手头的imx6ull的coretex-A7内核对应的start.S作为学习样本,逐步了解u-boot
c
.globl reset
.globl save_boot_params_ret
#ifdef CONFIG_ARMV7_LPAE
.global switch_to_hypervisor_ret
#endif
reset:
/* Allow the board to save important registers */
b save_boot_params
save_boot_params_ret:
开头这段主要是提供一个上电复位的全局入口标号reset,芯片上电复位(Reset)后,异常向量表中的复位跳转指令(如 b reset)就会精准跳转到这里的 reset: 标号处开始执行,
并且提供一个save_boot_params函数,其负责先把BootROM留下的一些参数(例如:芯片是从 SD 卡、NAND Flash 还是 USB 启动的,或者一些安全密钥、Boot ROM 传递的参数指针)存到内存中再回过头来初始化
CONFIG_ARMV7_LPAE 则是一个32位物理地址扩展功能,其能把4G的虚拟地址空间往1TB的物理地址空间中映射,我的这个imx6ull显然没有开启这个功能,所以跳过
switch_to_hypervisor_ret可以理解为对内存的一种超级虚拟化,这里提供一个Hypervisor / Virtualization 模式切换后的返回点
save_boot_params_ret: 是特定的返回入口标号。 在 save_boot_params 函数执行完毕后,它不会用 bx lr(因为此时连 SP 栈都还没建立),而是直接使用 b save_boot_params_ret 强行跳回这里,继续往下执行 CPU 的基础硬件初始化(如关 Cache、关 MMU、切 SVC 模式等)。
c
#ifdef CONFIG_ARMV7_LPAE
/*
* check for Hypervisor support
*/
mrc p15, 0, r0, c0, c1, 1 @ read ID_PFR1 (Processor Feature Register 1,处理器特征寄存器 1)
and r0, r0, #CPUID_ARM_VIRT_MASK @ mask virtualization bits
cmp r0, #(1 << CPUID_ARM_VIRT_SHIFT)
beq switch_to_hypervisor
switch_to_hypervisor_ret:
#endif
/*
* disable interrupts (FIQ and IRQ), also set the cpu to SVC32 mode,
* except if in HYP mode already
*/
mrs r0, cpsr
and r1, r0, #0x1f @ mask mode bits
teq r1, #0x1a @ test for HYP mode
bicne r0, r0, #0x1f @ clear all mode bits
orrne r0, r0, #0x13 @ set SVC mode
orr r0, r0, #0xc0 @ disable FIQ and IRQ
msr cpsr,r0
#ifdef CONFIG_ARMV7_LPAE中包含的是对hypervisor模式的检查与配置,最后会通过开头定义的全局标号在栈还未初始化的情况下,精确跳转回来switch_to_hypervisor_ret
接下来的一些语句就很清晰了,注释写的很清楚
关中断,并且将cpu切换为supervisor模式,让U-Boot 拥有全盘操控硬件的最高特权级
mrs r0, cpsr: 读取当前程序状态寄存器(CPSR)的值到r0。and r1, r0, #0x1f: 提取 CPSR 的低 5 位M[4:0](Mode bits),即当前 CPU 处于什么模式。teq r1, #0x1a: 测试当前模式是否为 HYP 模式(编码为0x1A) 。- 注:
TEQ是按位异或测试。如果相等,Z 标志位置 1,后续带有ne(Not Equal) 条件后缀的指令就会跳过不执行。
- 注:
bicne r0, r0, #0x1f: 如果不等于 HYP 模式(ne) :将低 5 位模式位全部清零(BIC= Bit Clear)。orrne r0, r0, #0x13: 如果不等于 HYP 模式(ne) :将低 5 位设置为0x13(0x13代表 SVC32 模式 ,即 Supervisor 模式)。- 逻辑小结:如果 CPU 已经处于 HYP 模式(比如刚才完成了 Hypervisor 初始化),就不去改变它的模式,让它保持在 HYP 模式;如果是 User、IRQ、System 等其他模式,则强制改成 SVC32 模式。
orr r0, r0, #0xc0: 将r0的第 6 位(F)和第 7 位(I)置 1(0xC0=1100 0000b)。在 ARM 中,将 I 和 F 位置 1 代表禁止 IRQ 和 FIQ 中断。msr cpsr, r0: 把修改后的r0值写回cpsr寄存器,模式切换和关中断在此指令完成后立即生效!
(mrs)M ove to R egister from S pecial System Register和(msr) M ove to S pecial System Register from R egister的缩写。
它们是 ARM 架构中专门用于在通用寄存器(R0~R15)与特殊系统程序状态寄存器(如 CPSR / SPSR)之间传输数据的指令。
c
/*
* Setup vector:
* (OMAP4 spl TEXT_BASE is not 32 byte aligned.
* Continue to use ROM code vector only in OMAP4 spl)
*/
#if !(defined(CONFIG_OMAP44XX) && defined(CONFIG_SPL_BUILD))
/* Set V=0 in CP15 SCTLR register - for VBAR to point to vector */
mrc p15, 0, r0, c1, c0, 0 @ Read CP15 SCTLR Register
bic r0, #CR_V @ V = 0
mcr p15, 0, r0, c1, c0, 0 @ Write CP15 SCTLR Register
/* Set vector address in CP15 VBAR register */
ldr r0, =_start
mcr p15, 0, r0, c12, c0, 0 @Set VBAR
#endif
/* the mask ROM code should have PLL and others stable */
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_cp15
#ifndef CONFIG_SKIP_LOWLEVEL_INIT_ONLY
bl cpu_init_crit
#endif
#endif
bl _main
#if !(defined(CONFIG_OMAP44XX) && defined(CONFIG_SPL_BUILD))显然是通过的
第一段if中主要做的是确定了异常向量表的位置在哪里,这里通过 bic r0, #CR_V 将 V 位置 0,解锁对 VBAR 寄存器的使用,异常向量表的位置就由 VBAR 寄存器说了算(也就是允许u-boot自定义异常向量表)
接着/* Set vector address in CP15 VBAR register */这代码将U-Boot 自己写的异常向量表首地址)写入 CP15 的 VBAR(Vector Base Address Register,向量基地址寄存器)
那么在u-boot的运行阶段,发生异常或者中断就有地方去处理了
紧接着
bl cpu_init_cp15: 跳转执行cpu_init_cp15函数。- 作用: 彻底复位与初始化 CP15 协处理器。清除并禁用 TLB(页表转换旁路缓存) 、I-Cache(指令缓存) 、D-Cache(数据缓存) 和 MMU(内存管理单元),保证 CPU 处于最干净的裸机物理寻址状态。
- 禁用I-Cache是因为在芯片初始化阶段,内存中的指令代码可能会被修改,关闭I-Cache这个指令加速器保证读到的一直是最新的指令
- 禁用D-Cache也是同理,往内存写数据(代码)必须随时保持最新----代码(指令)也是数据
- 禁用MMU和TLB则是因为此时页表还没建立,CPU发出的地址如果经过MMU,则必定出错
- 这里的D-Cache和I-Cache其实是哈佛架构(数据和代码指令分开)的一种体现,本质上是为了适配CPU和内存的速度差距
bl cpu_init_crit: 跳转执行cpu_init_crit函数(Critical Initialization,关键底座初始化)。- 作用: 调用平台/开发板级别的低级初始化(如
lowlevel_init)。主要在此处通过汇编操作硬件寄存器去初始化芯片的主频时钟(PLL)和外置 DRAM/DDR 内存控制器。
- 作用: 调用平台/开发板级别的低级初始化(如
bl _main: 则是进入c语言的世界- 这里的
_main位于arch/arm/lib/crt0.S(C Runtime 0,C 运行时初始化汇编)中。 - 在
_main中,代码会配置好 SRAM/DDR 上的栈指针 SP ,清空.bss段。 - 随后利用
bl指令直接跳转到 C 语言函数board_init_f和board_init_r。 - 从此,汇编代码完全退场,U-Boot 正式全面切入 C 语言的开发世界!
- 这里的
总结
1.保存启动参数 (save_boot_params):
芯片刚复位时,将 Boot ROM 传过来的启动媒介(如 SD 卡/NAND/USB)或寄存器参数先暂存起来,防止后续操作冲掉。
2.关中断并切特权级 (SVC / HYP Mode):
通过改写 CPSR 寄存器关闭全局中断(FIQ/IRQ),防止未初始化时误触中断挂死;同时将 CPU 模式锁定在特权最高的 SVC32 模式(若支持并配置了虚拟化,则做 HYP 模式检测与处理)。
3.接管异常向量表 (VBAR):
将 CP15 SCTLR 寄存器的 V 位置 0(禁用固定高向量表),并将 U-Boot 自身的 _start 首地址写入 VBAR 寄存器,正式接管芯片的异常/中断入口。
4.重置 CP15 协处理器 (cpu_init_cp15):
禁用并清空 MMU 、TLB 、I-Cache 和 D-Cache,让 CPU 回归最原始、无歧义的物理寻址和直读直写模式。
5.关键硬件初始化 (cpu_init_crit / lowlevel_init):
配置芯片锁相环(PLL 时钟)拉升主频,并初始化板载的 DRAM/DDR 控制器,为存放后续庞大的系统和应用准备好大容量内存。
6.跳转交接给 C 语言 (bl _main):
跳转到 _main(位于 crt0.S),在 SRAM/DDR 中建立起 C 语言依赖的 SP 栈 并清空 .bss 段,随后正式调用 board_init_f 进入 C 语言的广阔世界。