u-boot移植篇——u-boot启动流程分析 上

本文详细解析U-Boot启动过程,从_start入口点出发,深入探讨reset函数、_main函数及board_init_f函数等内容。文章重点分析了关键函数的作用及其执行流程。

本系列使用u-boot版本为u-boot-2018.01;

一、U-Boot启动流程详解

通过前面分析上文:u-boot移植篇——了解u-boot的分析,我们可以从u-boot.lds链接脚本入手开始分析u-boot,入口点是_start,_start 在文件 arch/arm/lib/vectors.S 中有定义,节选源码如下:

        .macro ARM_VECTORS
	b	reset
	ldr	pc, _undefined_instruction
	ldr	pc, _software_interrupt
	ldr	pc, _prefetch_abort
	ldr	pc, _data_abort
	ldr	pc, _not_used
	ldr	pc, _irq
	ldr	pc, _fiq
	.endm

前期做了一些异常向量跳转的加载,当一个异常或中断发生时,CPU根据异常,在异常(中断)向量表中找到对应的异常向量,然后执行异常向量处的跳转指令,CPU跳转到对应的异常处理程序执行。复位异常向量的指令“ b reset”决定 u-boot 启动后将自动跳转到标号 reset 处执行。标号reset 在arch/arm/cpu/armv7/start.S中定义,节选代码如下:

	.globl	reset
	.globl	save_boot_params_ret
	.type   save_boot_params_ret,%function
#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:
#ifdef CONFIG_ARMV7_LPAE
/*
 * check for Hypervisor support
 */
	mrc	p15, 0, r0, c0, c1, 1		@ read ID_PFR1
	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

/*
 * 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
arch/arm/cpu/armv7/start.S节选源码--reset
* reset:开始就是标号reset的入口,也可以成为函数; * 从reset函数下来,`b save_boot_params`,跳转到`save_boot_params`函数,函数源码如下:
/*************************************************************************
 *
 * void save_boot_params(u32 r0, u32 r1, u32 r2, u32 r3)
 *	__attribute__((weak));
 *
 * Stack pointer is not yet initialized at this moment
 * Don't save anything to stack even if compiled with -O0
 *
 *************************************************************************/
ENTRY(save_boot_params)
	b	save_boot_params_ret		@ back to my caller
ENDPROC(save_boot_params)
	.weak	save_boot_params

继续跳转save_boot_params_ret函数,save_boot_params_ret函数原型如下:

save_boot_params_ret:
#ifdef CONFIG_ARMV7_LPAE
/*
 * check for Hypervisor support
 */
	mrc	p15, 0, r0, c0, c1, 1		@ read ID_PFR1
	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

这里涉及到一下基础寄存器的读取和配置,一般我们可以不做过于深入的理解,可以从注释可以了解大概做了什么工作,比如:

  • 第16行,讲cpsr寄存器的内容保存到r0寄存器,cpsr寄存器是什么:CPSR也叫程序状态寄存器,CPSR中包含条件码标志、中断禁止位、当前处理器模式以及其他状态和控制信息;
  • 第17行,讲读取到的r0的值与上立即数0x1f,再将结果保存到r1寄存器中,目的是取出cpsr的低5位,而cpsr的低5位刚好又是控制位,用于设置处理器的工作模式的,所以也可以根据上面节选源码的注释进行直接理解即可;
  • 后面就不一一解读了,有兴趣的可以继续深入理解,这就进行模式判断,设置处理器进入超级管理模式SVC模式,然后关闭FIQ和IRQ两种中断;
  • 继续回到arch/arm/cpu/armv7/start.S节选源码–reset
#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

这里涉及到一个宏,CONFIG_SPL_BUILD,如果启动CONFIG_SPL_BUILD宏,一般会生成两个uboot相关的文件,一个为MLO(引导uboot)也叫SPL(Secondary Program Loader 的简称, 也就是第二阶段程序加载器)用于初始化DDR内存并将uboot主体加载到内存中的,剩下一个就是uboot主体镜像,一般TI的芯片比如AM335x、AM572x等芯片会使用CONFIG_SPL_BUILD来编译,而我目前使用zynq好像一般用FSBL(first stage boot loader 第一阶段的加载程序)比较多,FSBL和SPL在功能上是类似的,不用过于纠结。

  • 第3行,读取CP15协处理器中c1寄存器的值到r0,至于CP15协处理器是什么,大家自行补充,CP15东西太多,这里不喧宾夺主介绍一遍了;
  • 第4行,CR_V有段定义#define CR_V (1 << 13) /* Vectors relocated to 0xffff0000 */,这里主要将r0寄存器中的第13位清除,该位代表SCTLR寄存器的V位向量表控制位,清零表示软件可以重定位向量表
  • 第5行,将清零了第13位的r0重写回CP15 SCTLR寄存器中;
  • 第8行,设置 r0 寄存器的值为_start,_start就是整个uboot 的入口地址,其值为0x4000000,相当于uboot的起始地址,因此0x4000000也是向量表的起始地址;
  • 第9行,将r0寄存器的值(向量表值)写入到CP15的c12寄存器中,也就是VBAR寄存器。所以这段程序的作用总的来说就是设置向量表重定位的。

Keep going

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大大棋

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值