u-boot的start.S文件详解

主体内容阅读

以我手头的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 位设置为 0x130x13 代表 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_VV 位置 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_fboard_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):

禁用并清空 MMUTLBI-CacheD-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 语言的广阔世界。

相关推荐
道川贤林2 个月前
OrangePi 系统启动优先级修改
linux·linux驱动·orangepi·u-boot
Strugglingler3 个月前
【u-boot 提交 Patch 标准流程】
u-boot
blueSatchel6 个月前
U-Boot载入到DDR过程的代码分析
linux·开发语言·u-boot
blueSatchel6 个月前
U-Boot启动后做的事情
linux·u-boot
Sollong6 个月前
Redmi AX6 TTL 救砖记录
救砖·openwrt·嵌入式开发·u-boot·红米·ttl·ax6·mibib
万法若空6 个月前
U-Boot命令手册
网络·u-boot
uoscn6 个月前
从零开始实现U-Boot
arm·imx6ull·u-boot
kpler7 个月前
uboot移植第三方源码miniz
c语言·u-boot
uoscn8 个月前
Skipping xxx as repository xxxx doesn‘t support architecture ‘i386‘
linux·u-boot