[Android-virt] [RFC PATCH] ARM: KVM: Handle VFP/NEON state switching

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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



[Index of Archives]     [Linux KVM]     [Spice Development]     [Libvirt]     [Libvirt Users]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux