U-Boot源码分析5—启动过程分析start.S
- 1、`boot0.h`
- 2、`reset`
- 2.1、`vectors`
- 2.2、`ELn`
- 2.2.1 EL3
- 2.2.2、EL2、EL1
 
- 2.3、`SMPEN`
- 2.3、`core errate`
- 2.4、`lowlevel_init`
 
前面从U-Boot编译的角度分析了其Makefile、链接脚本等,本章开始正式分析U-Boot启动过程
从上一篇文章7、U-Boot源码分析4—链接脚本分析,可知U-Boot启动入口_start在arch/arm/cpu/armv8/start.S中
1、boot0.h
 
在start.S中找到启动入口`_start:
 
这里针对汇编文件的结尾*.s和*.S进行说明:其中小写的s表示文件中仅包含汇编代码,编译器将不进行预编译操作;而大写的S表示文件中包含相关预编译代码,编译器将进行预编译操作,即判断#ifdef、#ifndef、#if defined(xxx)等语句的条件是否成立并保留相关代码,或将#include指令包含的文件内容替换到对应位置,等等的预编译操作
这里搜索CONFIG_ENABLE_ARM_SOC_BOOT0_HOOK,发现存在定义:
 
 因此将执行第28行的语句,即#include <asm/arch/boot0.h>,对于某些芯片需要特殊的一些初始配置的话,可以通过配置CONFIG_ENABLE_ARM_SOC_BOOT0_HOOK并执行相应boot0.h中的代码来进行一些boot之前的操作,内容如下
 
可知其语句和其它启动文件基本一样,都是跳转至reset标号,但是增加了与AArch64切换有关的代码,查看arch/arm/mach-sunxi/rmr_switch.S中的说明,其实是本例所使用芯片的特殊属性,因为该芯片在复位后会进入AArch32状态,所以在一些时刻需要切换到AArch64状态,这里涉及ARMv8的Reset Management Register,暂时先不分析
随后进行 2 3 = 8 2^3=8 23=8字节对齐(.align 3),并定义了一些符号:_end_ofs、_bss_start_ofs、_bss_end_ofs,是一些关键段与代码段起始位置的偏移
2、reset
 
跳转进入reset后,首先跳转进入save_boot_params,这里save_boot_params通过WEAK关键字定义为save_boot_params_ret(start.S文件最后);
 因为使用了WEAK关键字,当然也可以直接在其它文件中定义自己的save_boot_params函数(需使用头文件引用或在已有的头文件中进行声明)
 
 这里搜索CONFIG_SYS_RESET_SCTRL,发现没有定义,因此不进行跳转,继续向下执行。
 其实跳转到reset_sctrl中主要是根据当前异常等级执行对应操作:设置小端,关MMU,关I/D-cache
2.1、vectors
 
接下来,第67行首先将中断向量vectors的地址保存在x0寄存器中,中断向量的定义在arch/arm/cpu/armv8/excetions.S中:
 
这里首先进行 2 1 1 = 2048 2^11=2048 211=2048字节即2K对齐(.align 11),且各个中断向量地址均以 2 7 = 128 2^7=128 27=128字节对齐;
每个中断向量都对应其跳转指令,比如b _do_bad_sync,先用bl指令跳转到do_bad_sync函数执行,然后再跳转到中断退出函数exception_exit;而do_bad_sync函数最终将输出各个关键寄存器的值,最后调用panic函数终止正在运行的程序并进行复位
/** do_bad_sync handles the impossible case in the Synchronous Abort vector.*/
void do_bad_sync(struct pt_regs *pt_regs, unsigned int esr)
{efi_restore_gd();printf("Bad mode in \"Synchronous Abort\" handler, esr 0x%08x\n", esr);show_regs(pt_regs);panic("Resetting CPU ...\n");
}
当然这里所有中断都没有打开,目前不会进入任何中断向量,另外现在没有初始化堆栈等,也无法执行C函数
2.2、ELn
 
然后第68行判断异常等级,并根据判断结果从不同分支开始执行
 
 其中switch_el在arch/arm/include/asm/macro.h中定义
/** Branch according to exception level*/
.macro	switch_el, xreg, el3_label, el2_label, el1_labelmrs	\xreg, CurrentELcmp	\xreg, 0xcb.eq	\el3_labelcmp	\xreg, 0x8b.eq	\el2_labelcmp	\xreg, 0x4b.eq	\el1_label
.endm
其中xreg即为传入的x1寄存器,这里将CurrentEL寄存器的值放入x1寄存器中,并依次和0xc、0x8、0x4进行比较,根据比较结果进入不同的分支标号
CurrentEL寄存器即为Current Exception Level寄存器,根据ARMv8的手册,其定义如下:
 
 因此0xc、0x8、0x4即分别对应EL3、EL2、EL1
2.2.1 EL3
可知当复位后CPU处于EL3时,执行如下代码
3:	msr	vbar_el3, x0mrs	x0, scr_el3orr	x0, x0, #0xf			/* SCR_EL3.NS|IRQ|FIQ|EA */msr	scr_el3, x0msr	cptr_el3, xzr			/* Enable FP/SIMD */
#ifdef COUNTER_FREQUENCYldr	x0, =COUNTER_FREQUENCYmsr	cntfrq_el0, x0			/* Initialize CNTFRQ */
#endifb	0f
首先将x0寄存器中的中断向量vectors的地址放入vbar_el3寄存器:Vector Base Address Register (EL3)
 
 其中低11位(0~10)保留为0,对应前面中断向量最开始的2K字节对齐(.align 11)
随后的3行语句,首先将scr_el3寄存器的值保存到x0寄存器中,然后将x0寄存器或操作上0xF即将低4位置1,然后保存到x0寄存器中,最后将x0寄存器的值再赋给scr_el3寄存器,即scr_el3寄存器低4为置1
 
 scr_el3寄存器bit3对应EA,即任何异常等级下发生的External Abort 和 SError 中断都由EL3进行处理;bit2对应FIQ,即任何异常等级下发生的硬件上的FIQ (快速中断请求)都由EL3进行处理;bit1对应IRQ,即任何异常等级下发生的硬件上的IRQ (中断请求)都由EL3进行处理;bit0对应NS,置1表示任何低于EL3的异常等级(EL0、EL1等)都是非安全状态,无法访问安全内存空间
然后使用xzr(64位全零寄存器)将cptr_el3寄存器清零,
 
 即(TCPAC)打开EL2访问CPTR_EL2、HCPTR寄存器的权限,打开EL2、EL1访问CPACR_EL1、CPACR寄存器的权限;(TAM)打开EL2、EL1访问Activity Monitor registers的权限;(TTA)打开系统寄存器访问trace寄存器的权限;(TFP)使能SVE、SIMD和浮点运算;但(EZ)又把SVE关了
因此这一行代码的功能也就是注释说的使能SIMD和FP运算
然后由于在include/configs/sunxi-common.h中定义了COUNTER_FREQUENCY的值为24000000,因此将此值由x0寄存器赋给cntfrq_el0寄存器,即配置系统计数器的时钟值,此系统计数器一般用于精确计时
结合上述分析,EL3等级对应的操作即为将中断向量地址放入vbar_el3寄存器,设置相关中断请求仅由EL3实现,使能SIMD和FP运算,设置系统计数器的时钟值
2.2.2、EL2、EL1
对应执行的代码为,均为将中断向量地址放入对应的vbar_eln寄存器,使能SIMD和FP运算等,这里不再详细分析
2:	msr	vbar_el2, x0mov	x0, #0x33ffmsr	cptr_el2, x0			/* Enable FP/SIMD */b	0f
1:	msr	vbar_el1, x0mov	x0, #3 << 20msr	cpacr_el1, x0			/* Enable FP/SIMD */
2.3、SMPEN
 
接下来的语句是开启多核相关的操作,这里虽然没有定义CONFIG_ARMV8_SET_SMPEN,但注释里建立用于Cortex-A53时使能,因此这里也分析一下
	/** Enable SMPEN bit for coherency.* This register is not architectural but at the moment* this bit should be set for A53/A57/A72.*/
#ifdef CONFIG_ARMV8_SET_SMPENswitch_el x1, 3f, 1f, 1f
3:mrs     x0, S3_1_c15_c2_1               /* cpuectlr_el1 */orr     x0, x0, #0x40msr     S3_1_c15_c2_1, x0
1:
#endif
这里的操作也一样,首先判断所处的异常等级,仅在EL3时进行操作:首先将S3_1_c15_c2_1也即cpuectlr_el1寄存器的值放入x0寄存器,然后通过orr指令将第6位置1;这里cpuectlr_el1寄存器在Cortex-A53手册中(ARMv8架构定义了一些必须实现的寄存器,同时预留一些空间给不同处理器来实现各自的特色功能等)
 
 第6位即SMPEN置1,表示使能各核心之间的数据一致性功能(主要与cache有关,对于多核系统来说,一般各个核具有自己的L1-cache,簇内共享L2-cache,还有外部的L3-cache,这样不同核心上的cache或ram对于同一个数据可能有多个副本,会导致数据观察者(CPU/GPU/DMA等)能看到的数据不一致,因此设置会再处理器中设置硬件来维护数据的一致性,具体内容可参考【Cache篇】一文总结ARMv8架构中关于Cache的知识点)
2.3、core errate
 
随后进行核心勘误,目前仅支持Cortex-A57进行核心勘误表的设置,这里跳过
	/* Apply ARM core specific erratas */bl	apply_core_errata
....
WEAK(apply_core_errata)mov	x29, lr			/* Save LR *//* For now, we support Cortex-A57 specific errata only *//* Check if we are running on a Cortex-A57 core */branch_if_a57_core x0, apply_a57_core_errata
0:mov	lr, x29			/* Restore LR */retapply_a57_core_errata:#ifdef CONFIG_ARM_ERRATA_828024mrs	x0, S3_1_c15_c2_0	/* cpuactlr_el1 *//* Disable non-allocate hint of w-b-n-a memory type */orr	x0, x0, #1 << 49/* Disable write streaming no L1-allocate threshold */orr	x0, x0, #3 << 25/* Disable write streaming no-allocate threshold */orr	x0, x0, #3 << 27msr	S3_1_c15_c2_0, x0	/* cpuactlr_el1 */
#endif#ifdef CONFIG_ARM_ERRATA_826974mrs	x0, S3_1_c15_c2_0	/* cpuactlr_el1 *//* Disable speculative load execution ahead of a DMB */orr	x0, x0, #1 << 59msr	S3_1_c15_c2_0, x0	/* cpuactlr_el1 */
#endif#ifdef CONFIG_ARM_ERRATA_833471mrs	x0, S3_1_c15_c2_0	/* cpuactlr_el1 *//* FPSCR write flush.* Note that in some cases where a flush is unnecessary thiscould impact performance. */orr	x0, x0, #1 << 38msr	S3_1_c15_c2_0, x0	/* cpuactlr_el1 */
#endif#ifdef CONFIG_ARM_ERRATA_829520mrs	x0, S3_1_c15_c2_0	/* cpuactlr_el1 *//* Disable Indirect Predictor bit will prevent this erratumfrom occurring* Note that in some cases where a flush is unnecessary thiscould impact performance. */orr	x0, x0, #1 << 4msr	S3_1_c15_c2_0, x0	/* cpuactlr_el1 */
#endif#ifdef CONFIG_ARM_ERRATA_833069mrs	x0, S3_1_c15_c2_0	/* cpuactlr_el1 *//* Disable Enable Invalidates of BTB bit */and	x0, x0, #0xEmsr	S3_1_c15_c2_0, x0	/* cpuactlr_el1 */
#endifb 0b
ENDPROC(apply_core_errata)
2.4、lowlevel_init
 
接下来就到了大名鼎鼎的lowlevel_init,本章先不分析,留待下章
本章分析完毕~
 完结撒花✿✿ヽ(°▽°)ノ✿