Applies to commit 827ee5bffde826336c4a7f33109347c77a2cbbcf of the latest kvm-a15-v7 branch. This patch allows to safely run guests that use the VFP and NEON floating point extensions. When we switch to a guest, we save the host's state (registers with floating point data, configuration registers) and load those used by the guest. We can also handle versions of the extensions with 16 or 32 double registers, and also the registers introduced by the Common VFP Subarchitecture, as defined in the ARM Architecture Reference Manual, Issue C. Possible future improvements include letting QEMU fetch a copy of the registers, and lazy switching of the registers only when they are actually used. Signed-off-by: Antonios Motakis <a.motakis at virtualopensystems.com> --- arch/arm/include/asm/kvm_arm.h | 9 ++++ arch/arm/include/asm/kvm_host.h | 12 ++++++ arch/arm/kernel/asm-offsets.c | 2 + arch/arm/kvm/interrupts.S | 82 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 105 insertions(+), 0 deletions(-) diff --git a/arch/arm/include/asm/kvm_arm.h b/arch/arm/include/asm/kvm_arm.h index 1769187..d8fcc34 100644 --- a/arch/arm/include/asm/kvm_arm.h +++ b/arch/arm/include/asm/kvm_arm.h @@ -127,4 +127,13 @@ #define HSR_EC_DABT (0x24) #define HSR_EC_DABT_HYP (0x25) +/* FPEXC bits */ +#define FPEXC_EX (1 << 31) +#define FPEXC_EN (1 << 30) +#define FPEXC_FP2V (1 << 28) + +/* MVFR0 bits */ +#define MVFR0_A_SIMD_BIT (0) +#define MVFR0_A_SIMD_MASK (0xf << MVFR0_A_SIMD_BIT) + #endif /* __KVM_ARM_H__ */ diff --git a/arch/arm/include/asm/kvm_host.h b/arch/arm/include/asm/kvm_host.h index 44abdc8..71b92e6 100644 --- a/arch/arm/include/asm/kvm_host.h +++ b/arch/arm/include/asm/kvm_host.h @@ -85,12 +85,24 @@ enum cp15_regs { nr_cp15_regs }; +enum cp10_regs { + FPEXC, /* Floating Point Exception Control Register */ + FPSCR, /* Floating Point Status and Control Register */ + FPINST, /* Common VFP Subarchitecture Registers */ + FPINST2, + nr_cp10_regs +}; + struct kvm_vcpu_arch { struct kvm_vcpu_regs regs; /* System control coprocessor (cp15) */ u32 cp15[nr_cp15_regs]; + /* Floating point registers (VFP and Advanced SIMD/NEON) */ + u32 cp10[nr_cp10_regs]; + u32 cp11[64]; + /* Exception Information */ u32 hsr; /* Hyp Syndrom Register */ u32 hdfar; /* Hyp Data Fault Address Register */ diff --git a/arch/arm/kernel/asm-offsets.c b/arch/arm/kernel/asm-offsets.c index c8c1b91..5bd1849 100644 --- a/arch/arm/kernel/asm-offsets.c +++ b/arch/arm/kernel/asm-offsets.c @@ -161,6 +161,8 @@ int main(void) DEFINE(VCPU_TID_URW, offsetof(struct kvm_vcpu, arch.cp15[c13_TID_URW])); DEFINE(VCPU_TID_URO, offsetof(struct kvm_vcpu, arch.cp15[c13_TID_URO])); DEFINE(VCPU_TID_PRIV, offsetof(struct kvm_vcpu, arch.cp15[c13_TID_PRIV])); + DEFINE(VCPU_CP10, offsetof(struct kvm_vcpu, arch.cp10)); + DEFINE(VCPU_CP11, offsetof(struct kvm_vcpu, arch.cp11)); DEFINE(VCPU_REGS, offsetof(struct kvm_vcpu, arch.regs)); DEFINE(VCPU_USR_REGS, offsetof(struct kvm_vcpu, arch.regs.usr_regs)); DEFINE(VCPU_SVC_REGS, offsetof(struct kvm_vcpu, arch.regs.svc_regs)); diff --git a/arch/arm/kvm/interrupts.S b/arch/arm/kvm/interrupts.S index 0cf4965..550e8ce 100644 --- a/arch/arm/kvm/interrupts.S +++ b/arch/arm/kvm/interrupts.S @@ -236,6 +236,80 @@ ENTRY(__kvm_flush_vm_context) mcr p15, 0, r11, c10, c2, 1 @ NMRR .endm +.macro store_vfp_state vcpu=0, vcpup + mrc p10, 7, r2, cr8, cr0, 0 @ FPEXC + tst r2, #FPEXC_EN @ Check if floating point is enabled + beq 1f + + .if \vcpu == 0 + vpush {d0-d15} + .else + add r11, \vcpup, #VCPU_CP11 + vstm r11!, {d0-d15} + .endif + mrc p10, 7, r9, cr7, cr0, 0 @ MVFR0 + and r9, r9, #MVFR0_A_SIMD_MASK + cmp r9, #2 @ Check for 32 registers + .if \vcpu == 0 + vpusheq {d16-d31} + .else + vstmeq r11!, {d16-d31} + .endif + + mrc p10, 7, r3, cr1, cr0, 0 @ FPSCR + tst r2, #FPEXC_EX @ Check for VFP Subarchitecture + beq 1f + mrc p10, 7, r4, cr9, cr0, 0 @ FPINST + tst r2, #FPEXC_FP2V + beq 1f + mrc p10, 7, r5, cr10, cr0, 0 @ FPINST2 + +1: + .if \vcpu == 0 + push {r2-r5} + .else + add r10, \vcpup, #VCPU_CP10 + stm r10, {r2-r5} @ Save FPEXC, FPSCR, FPINST, FPINST2 + .endif +.endm + +.macro restore_vfp_state vcpu=0, vcpup + .if \vcpu == 0 + pop {r2-r5} + .else + add r10, \vcpup, #VCPU_CP10 + ldm r10, {r2-r5} @ Load FPEXC, FPSCR, FPINST, FPINST2 + .endif + + tst r2, #FPEXC_EN @ Check if floating point is enabled + beq 2f + + mcr p10, 7, r2, cr8, cr0, 0 @ FPEXC + mcr p10, 7, r3, cr1, cr0, 0 @ FPSCR + tst r2, #FPEXC_EX @ Check for VFP Subarchitecture + beq 1f + mcr p10, 7, r4, cr9, cr0, 0 @ FPINST + tst r2, #FPEXC_FP2V + beq 1f + mcr p10, 7, r5, cr10, cr0, 0 @ FPINST2 + +1: + .if \vcpu == 1 + add r11, \vcpup, #VCPU_CP11 + vldm r11!, {d0-d15} + .endif + mrc p10, 7, r9, cr7, cr0, 0 @ MVFR0 + and r9, r9, #MVFR0_A_SIMD_MASK + cmp r9, #2 @ Check for 32 registers + .if \vcpu == 0 + vpopeq {d16-d31} + vpop {d0-d15} + .else + vldmeq r11!, {d16-d31} + .endif +2: +.endm + /* Configures the HSTR (Hyp System Trap Register) on entry/return * (hardware reset value is 0) */ .macro set_hstr entry @@ -284,6 +358,10 @@ ENTRY(__kvm_vcpu_run) read_cp15_state write_cp15_state 1, r0 + @ Switch VFP/NEON hardware state to the guest's + store_vfp_state + restore_vfp_state 1, r0 + push {r0} @ Push the VCPU pointer @ Set up guest memory translation @@ -361,6 +439,10 @@ __kvm_vcpu_return: mov r3, #0 mcrr p15, 6, r2, r3, c2 @ Write VTTBR + @ Switch VFP/NEON hardware state to the host's + store_vfp_state 1, r1 + restore_vfp_state + @ Store guest CP15 state and restore host state read_cp15_state 1, r1 write_cp15_state -- 1.7.5.4