KVM: PPC: booke: standard PPC floating point support
authorScott Wood <scottwood@freescale.com>
Tue, 20 Dec 2011 15:34:45 +0000 (15:34 +0000)
committerAvi Kivity <avi@redhat.com>
Sun, 8 Apr 2012 09:54:15 +0000 (12:54 +0300)
e500mc has a normal PPC FPU, rather than SPE which is found
on e500v1/v2.

Based on code from Liu Yu <yu.liu@freescale.com>.

Signed-off-by: Scott Wood <scottwood@freescale.com>
Signed-off-by: Alexander Graf <agraf@suse.de>
Signed-off-by: Avi Kivity <avi@redhat.com>
arch/powerpc/include/asm/switch_to.h
arch/powerpc/kvm/booke.c
arch/powerpc/kvm/booke.h

index caf82d0a00def0de572440332ce0c0ec3f507591..1622c356ba90cc6160e466006e0266c97aba89a6 100644 (file)
@@ -17,6 +17,7 @@ extern struct task_struct *_switch(struct thread_struct *prev,
                                   struct thread_struct *next);
 
 extern void giveup_fpu(struct task_struct *);
+extern void load_up_fpu(void);
 extern void disable_kernel_fp(void);
 extern void enable_kernel_fp(void);
 extern void flush_fp_to_thread(struct task_struct *);
index 75dbaeb2efa350061c9f4457b988da353ecfcc1d..0b77be187cf7740cc1cdff20f57bd1eee2cca5fc 100644 (file)
@@ -457,6 +457,11 @@ void kvmppc_core_prepare_to_enter(struct kvm_vcpu *vcpu)
 int kvmppc_vcpu_run(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu)
 {
        int ret;
+#ifdef CONFIG_PPC_FPU
+       unsigned int fpscr;
+       int fpexc_mode;
+       u64 fpr[32];
+#endif
 
        if (!vcpu->arch.sane) {
                kvm_run->exit_reason = KVM_EXIT_INTERNAL_ERROR;
@@ -479,7 +484,46 @@ int kvmppc_vcpu_run(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu)
        }
 
        kvm_guest_enter();
+
+#ifdef CONFIG_PPC_FPU
+       /* Save userspace FPU state in stack */
+       enable_kernel_fp();
+       memcpy(fpr, current->thread.fpr, sizeof(current->thread.fpr));
+       fpscr = current->thread.fpscr.val;
+       fpexc_mode = current->thread.fpexc_mode;
+
+       /* Restore guest FPU state to thread */
+       memcpy(current->thread.fpr, vcpu->arch.fpr, sizeof(vcpu->arch.fpr));
+       current->thread.fpscr.val = vcpu->arch.fpscr;
+
+       /*
+        * Since we can't trap on MSR_FP in GS-mode, we consider the guest
+        * as always using the FPU.  Kernel usage of FP (via
+        * enable_kernel_fp()) in this thread must not occur while
+        * vcpu->fpu_active is set.
+        */
+       vcpu->fpu_active = 1;
+
+       kvmppc_load_guest_fp(vcpu);
+#endif
+
        ret = __kvmppc_vcpu_run(kvm_run, vcpu);
+
+#ifdef CONFIG_PPC_FPU
+       kvmppc_save_guest_fp(vcpu);
+
+       vcpu->fpu_active = 0;
+
+       /* Save guest FPU state from thread */
+       memcpy(vcpu->arch.fpr, current->thread.fpr, sizeof(vcpu->arch.fpr));
+       vcpu->arch.fpscr = current->thread.fpscr.val;
+
+       /* Restore userspace FPU state from stack */
+       memcpy(current->thread.fpr, fpr, sizeof(current->thread.fpr));
+       current->thread.fpscr.val = fpscr;
+       current->thread.fpexc_mode = fpexc_mode;
+#endif
+
        kvm_guest_exit();
 
 out:
index d53bcf2558f50488b3debcdcfca2a64f6de8b6e7..62c4fe55d19ba771e11745ad8ae5a907cf9dc97b 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/types.h>
 #include <linux/kvm_host.h>
 #include <asm/kvm_ppc.h>
+#include <asm/switch_to.h>
 #include "timing.h"
 
 /* interrupt priortity ordering */
@@ -96,4 +97,34 @@ enum int_class {
 
 void kvmppc_set_pending_interrupt(struct kvm_vcpu *vcpu, enum int_class type);
 
+/*
+ * Load up guest vcpu FP state if it's needed.
+ * It also set the MSR_FP in thread so that host know
+ * we're holding FPU, and then host can help to save
+ * guest vcpu FP state if other threads require to use FPU.
+ * This simulates an FP unavailable fault.
+ *
+ * It requires to be called with preemption disabled.
+ */
+static inline void kvmppc_load_guest_fp(struct kvm_vcpu *vcpu)
+{
+#ifdef CONFIG_PPC_FPU
+       if (vcpu->fpu_active && !(current->thread.regs->msr & MSR_FP)) {
+               load_up_fpu();
+               current->thread.regs->msr |= MSR_FP;
+       }
+#endif
+}
+
+/*
+ * Save guest vcpu FP state into thread.
+ * It requires to be called with preemption disabled.
+ */
+static inline void kvmppc_save_guest_fp(struct kvm_vcpu *vcpu)
+{
+#ifdef CONFIG_PPC_FPU
+       if (vcpu->fpu_active && (current->thread.regs->msr & MSR_FP))
+               giveup_fpu(current);
+#endif
+}
 #endif /* __KVM_BOOKE_H__ */