ARM: KVM: Add VFP save/restore
authorMarc Zyngier <marc.zyngier@arm.com>
Mon, 4 Jan 2016 15:41:51 +0000 (15:41 +0000)
committerMarc Zyngier <marc.zyngier@arm.com>
Mon, 29 Feb 2016 18:34:13 +0000 (18:34 +0000)
This is almost a copy/paste of the existing version, with a couple
of subtle differences:
- Only write to FPEXC once on the save path
- Add an isb when enabling VFP access

The patch also defines a few sysreg accessors and a __vfp_enabled
predicate that test the VFP trapping state.

Reviewed-by: Christoffer Dall <christoffer.dall@linaro.org>
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
arch/arm/kvm/hyp/Makefile
arch/arm/kvm/hyp/hyp.h
arch/arm/kvm/hyp/vfp.S [new file with mode: 0644]

index d8acbb691249ec7585959240ceab32d2e925e512..5a45f4c21f83ee674c0893607138673b868ca252 100644 (file)
@@ -6,3 +6,4 @@ obj-$(CONFIG_KVM_ARM_HOST) += tlb.o
 obj-$(CONFIG_KVM_ARM_HOST) += cp15-sr.o
 obj-$(CONFIG_KVM_ARM_HOST) += timer-sr.o
 obj-$(CONFIG_KVM_ARM_HOST) += vgic-v2-sr.o
+obj-$(CONFIG_KVM_ARM_HOST) += vfp.o
index 7eb1c21d2d2110d9b69c6e336dce6d24a402dee4..dce0f7305cf57eefeaac20a5658ec0b48b325a6a 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/compiler.h>
 #include <linux/kvm_host.h>
 #include <asm/kvm_mmu.h>
+#include <asm/vfp.h>
 
 #define __hyp_text __section(.hyp.text) notrace
 
@@ -31,6 +32,8 @@
        "mrc", "mcr", __stringify(p15, Op1, %0, CRn, CRm, Op2), u32
 #define __ACCESS_CP15_64(Op1, CRm)             \
        "mrrc", "mcrr", __stringify(p15, Op1, %Q0, %R0, CRm), u64
+#define __ACCESS_VFP(CRn)                      \
+       "mrc", "mcr", __stringify(p10, 7, %0, CRn, cr0, 0), u32
 
 #define __write_sysreg(v, r, w, c, t)  asm volatile(w " " c : : "r" ((t)(v)))
 #define write_sysreg(v, ...)           __write_sysreg(v, __VA_ARGS__)
@@ -53,6 +56,7 @@
 #define VMPIDR         __ACCESS_CP15(c0, 4, c0, 5)
 #define SCTLR          __ACCESS_CP15(c1, 0, c0, 0)
 #define CPACR          __ACCESS_CP15(c1, 0, c0, 2)
+#define HCPTR          __ACCESS_CP15(c1, 4, c1, 2)
 #define TTBCR          __ACCESS_CP15(c2, 0, c0, 2)
 #define DACR           __ACCESS_CP15(c3, 0, c0, 0)
 #define DFSR           __ACCESS_CP15(c5, 0, c0, 0)
@@ -77,6 +81,8 @@
 #define CNTV_CTL       __ACCESS_CP15(c14, 0, c3, 1)
 #define CNTHCTL                __ACCESS_CP15(c14, 4, c1, 0)
 
+#define VFP_FPEXC      __ACCESS_VFP(FPEXC)
+
 void __timer_save_state(struct kvm_vcpu *vcpu);
 void __timer_restore_state(struct kvm_vcpu *vcpu);
 
@@ -86,4 +92,11 @@ void __vgic_v2_restore_state(struct kvm_vcpu *vcpu);
 void __sysreg_save_state(struct kvm_cpu_context *ctxt);
 void __sysreg_restore_state(struct kvm_cpu_context *ctxt);
 
+void asmlinkage __vfp_save_state(struct vfp_hard_struct *vfp);
+void asmlinkage __vfp_restore_state(struct vfp_hard_struct *vfp);
+static inline bool __vfp_enabled(void)
+{
+       return !(read_sysreg(HCPTR) & (HCPTR_TCP(11) | HCPTR_TCP(10)));
+}
+
 #endif /* __ARM_KVM_HYP_H__ */
diff --git a/arch/arm/kvm/hyp/vfp.S b/arch/arm/kvm/hyp/vfp.S
new file mode 100644 (file)
index 0000000..7c297e8
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2012 - Virtual Open Systems and Columbia University
+ * Author: Christoffer Dall <c.dall@virtualopensystems.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/linkage.h>
+#include <asm/vfpmacros.h>
+
+       .text
+       .pushsection    .hyp.text, "ax"
+
+/* void __vfp_save_state(struct vfp_hard_struct *vfp); */
+ENTRY(__vfp_save_state)
+       push    {r4, r5}
+       VFPFMRX r1, FPEXC
+
+       @ Make sure *really* VFP is enabled so we can touch the registers.
+       orr     r5, r1, #FPEXC_EN
+       tst     r5, #FPEXC_EX           @ Check for VFP Subarchitecture
+       bic     r5, r5, #FPEXC_EX       @ FPEXC_EX disable
+       VFPFMXR FPEXC, r5
+       isb
+
+       VFPFMRX r2, FPSCR
+       beq     1f
+
+       @ If FPEXC_EX is 0, then FPINST/FPINST2 reads are upredictable, so
+       @ we only need to save them if FPEXC_EX is set.
+       VFPFMRX r3, FPINST
+       tst     r5, #FPEXC_FP2V
+       VFPFMRX r4, FPINST2, ne         @ vmrsne
+1:
+       VFPFSTMIA r0, r5                @ Save VFP registers
+       stm     r0, {r1-r4}             @ Save FPEXC, FPSCR, FPINST, FPINST2
+       pop     {r4, r5}
+       bx      lr
+ENDPROC(__vfp_save_state)
+
+/* void __vfp_restore_state(struct vfp_hard_struct *vfp);
+ * Assume FPEXC_EN is on and FPEXC_EX is off */
+ENTRY(__vfp_restore_state)
+       VFPFLDMIA r0, r1                @ Load VFP registers
+       ldm     r0, {r0-r3}             @ Load FPEXC, FPSCR, FPINST, FPINST2
+
+       VFPFMXR FPSCR, r1
+       tst     r0, #FPEXC_EX           @ Check for VFP Subarchitecture
+       beq     1f
+       VFPFMXR FPINST, r2
+       tst     r0, #FPEXC_FP2V
+       VFPFMXR FPINST2, r3, ne
+1:
+       VFPFMXR FPEXC, r0               @ FPEXC (last, in case !EN)
+       bx      lr
+ENDPROC(__vfp_restore_state)
+
+       .popsection