KVM: PPC: Add helpers to call FPU instructions
authorAlexander Graf <agraf@suse.de>
Fri, 19 Feb 2010 10:00:40 +0000 (11:00 +0100)
committerAvi Kivity <avi@redhat.com>
Sun, 25 Apr 2010 09:35:15 +0000 (12:35 +0300)
To emulate paired single instructions, we need to be able to call FPU
operations from within the kernel. Since we don't want gcc to spill
arbitrary FPU code everywhere, we tell it to use a soft fpu.

Since we know we can really call the FPU in safe areas, let's also add
some calls that we can later use to actually execute real world FPU
operations on the host's FPU.

Signed-off-by: Alexander Graf <agraf@suse.de>
Signed-off-by: Avi Kivity <avi@redhat.com>
arch/powerpc/include/asm/kvm_fpu.h [new file with mode: 0644]
arch/powerpc/kernel/ppc_ksyms.c
arch/powerpc/kvm/Makefile
arch/powerpc/kvm/fpu.S [new file with mode: 0644]

diff --git a/arch/powerpc/include/asm/kvm_fpu.h b/arch/powerpc/include/asm/kvm_fpu.h
new file mode 100644 (file)
index 0000000..94f05de
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * 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, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ * Copyright Novell Inc. 2010
+ *
+ * Authors: Alexander Graf <agraf@suse.de>
+ */
+
+#ifndef __ASM_KVM_FPU_H__
+#define __ASM_KVM_FPU_H__
+
+#include <linux/types.h>
+
+extern void fps_fres(struct thread_struct *t, u32 *dst, u32 *src1);
+extern void fps_frsqrte(struct thread_struct *t, u32 *dst, u32 *src1);
+extern void fps_fsqrts(struct thread_struct *t, u32 *dst, u32 *src1);
+
+extern void fps_fadds(struct thread_struct *t, u32 *dst, u32 *src1, u32 *src2);
+extern void fps_fdivs(struct thread_struct *t, u32 *dst, u32 *src1, u32 *src2);
+extern void fps_fmuls(struct thread_struct *t, u32 *dst, u32 *src1, u32 *src2);
+extern void fps_fsubs(struct thread_struct *t, u32 *dst, u32 *src1, u32 *src2);
+
+extern void fps_fmadds(struct thread_struct *t, u32 *dst, u32 *src1, u32 *src2,
+                      u32 *src3);
+extern void fps_fmsubs(struct thread_struct *t, u32 *dst, u32 *src1, u32 *src2,
+                      u32 *src3);
+extern void fps_fnmadds(struct thread_struct *t, u32 *dst, u32 *src1, u32 *src2,
+                       u32 *src3);
+extern void fps_fnmsubs(struct thread_struct *t, u32 *dst, u32 *src1, u32 *src2,
+                       u32 *src3);
+extern void fps_fsel(struct thread_struct *t, u32 *dst, u32 *src1, u32 *src2,
+                    u32 *src3);
+
+#define FPD_ONE_IN(name) extern void fpd_ ## name(u64 *fpscr, u32 *cr, \
+                               u64 *dst, u64 *src1);
+#define FPD_TWO_IN(name) extern void fpd_ ## name(u64 *fpscr, u32 *cr, \
+                               u64 *dst, u64 *src1, u64 *src2);
+#define FPD_THREE_IN(name) extern void fpd_ ## name(u64 *fpscr, u32 *cr, \
+                               u64 *dst, u64 *src1, u64 *src2, u64 *src3);
+
+extern void fpd_fcmpu(u64 *fpscr, u32 *cr, u64 *src1, u64 *src2);
+extern void fpd_fcmpo(u64 *fpscr, u32 *cr, u64 *src1, u64 *src2);
+
+FPD_ONE_IN(fsqrts)
+FPD_ONE_IN(frsqrtes)
+FPD_ONE_IN(fres)
+FPD_ONE_IN(frsp)
+FPD_ONE_IN(fctiw)
+FPD_ONE_IN(fctiwz)
+FPD_ONE_IN(fsqrt)
+FPD_ONE_IN(fre)
+FPD_ONE_IN(frsqrte)
+FPD_ONE_IN(fneg)
+FPD_ONE_IN(fabs)
+FPD_TWO_IN(fadds)
+FPD_TWO_IN(fsubs)
+FPD_TWO_IN(fdivs)
+FPD_TWO_IN(fmuls)
+FPD_TWO_IN(fcpsgn)
+FPD_TWO_IN(fdiv)
+FPD_TWO_IN(fadd)
+FPD_TWO_IN(fmul)
+FPD_TWO_IN(fsub)
+FPD_THREE_IN(fmsubs)
+FPD_THREE_IN(fmadds)
+FPD_THREE_IN(fnmsubs)
+FPD_THREE_IN(fnmadds)
+FPD_THREE_IN(fsel)
+FPD_THREE_IN(fmsub)
+FPD_THREE_IN(fmadd)
+FPD_THREE_IN(fnmsub)
+FPD_THREE_IN(fnmadd)
+
+#endif
index ab3e392..58fdb3a 100644 (file)
@@ -101,6 +101,8 @@ EXPORT_SYMBOL(pci_dram_offset);
 EXPORT_SYMBOL(start_thread);
 EXPORT_SYMBOL(kernel_thread);
 
+EXPORT_SYMBOL_GPL(cvt_df);
+EXPORT_SYMBOL_GPL(cvt_fd);
 EXPORT_SYMBOL(giveup_fpu);
 #ifdef CONFIG_ALTIVEC
 EXPORT_SYMBOL(giveup_altivec);
index 56484d6..e575cfd 100644 (file)
@@ -40,6 +40,7 @@ kvm-objs-$(CONFIG_KVM_E500) := $(kvm-e500-objs)
 
 kvm-book3s_64-objs := \
        $(common-objs-y) \
+       fpu.o \
        book3s.o \
        book3s_64_emulate.o \
        book3s_64_interrupts.o \
diff --git a/arch/powerpc/kvm/fpu.S b/arch/powerpc/kvm/fpu.S
new file mode 100644 (file)
index 0000000..2b340a3
--- /dev/null
@@ -0,0 +1,273 @@
+/*
+ *  FPU helper code to use FPU operations from inside the kernel
+ *
+ *    Copyright (C) 2010 Alexander Graf (agraf@suse.de)
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version
+ *  2 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <asm/reg.h>
+#include <asm/page.h>
+#include <asm/mmu.h>
+#include <asm/pgtable.h>
+#include <asm/cputable.h>
+#include <asm/cache.h>
+#include <asm/thread_info.h>
+#include <asm/ppc_asm.h>
+#include <asm/asm-offsets.h>
+
+/* Instructions operating on single parameters */
+
+/*
+ * Single operation with one input operand
+ *
+ * R3 = (double*)&fpscr
+ * R4 = (short*)&result
+ * R5 = (short*)&param1
+ */
+#define FPS_ONE_IN(name)                                       \
+_GLOBAL(fps_ ## name);                                                 \
+       lfd     0,0(r3);                /* load up fpscr value */       \
+       MTFSF_L(0);                                                     \
+       lfs     0,0(r5);                                                \
+                                                                       \
+       name    0,0;                                                    \
+                                                                       \
+       stfs    0,0(r4);                                                \
+       mffs    0;                                                      \
+       stfd    0,0(r3);        /* save new fpscr value */      \
+       blr
+
+/*
+ * Single operation with two input operands
+ *
+ * R3 = (double*)&fpscr
+ * R4 = (short*)&result
+ * R5 = (short*)&param1
+ * R6 = (short*)&param2
+ */
+#define FPS_TWO_IN(name)                                       \
+_GLOBAL(fps_ ## name);                                                 \
+       lfd     0,0(r3);                /* load up fpscr value */       \
+       MTFSF_L(0);                                                     \
+       lfs     0,0(r5);                                                \
+       lfs     1,0(r6);                                                \
+                                                                       \
+       name    0,0,1;                                                  \
+                                                                       \
+       stfs    0,0(r4);                                                \
+       mffs    0;                                                      \
+       stfd    0,0(r3);                /* save new fpscr value */      \
+       blr
+
+/*
+ * Single operation with three input operands
+ *
+ * R3 = (double*)&fpscr
+ * R4 = (short*)&result
+ * R5 = (short*)&param1
+ * R6 = (short*)&param2
+ * R7 = (short*)&param3
+ */
+#define FPS_THREE_IN(name)                                     \
+_GLOBAL(fps_ ## name);                                                 \
+       lfd     0,0(r3);                /* load up fpscr value */       \
+       MTFSF_L(0);                                                     \
+       lfs     0,0(r5);                                                \
+       lfs     1,0(r6);                                                \
+       lfs     2,0(r7);                                                \
+                                                                       \
+       name    0,0,1,2;                                                \
+                                                                       \
+       stfs    0,0(r4);                                                \
+       mffs    0;                                                      \
+       stfd    0,0(r3);                /* save new fpscr value */      \
+       blr
+
+FPS_ONE_IN(fres)
+FPS_ONE_IN(frsqrte)
+FPS_ONE_IN(fsqrts)
+FPS_TWO_IN(fadds)
+FPS_TWO_IN(fdivs)
+FPS_TWO_IN(fmuls)
+FPS_TWO_IN(fsubs)
+FPS_THREE_IN(fmadds)
+FPS_THREE_IN(fmsubs)
+FPS_THREE_IN(fnmadds)
+FPS_THREE_IN(fnmsubs)
+FPS_THREE_IN(fsel)
+
+
+/* Instructions operating on double parameters */
+
+/*
+ * Beginning of double instruction processing
+ *
+ * R3 = (double*)&fpscr
+ * R4 = (u32*)&cr
+ * R5 = (double*)&result
+ * R6 = (double*)&param1
+ * R7 = (double*)&param2 [load_two]
+ * R8 = (double*)&param3 [load_three]
+ * LR = instruction call function
+ */
+fpd_load_three:
+       lfd     2,0(r8)                 /* load param3 */
+fpd_load_two:
+       lfd     1,0(r7)                 /* load param2 */
+fpd_load_one:
+       lfd     0,0(r6)                 /* load param1 */
+fpd_load_none:
+       lfd     3,0(r3)                 /* load up fpscr value */
+       MTFSF_L(3)
+       lwz     r6, 0(r4)               /* load cr */
+       mtcr    r6
+       blr
+
+/*
+ * End of double instruction processing
+ *
+ * R3 = (double*)&fpscr
+ * R4 = (u32*)&cr
+ * R5 = (double*)&result
+ * LR = caller of instruction call function
+ */
+fpd_return:
+       mfcr    r6
+       stfd    0,0(r5)                 /* save result */
+       mffs    0
+       stfd    0,0(r3)                 /* save new fpscr value */
+       stw     r6,0(r4)                /* save new cr value */
+       blr
+
+/*
+ * Double operation with no input operand
+ *
+ * R3 = (double*)&fpscr
+ * R4 = (u32*)&cr
+ * R5 = (double*)&result
+ */
+#define FPD_NONE_IN(name)                                              \
+_GLOBAL(fpd_ ## name);                                                 \
+       mflr    r12;                                                    \
+       bl      fpd_load_none;                                          \
+       mtlr    r12;                                                    \
+                                                                       \
+       name.   0;                      /* call instruction */          \
+       b       fpd_return
+
+/*
+ * Double operation with one input operand
+ *
+ * R3 = (double*)&fpscr
+ * R4 = (u32*)&cr
+ * R5 = (double*)&result
+ * R6 = (double*)&param1
+ */
+#define FPD_ONE_IN(name)                                               \
+_GLOBAL(fpd_ ## name);                                                 \
+       mflr    r12;                                                    \
+       bl      fpd_load_one;                                           \
+       mtlr    r12;                                                    \
+                                                                       \
+       name.   0,0;                    /* call instruction */          \
+       b       fpd_return
+
+/*
+ * Double operation with two input operands
+ *
+ * R3 = (double*)&fpscr
+ * R4 = (u32*)&cr
+ * R5 = (double*)&result
+ * R6 = (double*)&param1
+ * R7 = (double*)&param2
+ * R8 = (double*)&param3
+ */
+#define FPD_TWO_IN(name)                                               \
+_GLOBAL(fpd_ ## name);                                                 \
+       mflr    r12;                                                    \
+       bl      fpd_load_two;                                           \
+       mtlr    r12;                                                    \
+                                                                       \
+       name.   0,0,1;                  /* call instruction */          \
+       b       fpd_return
+
+/*
+ * CR Double operation with two input operands
+ *
+ * R3 = (double*)&fpscr
+ * R4 = (u32*)&cr
+ * R5 = (double*)&param1
+ * R6 = (double*)&param2
+ * R7 = (double*)&param3
+ */
+#define FPD_TWO_IN_CR(name)                                            \
+_GLOBAL(fpd_ ## name);                                                 \
+       lfd     1,0(r6);                /* load param2 */               \
+       lfd     0,0(r5);                /* load param1 */               \
+       lfd     3,0(r3);                /* load up fpscr value */       \
+       MTFSF_L(3);                                                     \
+       lwz     r6, 0(r4);              /* load cr */                   \
+       mtcr    r6;                                                     \
+                                                                       \
+       name    0,0,1;                  /* call instruction */          \
+       mfcr    r6;                                                     \
+       mffs    0;                                                      \
+       stfd    0,0(r3);                /* save new fpscr value */      \
+       stw     r6,0(r4);               /* save new cr value */         \
+       blr
+
+/*
+ * Double operation with three input operands
+ *
+ * R3 = (double*)&fpscr
+ * R4 = (u32*)&cr
+ * R5 = (double*)&result
+ * R6 = (double*)&param1
+ * R7 = (double*)&param2
+ * R8 = (double*)&param3
+ */
+#define FPD_THREE_IN(name)                                             \
+_GLOBAL(fpd_ ## name);                                                 \
+       mflr    r12;                                                    \
+       bl      fpd_load_three;                                         \
+       mtlr    r12;                                                    \
+                                                                       \
+       name.   0,0,1,2;                /* call instruction */          \
+       b       fpd_return
+
+FPD_ONE_IN(fsqrts)
+FPD_ONE_IN(frsqrtes)
+FPD_ONE_IN(fres)
+FPD_ONE_IN(frsp)
+FPD_ONE_IN(fctiw)
+FPD_ONE_IN(fctiwz)
+FPD_ONE_IN(fsqrt)
+FPD_ONE_IN(fre)
+FPD_ONE_IN(frsqrte)
+FPD_ONE_IN(fneg)
+FPD_ONE_IN(fabs)
+FPD_TWO_IN(fadds)
+FPD_TWO_IN(fsubs)
+FPD_TWO_IN(fdivs)
+FPD_TWO_IN(fmuls)
+FPD_TWO_IN_CR(fcmpu)
+FPD_TWO_IN(fcpsgn)
+FPD_TWO_IN(fdiv)
+FPD_TWO_IN(fadd)
+FPD_TWO_IN(fmul)
+FPD_TWO_IN_CR(fcmpo)
+FPD_TWO_IN(fsub)
+FPD_THREE_IN(fmsubs)
+FPD_THREE_IN(fmadds)
+FPD_THREE_IN(fnmsubs)
+FPD_THREE_IN(fnmadds)
+FPD_THREE_IN(fsel)
+FPD_THREE_IN(fmsub)
+FPD_THREE_IN(fmadd)
+FPD_THREE_IN(fnmsub)
+FPD_THREE_IN(fnmadd)