该汇编函数,用w0寄存器保存返回值,返回值有两种:
BOOT_CPU_MODE_EL1:表示当前CPU跳入内核时处于权限级EL1
BOOT_CPU_MODE_EL2:表示当前CPU跳入内核时处于权限级EL2
//////////////////////////////////////
1)在权限级ELx下,寄存器SPsel被设置为1时,堆栈寄存器SP就是用寄存器SP_ELx。
msr SPsel, #1
2)寄存器CurrentEL记录了当前CPU的权限级
mrs x0, CurrentEL
cmp x0, #CurrentEL_EL2
b.eq 1f
/*当前CPU权限级为EL1*/
1:
/*跳转到此处,表明当前CPU权限级为EL2*/
3)如果当前CPU权限级为EL1,就直接配置EL1的系统控制寄存器,el2_setup函数直接返回BOOT_CPU_MODE_EL1。
系统控制寄存器 sctlr_el1
ENDIAN_SET_EL1:关闭EL1/EL0的stage1阶段的地址翻译功能。
SCTLR_EL1_RES1:将保留位11/20/22/28/29全部设置为1(具体原因,手册不详)
如果当前CPU权限级为EL2,也需要配置EL2的系统控制寄存器,然后进行后面一大段的EL2初始化过程,最终el2_setup函数返回BOOT_CPU_MODE_EL2。
系统控制寄存器 sctlr_el2
ENDIAN_SET_EL2:关闭EL2的stage1阶段的地址翻译功能。
SCTLR_EL2_RES1:将保留位4/5/11/16/18/22/23/28/29全部设置为1(具体原因,手册不详)
/////////////////////////////////////////////////////
后面的讨论全部都基于当前CPU处于EL2状态来讨论,ARM64支持两种虚拟机方式:Hyp和VHE两种方式。
1)Hyp模式下,宿主OS处于EL1状态,客户OS也处于EL1状态,客户OS陷入到EL2后,需要宿主OS帮助完成相关功能后,再次陷入EL2然后返回客户OS,即CPU需要两次陷入和四次上下文切换才能完成一次对客户OS的服务。
2)HVE模式下,宿主OS处于EL2状态,客户OS处于EL1状态,客户OS可以直接陷入EL2的宿主OS完成相关功能,然后直接返回客户OS,即CPU只需要一次陷入和两次上下文切换就可以完成一次对客户OS的服务。
//////////////////////////////////////////////////////
1)内存模型特征寄存器id_aa64mmfrl_el1的[8..11]这四位如果不全为零,表示CPU支持VHE模式;并根据Hyp/VHE模式来设置虚拟i机配置寄存器hcr_el2。
当CPU处于Hyp模式时,将虚拟机配置寄存器hcr_el2设置为HCR_HOST_NVHE_FLAGS
当CPU处于VHE模式时,将虚拟机配置寄存器hcr_el2设置为HCR_HOST_VHE_FLAGS
#define HCR_HOST_NVHE_FLAGS (HCR_RW | HCR_API | HCR_APK)
#define HCR_HOST_VHE_FLAGS (HCR_RW | HCR_TGE | HCR_E2H)
HCR_RW[31]:EL1设置为aarch64模式。
HCR_TGE[27]:将原本路由到EL1各类中断全部路由到EL2来处理,包括快速中断FMO、中断IMO、错误中断AMO全部设置为1。
其他的各个域手册不详。
2)设置计数器-定时器虚拟机控制器寄存器cnthctl_el2,清零计数器-定时器虚拟偏移寄存器cntvoff_el2。
在Hyp模式下,允许非安全态的EL0/EL1可以访问四个定时器寄存器:CNTP_CVAL_EL0、CNTP_TVAL_EL0、CNTP_CTL_EL0和CNTPCT_EL0;VHE模式下,在后面的代码会对EL0进行相关设置。
在Hyp和VHE模式下,都将虚拟偏移寄存器清零了。
3)GICv3相关寄存器设置工作
首先判断GIC版本,处理器特征寄存器id_aa64pfr0_el1的[24...27]这四位,0001表示GICv3。
mrs x0, id_aa64pfr0_el1
ubfx x0, x0, #24, #4
cbz x0, 3f
/*当前处理器采用GICv3的中断控制器*/
3:
/* 如果直接跳转到这里,表示当前处理器没有采用GICv3版本的中断控制器*/
然后设置中断控制器系统寄存器使能寄存器ICC_SRE_EL2的SRE和Enable两个域:
[0]位, SRE系统寄存器接口使能位。
[3]位,Enable在非安全态下,EL1权限级访问寄存器ICC_SRE_EL1的使能位。
然后再次读出ICC_SRE_EL2,并判断SRE是否为零,如果不是零,就将中断控制器虚拟机控制寄存器ICH_HCR_EL2清零。中断控制器虚拟机控制寄存器ICH_HCR_EL2用于控制客户操作系统的中断环境。
注:此处我没有搞清楚SRE为零表示使能成功,还是非零表示使能成功。
注:寄存器ICH_HCR_EL2和寄存器ICC_SRE_EL2采用msr_s和mrs_s间接指令来访问,而不是常规的msr/mrs指令访问。
4)ID寄存器设置工作
CPU ID寄存器MIDR_EL1,CPU厂商号,体系结构,类型和版本号。
多CPU亲和属性寄存器MPIDR_EL1,主要用于软件调度提供依据。
虚拟机CPU ID寄存器VPIDR_EL2;虚拟机多CPU亲和属性寄存器VMPIDR_EL2。
将物理CPU两个属性直接复制给虚拟机CPU。
5)在32位兼容模式下,希望访问CP15不会陷入到EL2,所以就将HSTR_EL2清零。
6)判断CPU是否支持PMUv3,如果支持就获取事件计数器的个数,最多支持31个事件计数器。
mrs x1, id_aa64dfr0_el1
sbfx x0, x1, #8, #4
cmp x0, #1
b.lt 4f
mrs x0, pmcr_el0
ubfx x0, x0, #11, #5
4:
csel x3, xzr, x0, lt
注:在AArch64调试特征寄存器id_aa64dfr0_el1中, PMUver[8:11]位域描述了性能监控扩展版本。
0000 表示CPU不支持性能监控扩展;0001表示支持PMUv3;其他表示厂家自定义,但不是PMUv3。
性能监控器控制寄存器pmcr_el0的N[11..15]直接描述了事件计数器个数。
此时,寄存器x3包含了事件计数器的个数。
7)在AArch64调试特征寄存器id_aa64dfr0_el1中,[32..35]这四位判断CPU是否支持SPE:如果为零,表示不支持。(飞腾当前不支持,我们直接跳转7f)。
此时,,寄存器x3依然包含了事件计数器的个数。
8) 用寄存器x3配置mdcr_el2寄存器,主要是HPMN[0:4],即非安全态EL0/EL1可以访问的事件计数器个数。
9)内存模式特征寄存器id_aa64mmfr1_el1,通过ID_AA64MMFRI_LOR_SHIFT[16..19]判断EL0能否支持大小端两种模式:0000不支持两种模式;0001支持两种模式。如果支持两种模式就要将寄存器SYS_LORC_EL1清零。
10)将虚拟页表基址寄存器清零,寄存器vvtbr_el2用于指向非安全态EL0/EL1的stage2阶段页表基地址。
11)如果是VHE模式,直接返回w0为BOOT_CPU_MODE_EL2。如果是Hyp模式,就跳转到install_el2_stub汇编,再返回。
/////////////////////////////////////////
此时CPU都处于EL2模式,但是VHE返回方式和Hyp返回方式不同:
1)VHE模式返回到EL2模式,所以只需要常规返回,即设置返回值w0,用ret返回。
2)Hyp模式需要返回到EL1模式,在返回之前,需要设置好向量表,返回后的处理器状态,返回后的地址,以及返回值w0,用eret返回。
////////////////////////////////////////下面是Hyp模式的返回实现函数install_el2_stub
1)设置系统控制寄存器sctlr_el1,el2_setup函数最开始已经做过了,一模一样的。
2)设置体系结构特征陷入寄存器cptr_el2,该寄存器主要是控制FP和ASIMD运行相关的跟踪调试(CPACR和CPACR_EL1)寄存器访问是陷入EL2。
TCPAC[31] 0 表示访问这些寄存器不发生EL2自陷;
TTA[20] 0 表示访问跟踪相关系统寄存器不发生EL2自陷;
TFP[10] 0 表示FP和ASIMD指令不会发生EL2自陷。
3)判断是否支持SVE,如果支持SVE,就将寄存器cptr_el2的CPTR_EL2_TZ[8]位清零(前面置过1)(不发生协处理器SVE自陷到EL2),设置寄存器SYS_ZCR_EL2为ZCR_ELx_LEN_MASK.
AArch64处理器特征寄存器id_aa64pfr0_el1的 ID_AA64PFR0_SVE_SHIFT[32..35],为零表示CPU不支持SVE。
4)Hyp模式的返回前设置:包括向量基地址寄存器vbar_el2填写,返回后处理器状态寄存器spsr_el2设置,返回地址elr_el2,返回值w0,最后eret返回。
注:在Hyp模式下,向量表为__hyp_stub_vecotrs,这个向量表以后会被修改!
返回后的处理器状态为 PSR_F_BIT、PSR_I_BIT、PSR_A_BIT、PSR_D_BIT、PSR_MODE_EL1h。