dect
/
linux-2.6
Archived
13
0
Fork 0

KVM: PPC: Add helpers to call FPU instructions

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>
This commit is contained in:
Alexander Graf 2010-02-19 11:00:40 +01:00 committed by Avi Kivity
parent aba3bd7ffe
commit 963cf3dc63
4 changed files with 361 additions and 0 deletions

View File

@ -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

View 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);

View 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 \

273
arch/powerpc/kvm/fpu.S Normal file
View File

@ -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)