1.前言
本文基于高通8996平台,kernel版本为3.18.31。
本文主要介绍head.S的el2_setup执行流程
2. el2_setup
/*
* If we're fortunate enough to boot at EL2,
* ensure that the world is sane before dropping to EL1.
*
* Returns either BOOT_CPU_MODE_EL1 or BOOT_CPU_MODE_EL2 in x20 if
* booted in EL1 or EL2 respectively.
*/
ENTRY(el2_setup)
//当前的exception level保存在PSTATE中,
//程序可以通过MRS或者MSR来访问PSTATE,
//CurrentEL就是获取PSTATE中current exception level域的特殊寄存器。
mrs x0, CurrentEL
// #define CurrentEL_EL2 (2 << 2)
//判断当前是否处于EL2
cmp x0, #CurrentEL_EL2
//当cpu不处于EL2时跳转到标号1
b.ne 1f
// 读取EL2状态的系统控制寄存器,该寄存器可以控制整个系统的行为
mrs x0, sctlr_el2
// Set the EE bit for EL2 // 大端置位EE位
CPU_BE( orr x0, x0, #(1 << 25) )
// Clear the EE bit for EL2 // 小端清除EE位
CPU_LE( bic x0, x0, #(1 << 25) )
// 写入EL2状态的系统控制寄存器
msr sctlr_el2, x0
b 2f
1: // 当前是EL1状态,读取EL1状态的系统控制寄存器
mrs x0, sctlr_el1
// Set the EE and E0E bits for EL1 // 大端置位EE位和EOE位
CPU_BE( orr x0, x0, #(3 << 24) )
// Clear the EE and E0E bits for EL1 // 小端清除EE位和EOE位
CPU_LE( bic x0, x0, #(3 << 24) )
// 写入EL1状态的系统控制寄存器
msr sctlr_el1, x0
// This cpu booted in EL1
// #define BOOT_CPU_MODE_EL1 (0xe11)
// 将BOOT_CPU_MODE_EL1保存到通用寄存器w20
mov w20, #BOOT_CPU_MODE_EL1
isb // 指令同步屏障,确保之前的指令已执行完成
ret // 当cpu处于EL1,在这里就返回了
/* Hyp configuration. */
2:
mov x0, #(1 << 31) // 64-bit EL1
// Hypervisor Configuration Register
// Controls virtualization settings and trapping of exceptions to EL2
msr hcr_el2, x0
/* Generic timers. */
mrs x0, cnthctl_el2 // Counter-timer Hyp Control register
orr x0, x0, #3 // Enable EL1 physical timers
msr cnthctl_el2, x0
msr cntvoff_el2, xzr // Clear virtual offset
// Counter-timer Virtual Offset register
// xzr零寄存器,顾名思义值为0
#ifdef CONFIG_ARM_GIC_V3
/* GICv3 system register access */
// AArch64 Processor Feature Register 0
mrs x0, id_aa64pfr0_el1
// ubfx: ARM位域提取指令,取x0的[27:24]bit
ubfx x0, x0, #24, #4
// 判断是否支持GIC V3和GIC V4
cmp x0, #1
// 不支持的话,跳转到标号3
b.ne 3f
// Interrupt Controller System Register Enable register (EL2)
mrs_s x0, ICC_SRE_EL2
// Set ICC_SRE_EL2.SRE==1 // 通过system register方式访问GIC
orr x0, x0, #ICC_SRE_EL2_SRE
// Set ICC_SRE_EL2.Enable==1 // EL1可以直接访问GIC
orr x0, x0, #ICC_SRE_EL2_ENABLE
msr_s ICC_SRE_EL2, x0
// Make sure SRE is now set
// 指令同步屏障,确保之前的指令已执行完成
isb
// Reset ICC_HCR_EL2 to defaults
msr_s ICH_HCR_EL2, xzr
3:
#endif
/* Populate ID registers. */
// Main ID Register 描述PE信息的寄存器
// PE: Processing Element, 处理单元
mrs x0, midr_el1
// Multiprocessor Affinity Register
mrs x1, mpidr_el1
// 将Main ID Register 写入到对应的virtual processor的寄存器
msr vpidr_el2, x0
msr vmpidr_el2, x1
/* sctlr_el1 */
// Set/clear RES{1,0} bits
mov x0, #0x0800
// Set EE and E0E on BE systems // 大端0x33d00800
CPU_BE( movk x0, #0x33d0, lsl #16 )
// Clear EE and E0E on LE systems // 小端0x30d00800
CPU_LE( movk x0, #0x30d0, lsl #16 )
// 除了EE和EOE,其他几位都没查到,囧!
msr sctlr_el1, x0
/* Coprocessor traps. */
mov x0, #0x33ff
msr cptr_el2, x0 // Disable copro. traps to EL2
// Architectural Feature Trap Register
// 是否支持64位kernel上运行32位的application
#ifdef CONFIG_COMPAT
msr hstr_el2, xzr // Disable CP15 traps to EL2
#endif
/* Stage-2 translation */
// Virtualization Translation Table Base Register
msr vttbr_el2, xzr
/* Hypervisor stub */
// 读取__hyp_stub_vectors相对于当前PC的地址,页对齐
adrp x0, __hyp_stub_vectors
// 处理页内偏移
add x0, x0, #:lo12:__hyp_stub_vectors
// 设置EL2的异常向量表的基地址
msr vbar_el2, x0
/* spsr */
mov x0, #(PSR_F_BIT | PSR_I_BIT | PSR_A_BIT | PSR_D_BIT |\
PSR_MODE_EL1h)
// 设置SPSR_EL2,Saved Program Status Register (EL2)的缺省值
msr spsr_el2, x0
// 设置Exception Link Register (EL2)的缺省值
msr elr_el2, lr
// This CPU booted in EL2
// 将BOOT_CPU_MODE_EL2保存到通用寄存器w20
mov w20, #BOOT_CPU_MODE_EL2
// eret指令是用来返回发生exception的现场,
//执行该指令会使得CPU返回EL1状态,并且将spsr_el2的值赋给PSTATE,elr_el2的值赋给pc
eret
ENDPROC(el2_setup)
补充知识
MRS Move to Register from State register, MSR Move from State register to Register, 分别用于对状态寄存器(AARCH64对应PSTATE,AARCH32对应CPSP)进行读写。
3. 总结
el2_setup主要做的工作是区分当前处于EL1还是EL2模式,根据所处模式的不同有不同的行为。
如果处于EL1模式,则设置大小端后返回;
如果处于EL2模式,设置大小端外,还做了其他一些操作,如清零计数器,设置GIC等。
参考文档
1.https://blog.csdn.net/xichangbao/article/details/51568782
2.https://www.cnblogs.com/smartjourneys/diary/2017/04/27/6774121.html
3.http://www.wowotech.net/armv8a_arch/arm64_initialize_1.html ARM64的启动过程之(一):内核第一个脚印
3.http://www.wowotech.net/armv8a_arch/create_page_tables.html ARM64的启动过程之(二):创建启动阶段的页表
4.http://www.wowotech.net/armv8a_arch/__cpu_setup.html ARM64的启动过程之(三):为打开MMU而进行的CPU初始化
5.http://www.wowotech.net/armv8a_arch/turn-on-mmu.html ARM64的启动过程之(四):打开MMU
6.https://blog.csdn.net/xichangbao/article/details/51568782 Kernel启动流程源码解析 1 head.S
7.https://blog.csdn.net/xichangbao/article/details/51605462 Kernel启动流程源码解析 2 head.S