首页 > 编程知识 正文

飞腾cpu 虚拟机,飞腾cpu x86

时间:2023-05-06 16:04:42 阅读:280295 作者:1912

飞腾CPU虚拟化相关代码分析(一)—— 函数el2_setup

函数el2_setup是ARM64体系结构下Linux内核运行的第一个和虚拟化相关的函数。

相关概念

ARM64支持两种虚拟方式:Hyp和VHE两种方式。

传统分裂模式Hyp:

宿主OS内核处于EL1状态,客户OS内核也处于EL1状态,CPU需要两次陷入和四次上下文切换才能完成一次对客户OS的服务。

虚拟主机扩展模式VHE(当前飞腾CPU还没有支持VHE模式):

宿主OS内核处于EL2状态,客户OS内核处于EL1状态,CPU只需要一次陷入和两次上下文切换就可以完成一次对客户OS的服务。

函数输入输出描述 输入 MMU关闭,数据cache关闭。x21保存了设备树起始地址,在启动阶段不能被占用。u64 __cacheline_aligned boot_args[4]保存了寄存器x0/1/2/3
1)boot_args[0]保存了设备树起始地址。
2)boot_args[1]-[3]如果非零,表示固件不是符合标准的UEFI,很可能就是uboot。 输出 寄存器w0返回,刚刚进入内核时,CPU的权限级
1) BOOT_CPU_MODE_EL1:表示当前CPU跳入内核时处于权限级EL1;函数正常返回,无权限级切换。
2)BOOT_CPU_MODE_EL2:表示当前CPU跳入内核时处于权限级EL2。此次内核启动可以支持KVM虚拟机,Hyp模式下函数异常返回,需要将CPU切换回EL1权限级;如果VHE模式,函数正常返回,CPU保持EL2权限级。 函数分析 堆栈寄存器的选择

msr SPsel, #1

判断当前CPU权限级

mrs x0, CurrentEL
cmp x0, #CurrentEL_EL2
b.eq 1f
/*不跳转,说明当前为EL1,内核后续不能支持虚拟化*/
1: /*跳转,说明当前为EL2,el2_setup函数为后续虚拟化做好铺垫*/

如果当前CPU权限级已经是EL1,通过设置sctlr_el1,将CPU的EL0/1设置为小端模式,就直接调用ret指令返回,返回地址为lr寄存器所包含的地址(因此el2_setup函数需要用bl调用)。

mov_q x0, (SCTLR_EL1_RES1 | ENDIAN_SET_EL1)
msr sctlr_el1, x0
mov w0, #BOOT_CPU_MODE_EL1
isb
ret

我们现在在权限级EL2级。 通过设置sctlr_el2,将CPU的EL2设置为小端模式。通过读取id_aa64mmfr1_el1,判断CPU是支持虚拟主机扩展VHE模式,还是传统的分离Hyp模式。
mrs x2, id_aa64mmfr1_el1
ubfx x2, x2, #8, #4 设置Hypervisor配置寄存器hcr_el2。 Hyp模式
#define HCR_HOST_NVHE_FLAGS (HCR_RW | HCR_API | HCR_APK)
mov_q x0, HCR_HOST_NVHE_FLAGS
msr hcr_el2, x0VHE模式
#define HCR_HOST_VHE_FLAGS (HCR_RW | HCR_TGE | HCR_E2H)
mov_q x0, HCR_HOST_VHE_FLAGS
msr hcr_el2, x0

这以后,寄存器x2,零表示Hyp模式;非零表示VHE模式

设置定时器相关寄存器

将定时器虚拟偏移量清零

Hyp模式
/*启动计数器事件流*/
mrs x0, cnthctl_el2
orr x0, x0, #3
msr cnthctl_el2, x0
/*虚拟偏移量清零*/
msr cntvoff_el2, xzrVHE模式
msr cntvoff_el2, xzr GICv3中断控制器相关设置 中断控制器类型判断
mrs x1, id_aa64pfr0_el1
ubfx x0, x0, #24, #4
cbz x0, 3f
/*当中断控制是GICv3时,不发生跳转*/
......
3: /*如果直接跳转到这里,说明CPU不支持GICv3*/如果是GICv3,设置SYS_ICC_SRE_EL2、SYS_ICH_HCR_EL2寄存器,这两个寄存器是GICv3的CPU接口寄存器。
/*不允许内核访问ICC_SRE_EL1;设置采用MSR/MRS方式访问ICC_*相关寄存器*/
mrs_s x0, SYS_ICC_SRE_EL2
orr x0, x0, #ICC_SRE_EL2_SRE
orr x0, x0, #ICC_SRE_EL2_ENABLE
msr_s SYS_ICC_SRE_EL2, x0
isb
mrs_s x0, SYS_ICC_SRE_EL2/*写之后再读,确认是否真正设置成功*/
tbz x0, #0, 3f
/*如果SYS_ICC_SRE_EL2寄存器的SRE位不为0,表示设置成功*/
msr_s SYS_ICH_HCR_EL2, xzr
3: /*如果设置不成功,就跳转到此处运行*/
msr_s SYS_ICH_HCR_EL2, xzr /*关闭所有虚拟中断和维护中断*/ 根据物理CPU的ID寄存器和亲合属性寄存器,来设置虚拟CPU对应的寄存器。

/*ID寄存器*/
mrs x0, midr_el1
msr vpidr_el1, x0
/*亲合属性寄存器*/
mrs x1, mpidr_el1
msr vmpidr_el2, x1

将Hypervisor系统陷入寄存器HSTR_EL2清零。一般情况下,当客户虚拟机是AArch32位,会有Thumb和协处理器方式,不希望在访问相关寄存器陷入到EL2中。

msr hstr_el2, xzr

获取事件计数器数量。通过AArch64调试特征寄存器0,id_aa64dfr0_el1,判断CPU是否可以访问PMU寄存器,如果可以就获取事件计数器数量,否则直接为零。

mrs x1, id_aa64dfr0_el1
sbfx x0, x1, #8, #4
cmp x0, #1
b.lt 4f
/*id_aa64dfr0_el1的11:8位小于0b0001,不跳转表示,支持性能监控扩展系统寄存器*/
/*当支持PMU时,这里会获取事件计数器数量*/
mrs x0, pmcr_el0
ubfx x0, x0, #11, #5
4:/*跳转,表示支持*/
csel x3, xze, x0, lt /*这条指令的直接含义是:当前面cmp x0, #1和b.lt 跳转成功时,x3直接赋值为零,否则赋值为x0 */

此时,寄存器x3的低5位记录了事件计数器的数量,如果该数量为零,表示不支持PMU

也是通过AArch64调试特征寄存器0,id_aa64dfr0_el1,判断CPU是否可以访问SPE寄存器。

ubfx x0, x1, #32, #4
cbz x0, 7f
/*不跳转,表示支持PMS*/
/*飞腾还没有支持,这一段我们先不分析(?)*/
7: /*直接跳转到这里,表示不支持SPE*/

根据前面的第10和第11步,已经完成对寄存器x3的初始化,然后直接赋值给寄存器mdcr_el2。

msr mdcr_el2, x3

内存模式特征寄存器id_aa64mmfr1_el1,这个寄存器手册上还没有说明。

mrs x1, id_aa64mmfr1_el1
ubfx x0, x1, #ID_AA64MMFR1_LOR_SHIFT, 4
cbz x0, 1f
msr_s SYS_LORC_EL1, zxr
1:

将非安全态EL0/EL1的stage2阶段的虚拟页表基址寄存器清零,即将寄存器vttbr_el2清零。

msr vttbr_el2, xzr

从下面代码开始,VHE模式和Hyp模式走向不同的分支。

cbz x2, install_el2_stub
/*VHE模式返回代码*/
install_el2_stub:
/*Hyp模式设置和返回代码*/

如果是VHE模式,直接返回w0为BOOT_CPU_MODE_EL2。由于进入内核时,CPU处于EL2,所以直接调用ret指令返回,CPU仍然是EL2。

mov w0, #BOOT_CPU_MODE_EL2
isb
ret

install_el2_stub是Hyp模式相关设置和返回代码。通过设置sctlr_el1,将CPU的EL0/1设置为小端模式。

mov_q x0, (SCTLR_EL1_RES1 | ENDIAN_SET_EL1)
msr sctlr_el1, x0

设置寄存器cptr_el2,浮点和ASIMD相关寄存器的访问不陷入EL2

mov x0, #0x33ff
msr cptr_el2, x0

先判断是否支持SVE指令,如果支持设置相关寄存器访问不陷入EL2,而且设置在EL1可以进行全向量长度支持。 SVE指令支持判断

mrs x1, id_aa64pfr0_el1
ubfx x1, x1, #ID_AA64PFR0_SVE_SHIFT, #4
cbz x1, 7f
/*支持SVE指令*/
7: /**/
2.支持设置相关寄存器访问不陷入EL2
bic x0, x0, #CPTR_EL2_TZ
msr cptr_el2, x0
isb

设置全向量长度支持

mov x1, #ZCR_ELx_LEN_MASK
msr_s SYS_ZCR_EL2, x1

设置EL2异常向量表基地址寄存器(参见《飞腾CPU虚拟化相关代码分析(二)之EL2异常向量表》)

adr_l x0, __hyp_stub_vectors /*装载64位地址*/
msr vbar_el2, x0
__hyp_stub_vectors是EL2异常向量表,寄存器vbar_el2是EL2级异常向量表基地址寄存器。

Hyp模式的el2_setup函数返回

mov x0, #(PSR_F_BIT | PSR_I_BIT | PSR_A_BIT | PSR_D_BIT | PSR_MODE_EL1h)
msr spsr_el2, x0
msr elr_el2, lr
mov w0, #BOOT_CPU_MODE_EL2
eret

设置寄存器spsr_el2是为了返回设置CPU返回后的EL1状态,el1h说明了返回EL1状态,EL1堆栈寄存器采用sp_el1,F/I/A/D表明各种异常都屏蔽。elr_el2异常链接地址寄存器,也设置为当前链接寄存器lr地址,即即使从EL2返回到EL1,返回地址不变。eret指令为异常返回指令。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。