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
,本章先不分析,留待下章
本章分析完毕~
完结撒花✿✿ヽ(°▽°)ノ✿