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_label
mrs \xreg, CurrentEL
cmp \xreg, 0xc
b.eq \el3_label
cmp \xreg, 0x8
b.eq \el2_label
cmp \xreg, 0x4
b.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, x0
mrs x0, scr_el3
orr x0, x0, #0xf /* SCR_EL3.NS|IRQ|FIQ|EA */
msr scr_el3, x0
msr cptr_el3, xzr /* Enable FP/SIMD */
#ifdef COUNTER_FREQUENCY
ldr x0, =COUNTER_FREQUENCY
msr cntfrq_el0, x0 /* Initialize CNTFRQ */
#endif
b 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, x0
mov x0, #0x33ff
msr cptr_el2, x0 /* Enable FP/SIMD */
b 0f
1: msr vbar_el1, x0
mov x0, #3 << 20
msr 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_SMPEN
switch_el x1, 3f, 1f, 1f
3:
mrs x0, S3_1_c15_c2_1 /* cpuectlr_el1 */
orr x0, x0, #0x40
msr 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 */
ret
apply_a57_core_errata:
#ifdef CONFIG_ARM_ERRATA_828024
mrs 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 << 27
msr S3_1_c15_c2_0, x0 /* cpuactlr_el1 */
#endif
#ifdef CONFIG_ARM_ERRATA_826974
mrs x0, S3_1_c15_c2_0 /* cpuactlr_el1 */
/* Disable speculative load execution ahead of a DMB */
orr x0, x0, #1 << 59
msr S3_1_c15_c2_0, x0 /* cpuactlr_el1 */
#endif
#ifdef CONFIG_ARM_ERRATA_833471
mrs x0, S3_1_c15_c2_0 /* cpuactlr_el1 */
/* FPSCR write flush.
* Note that in some cases where a flush is unnecessary this
could impact performance. */
orr x0, x0, #1 << 38
msr S3_1_c15_c2_0, x0 /* cpuactlr_el1 */
#endif
#ifdef CONFIG_ARM_ERRATA_829520
mrs x0, S3_1_c15_c2_0 /* cpuactlr_el1 */
/* Disable Indirect Predictor bit will prevent this erratum
from occurring
* Note that in some cases where a flush is unnecessary this
could impact performance. */
orr x0, x0, #1 << 4
msr S3_1_c15_c2_0, x0 /* cpuactlr_el1 */
#endif
#ifdef CONFIG_ARM_ERRATA_833069
mrs x0, S3_1_c15_c2_0 /* cpuactlr_el1 */
/* Disable Enable Invalidates of BTB bit */
and x0, x0, #0xE
msr S3_1_c15_c2_0, x0 /* cpuactlr_el1 */
#endif
b 0b
ENDPROC(apply_core_errata)
2.4、lowlevel_init
接下来就到了大名鼎鼎的lowlevel_init,本章先不分析,留待下章
本章分析完毕~
完结撒花✿✿ヽ(°▽°)ノ✿


![[动态规划]---part1](https://img-blog.csdnimg.cn/direct/2e53238bbd894b3e8741f29286259a56.png)















![BUUCTF---[ACTF2020 新生赛]BackupFile1](https://img-blog.csdnimg.cn/direct/ebe27d78c03d4ceaa5746789bcacf836.png)