MIPS: Support for 64-bit FP with O32 binaries
CPUs implementing MIPS32 R2 may include a 64-bit FPU, just as MIPS64 CPUs do. In order to preserve backwards compatibility a 64-bit FPU will act like a 32-bit FPU (by accessing doubles from the least significant 32 bits of an even-odd pair of FP registers) when the Status.FR bit is zero, again just like a mips64 CPU. The standard O32 ABI is defined expecting a 32-bit FPU, however recent toolchains support use of a 64-bit FPU from an O32 MIPS32 executable. When an ELF executable is built to use a 64-bit FPU a new flag (EF_MIPS_FP64) is set in the ELF header. With this patch the kernel will check the EF_MIPS_FP64 flag when executing an O32 binary, and set Status.FR accordingly. The addition of O32 64-bit FP support lessens the opportunity for optimisation in the FPU emulator, so a CONFIG_MIPS_O32_FP64_SUPPORT Kconfig option is introduced to allow this support to be disabled for those that don't require it. Inspired by an earlier patch by Leonid Yegoshin, but implemented more cleanly & correctly. Signed-off-by: Paul Burton <paul.burton@imgtec.com> Cc: linux-mips@linux-mips.org Cc: Paul Burton <paul.burton@imgtec.com> Patchwork: https://patchwork.linux-mips.org/patch/6154/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
This commit is contained in:
parent
56a22d21bf
commit
597ce1723e
|
@ -2335,6 +2335,23 @@ config CC_STACKPROTECTOR
|
|||
|
||||
This feature requires gcc version 4.2 or above.
|
||||
|
||||
config MIPS_O32_FP64_SUPPORT
|
||||
bool "Support for O32 binaries using 64-bit FP"
|
||||
depends on 32BIT || MIPS32_O32
|
||||
default y
|
||||
help
|
||||
When this is enabled, the kernel will support use of 64-bit floating
|
||||
point registers with binaries using the O32 ABI along with the
|
||||
EF_MIPS_FP64 ELF header flag (typically built with -mfp64). On
|
||||
32-bit MIPS systems this support is at the cost of increasing the
|
||||
size and complexity of the compiled FPU emulator. Thus if you are
|
||||
running a MIPS32 system and know that none of your userland binaries
|
||||
will require 64-bit floating point, you may wish to reduce the size
|
||||
of your kernel & potentially improve FP emulation performance by
|
||||
saying N here.
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
config USE_OF
|
||||
bool
|
||||
select OF
|
||||
|
|
|
@ -12,27 +12,6 @@
|
|||
#include <asm/fpregdef.h>
|
||||
#include <asm/mipsregs.h>
|
||||
|
||||
.macro fpu_save_double thread status tmp1=t0
|
||||
cfc1 \tmp1, fcr31
|
||||
sdc1 $f0, THREAD_FPR0(\thread)
|
||||
sdc1 $f2, THREAD_FPR2(\thread)
|
||||
sdc1 $f4, THREAD_FPR4(\thread)
|
||||
sdc1 $f6, THREAD_FPR6(\thread)
|
||||
sdc1 $f8, THREAD_FPR8(\thread)
|
||||
sdc1 $f10, THREAD_FPR10(\thread)
|
||||
sdc1 $f12, THREAD_FPR12(\thread)
|
||||
sdc1 $f14, THREAD_FPR14(\thread)
|
||||
sdc1 $f16, THREAD_FPR16(\thread)
|
||||
sdc1 $f18, THREAD_FPR18(\thread)
|
||||
sdc1 $f20, THREAD_FPR20(\thread)
|
||||
sdc1 $f22, THREAD_FPR22(\thread)
|
||||
sdc1 $f24, THREAD_FPR24(\thread)
|
||||
sdc1 $f26, THREAD_FPR26(\thread)
|
||||
sdc1 $f28, THREAD_FPR28(\thread)
|
||||
sdc1 $f30, THREAD_FPR30(\thread)
|
||||
sw \tmp1, THREAD_FCR31(\thread)
|
||||
.endm
|
||||
|
||||
.macro fpu_save_single thread tmp=t0
|
||||
cfc1 \tmp, fcr31
|
||||
swc1 $f0, THREAD_FPR0(\thread)
|
||||
|
@ -70,27 +49,6 @@
|
|||
sw \tmp, THREAD_FCR31(\thread)
|
||||
.endm
|
||||
|
||||
.macro fpu_restore_double thread status tmp=t0
|
||||
lw \tmp, THREAD_FCR31(\thread)
|
||||
ldc1 $f0, THREAD_FPR0(\thread)
|
||||
ldc1 $f2, THREAD_FPR2(\thread)
|
||||
ldc1 $f4, THREAD_FPR4(\thread)
|
||||
ldc1 $f6, THREAD_FPR6(\thread)
|
||||
ldc1 $f8, THREAD_FPR8(\thread)
|
||||
ldc1 $f10, THREAD_FPR10(\thread)
|
||||
ldc1 $f12, THREAD_FPR12(\thread)
|
||||
ldc1 $f14, THREAD_FPR14(\thread)
|
||||
ldc1 $f16, THREAD_FPR16(\thread)
|
||||
ldc1 $f18, THREAD_FPR18(\thread)
|
||||
ldc1 $f20, THREAD_FPR20(\thread)
|
||||
ldc1 $f22, THREAD_FPR22(\thread)
|
||||
ldc1 $f24, THREAD_FPR24(\thread)
|
||||
ldc1 $f26, THREAD_FPR26(\thread)
|
||||
ldc1 $f28, THREAD_FPR28(\thread)
|
||||
ldc1 $f30, THREAD_FPR30(\thread)
|
||||
ctc1 \tmp, fcr31
|
||||
.endm
|
||||
|
||||
.macro fpu_restore_single thread tmp=t0
|
||||
lw \tmp, THREAD_FCR31(\thread)
|
||||
lwc1 $f0, THREAD_FPR0(\thread)
|
||||
|
|
|
@ -13,102 +13,6 @@
|
|||
#include <asm/fpregdef.h>
|
||||
#include <asm/mipsregs.h>
|
||||
|
||||
.macro fpu_save_16even thread tmp=t0
|
||||
cfc1 \tmp, fcr31
|
||||
sdc1 $f0, THREAD_FPR0(\thread)
|
||||
sdc1 $f2, THREAD_FPR2(\thread)
|
||||
sdc1 $f4, THREAD_FPR4(\thread)
|
||||
sdc1 $f6, THREAD_FPR6(\thread)
|
||||
sdc1 $f8, THREAD_FPR8(\thread)
|
||||
sdc1 $f10, THREAD_FPR10(\thread)
|
||||
sdc1 $f12, THREAD_FPR12(\thread)
|
||||
sdc1 $f14, THREAD_FPR14(\thread)
|
||||
sdc1 $f16, THREAD_FPR16(\thread)
|
||||
sdc1 $f18, THREAD_FPR18(\thread)
|
||||
sdc1 $f20, THREAD_FPR20(\thread)
|
||||
sdc1 $f22, THREAD_FPR22(\thread)
|
||||
sdc1 $f24, THREAD_FPR24(\thread)
|
||||
sdc1 $f26, THREAD_FPR26(\thread)
|
||||
sdc1 $f28, THREAD_FPR28(\thread)
|
||||
sdc1 $f30, THREAD_FPR30(\thread)
|
||||
sw \tmp, THREAD_FCR31(\thread)
|
||||
.endm
|
||||
|
||||
.macro fpu_save_16odd thread
|
||||
sdc1 $f1, THREAD_FPR1(\thread)
|
||||
sdc1 $f3, THREAD_FPR3(\thread)
|
||||
sdc1 $f5, THREAD_FPR5(\thread)
|
||||
sdc1 $f7, THREAD_FPR7(\thread)
|
||||
sdc1 $f9, THREAD_FPR9(\thread)
|
||||
sdc1 $f11, THREAD_FPR11(\thread)
|
||||
sdc1 $f13, THREAD_FPR13(\thread)
|
||||
sdc1 $f15, THREAD_FPR15(\thread)
|
||||
sdc1 $f17, THREAD_FPR17(\thread)
|
||||
sdc1 $f19, THREAD_FPR19(\thread)
|
||||
sdc1 $f21, THREAD_FPR21(\thread)
|
||||
sdc1 $f23, THREAD_FPR23(\thread)
|
||||
sdc1 $f25, THREAD_FPR25(\thread)
|
||||
sdc1 $f27, THREAD_FPR27(\thread)
|
||||
sdc1 $f29, THREAD_FPR29(\thread)
|
||||
sdc1 $f31, THREAD_FPR31(\thread)
|
||||
.endm
|
||||
|
||||
.macro fpu_save_double thread status tmp
|
||||
sll \tmp, \status, 5
|
||||
bgez \tmp, 2f
|
||||
fpu_save_16odd \thread
|
||||
2:
|
||||
fpu_save_16even \thread \tmp
|
||||
.endm
|
||||
|
||||
.macro fpu_restore_16even thread tmp=t0
|
||||
lw \tmp, THREAD_FCR31(\thread)
|
||||
ldc1 $f0, THREAD_FPR0(\thread)
|
||||
ldc1 $f2, THREAD_FPR2(\thread)
|
||||
ldc1 $f4, THREAD_FPR4(\thread)
|
||||
ldc1 $f6, THREAD_FPR6(\thread)
|
||||
ldc1 $f8, THREAD_FPR8(\thread)
|
||||
ldc1 $f10, THREAD_FPR10(\thread)
|
||||
ldc1 $f12, THREAD_FPR12(\thread)
|
||||
ldc1 $f14, THREAD_FPR14(\thread)
|
||||
ldc1 $f16, THREAD_FPR16(\thread)
|
||||
ldc1 $f18, THREAD_FPR18(\thread)
|
||||
ldc1 $f20, THREAD_FPR20(\thread)
|
||||
ldc1 $f22, THREAD_FPR22(\thread)
|
||||
ldc1 $f24, THREAD_FPR24(\thread)
|
||||
ldc1 $f26, THREAD_FPR26(\thread)
|
||||
ldc1 $f28, THREAD_FPR28(\thread)
|
||||
ldc1 $f30, THREAD_FPR30(\thread)
|
||||
ctc1 \tmp, fcr31
|
||||
.endm
|
||||
|
||||
.macro fpu_restore_16odd thread
|
||||
ldc1 $f1, THREAD_FPR1(\thread)
|
||||
ldc1 $f3, THREAD_FPR3(\thread)
|
||||
ldc1 $f5, THREAD_FPR5(\thread)
|
||||
ldc1 $f7, THREAD_FPR7(\thread)
|
||||
ldc1 $f9, THREAD_FPR9(\thread)
|
||||
ldc1 $f11, THREAD_FPR11(\thread)
|
||||
ldc1 $f13, THREAD_FPR13(\thread)
|
||||
ldc1 $f15, THREAD_FPR15(\thread)
|
||||
ldc1 $f17, THREAD_FPR17(\thread)
|
||||
ldc1 $f19, THREAD_FPR19(\thread)
|
||||
ldc1 $f21, THREAD_FPR21(\thread)
|
||||
ldc1 $f23, THREAD_FPR23(\thread)
|
||||
ldc1 $f25, THREAD_FPR25(\thread)
|
||||
ldc1 $f27, THREAD_FPR27(\thread)
|
||||
ldc1 $f29, THREAD_FPR29(\thread)
|
||||
ldc1 $f31, THREAD_FPR31(\thread)
|
||||
.endm
|
||||
|
||||
.macro fpu_restore_double thread status tmp
|
||||
sll \tmp, \status, 5
|
||||
bgez \tmp, 1f # 16 register mode?
|
||||
|
||||
fpu_restore_16odd \thread
|
||||
1: fpu_restore_16even \thread \tmp
|
||||
.endm
|
||||
|
||||
.macro cpu_save_nonscratch thread
|
||||
LONG_S s0, THREAD_REG16(\thread)
|
||||
LONG_S s1, THREAD_REG17(\thread)
|
||||
|
|
|
@ -62,6 +62,113 @@
|
|||
.endm
|
||||
#endif /* CONFIG_MIPS_MT_SMTC */
|
||||
|
||||
.macro fpu_save_16even thread tmp=t0
|
||||
cfc1 \tmp, fcr31
|
||||
sdc1 $f0, THREAD_FPR0(\thread)
|
||||
sdc1 $f2, THREAD_FPR2(\thread)
|
||||
sdc1 $f4, THREAD_FPR4(\thread)
|
||||
sdc1 $f6, THREAD_FPR6(\thread)
|
||||
sdc1 $f8, THREAD_FPR8(\thread)
|
||||
sdc1 $f10, THREAD_FPR10(\thread)
|
||||
sdc1 $f12, THREAD_FPR12(\thread)
|
||||
sdc1 $f14, THREAD_FPR14(\thread)
|
||||
sdc1 $f16, THREAD_FPR16(\thread)
|
||||
sdc1 $f18, THREAD_FPR18(\thread)
|
||||
sdc1 $f20, THREAD_FPR20(\thread)
|
||||
sdc1 $f22, THREAD_FPR22(\thread)
|
||||
sdc1 $f24, THREAD_FPR24(\thread)
|
||||
sdc1 $f26, THREAD_FPR26(\thread)
|
||||
sdc1 $f28, THREAD_FPR28(\thread)
|
||||
sdc1 $f30, THREAD_FPR30(\thread)
|
||||
sw \tmp, THREAD_FCR31(\thread)
|
||||
.endm
|
||||
|
||||
.macro fpu_save_16odd thread
|
||||
.set push
|
||||
.set mips64r2
|
||||
sdc1 $f1, THREAD_FPR1(\thread)
|
||||
sdc1 $f3, THREAD_FPR3(\thread)
|
||||
sdc1 $f5, THREAD_FPR5(\thread)
|
||||
sdc1 $f7, THREAD_FPR7(\thread)
|
||||
sdc1 $f9, THREAD_FPR9(\thread)
|
||||
sdc1 $f11, THREAD_FPR11(\thread)
|
||||
sdc1 $f13, THREAD_FPR13(\thread)
|
||||
sdc1 $f15, THREAD_FPR15(\thread)
|
||||
sdc1 $f17, THREAD_FPR17(\thread)
|
||||
sdc1 $f19, THREAD_FPR19(\thread)
|
||||
sdc1 $f21, THREAD_FPR21(\thread)
|
||||
sdc1 $f23, THREAD_FPR23(\thread)
|
||||
sdc1 $f25, THREAD_FPR25(\thread)
|
||||
sdc1 $f27, THREAD_FPR27(\thread)
|
||||
sdc1 $f29, THREAD_FPR29(\thread)
|
||||
sdc1 $f31, THREAD_FPR31(\thread)
|
||||
.set pop
|
||||
.endm
|
||||
|
||||
.macro fpu_save_double thread status tmp
|
||||
#if defined(CONFIG_MIPS64) || defined(CONFIG_CPU_MIPS32_R2)
|
||||
sll \tmp, \status, 5
|
||||
bgez \tmp, 10f
|
||||
fpu_save_16odd \thread
|
||||
10:
|
||||
#endif
|
||||
fpu_save_16even \thread \tmp
|
||||
.endm
|
||||
|
||||
.macro fpu_restore_16even thread tmp=t0
|
||||
lw \tmp, THREAD_FCR31(\thread)
|
||||
ldc1 $f0, THREAD_FPR0(\thread)
|
||||
ldc1 $f2, THREAD_FPR2(\thread)
|
||||
ldc1 $f4, THREAD_FPR4(\thread)
|
||||
ldc1 $f6, THREAD_FPR6(\thread)
|
||||
ldc1 $f8, THREAD_FPR8(\thread)
|
||||
ldc1 $f10, THREAD_FPR10(\thread)
|
||||
ldc1 $f12, THREAD_FPR12(\thread)
|
||||
ldc1 $f14, THREAD_FPR14(\thread)
|
||||
ldc1 $f16, THREAD_FPR16(\thread)
|
||||
ldc1 $f18, THREAD_FPR18(\thread)
|
||||
ldc1 $f20, THREAD_FPR20(\thread)
|
||||
ldc1 $f22, THREAD_FPR22(\thread)
|
||||
ldc1 $f24, THREAD_FPR24(\thread)
|
||||
ldc1 $f26, THREAD_FPR26(\thread)
|
||||
ldc1 $f28, THREAD_FPR28(\thread)
|
||||
ldc1 $f30, THREAD_FPR30(\thread)
|
||||
ctc1 \tmp, fcr31
|
||||
.endm
|
||||
|
||||
.macro fpu_restore_16odd thread
|
||||
.set push
|
||||
.set mips64r2
|
||||
ldc1 $f1, THREAD_FPR1(\thread)
|
||||
ldc1 $f3, THREAD_FPR3(\thread)
|
||||
ldc1 $f5, THREAD_FPR5(\thread)
|
||||
ldc1 $f7, THREAD_FPR7(\thread)
|
||||
ldc1 $f9, THREAD_FPR9(\thread)
|
||||
ldc1 $f11, THREAD_FPR11(\thread)
|
||||
ldc1 $f13, THREAD_FPR13(\thread)
|
||||
ldc1 $f15, THREAD_FPR15(\thread)
|
||||
ldc1 $f17, THREAD_FPR17(\thread)
|
||||
ldc1 $f19, THREAD_FPR19(\thread)
|
||||
ldc1 $f21, THREAD_FPR21(\thread)
|
||||
ldc1 $f23, THREAD_FPR23(\thread)
|
||||
ldc1 $f25, THREAD_FPR25(\thread)
|
||||
ldc1 $f27, THREAD_FPR27(\thread)
|
||||
ldc1 $f29, THREAD_FPR29(\thread)
|
||||
ldc1 $f31, THREAD_FPR31(\thread)
|
||||
.set pop
|
||||
.endm
|
||||
|
||||
.macro fpu_restore_double thread status tmp
|
||||
#if defined(CONFIG_MIPS64) || defined(CONFIG_CPU_MIPS32_R2)
|
||||
sll \tmp, \status, 5
|
||||
bgez \tmp, 10f # 16 register mode?
|
||||
|
||||
fpu_restore_16odd \thread
|
||||
10:
|
||||
#endif
|
||||
fpu_restore_16even \thread \tmp
|
||||
.endm
|
||||
|
||||
/*
|
||||
* Temporary until all gas have MT ASE support
|
||||
*/
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#define EF_MIPS_ABI2 0x00000020
|
||||
#define EF_MIPS_OPTIONS_FIRST 0x00000080
|
||||
#define EF_MIPS_32BITMODE 0x00000100
|
||||
#define EF_MIPS_FP64 0x00000200
|
||||
#define EF_MIPS_ABI 0x0000f000
|
||||
#define EF_MIPS_ARCH 0xf0000000
|
||||
|
||||
|
@ -175,6 +176,18 @@ typedef elf_fpreg_t elf_fpregset_t[ELF_NFPREG];
|
|||
|
||||
#ifdef CONFIG_32BIT
|
||||
|
||||
/*
|
||||
* In order to be sure that we don't attempt to execute an O32 binary which
|
||||
* requires 64 bit FP (FR=1) on a system which does not support it we refuse
|
||||
* to execute any binary which has bits specified by the following macro set
|
||||
* in its ELF header flags.
|
||||
*/
|
||||
#ifdef CONFIG_MIPS_O32_FP64_SUPPORT
|
||||
# define __MIPS_O32_FP64_MUST_BE_ZERO 0
|
||||
#else
|
||||
# define __MIPS_O32_FP64_MUST_BE_ZERO EF_MIPS_FP64
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This is used to ensure we don't load something for the wrong architecture.
|
||||
*/
|
||||
|
@ -191,6 +204,8 @@ typedef elf_fpreg_t elf_fpregset_t[ELF_NFPREG];
|
|||
__res = 0; \
|
||||
if (((__h->e_flags & EF_MIPS_ABI) != 0) && \
|
||||
((__h->e_flags & EF_MIPS_ABI) != EF_MIPS_ABI_O32)) \
|
||||
__res = 0; \
|
||||
if (__h->e_flags & __MIPS_O32_FP64_MUST_BE_ZERO) \
|
||||
__res = 0; \
|
||||
\
|
||||
__res; \
|
||||
|
@ -249,6 +264,11 @@ extern struct mips_abi mips_abi_n32;
|
|||
|
||||
#define SET_PERSONALITY(ex) \
|
||||
do { \
|
||||
if ((ex).e_flags & EF_MIPS_FP64) \
|
||||
clear_thread_flag(TIF_32BIT_FPREGS); \
|
||||
else \
|
||||
set_thread_flag(TIF_32BIT_FPREGS); \
|
||||
\
|
||||
if (personality(current->personality) != PER_LINUX) \
|
||||
set_personality(PER_LINUX); \
|
||||
\
|
||||
|
@ -271,14 +291,18 @@ do { \
|
|||
#endif
|
||||
|
||||
#ifdef CONFIG_MIPS32_O32
|
||||
#define __SET_PERSONALITY32_O32() \
|
||||
#define __SET_PERSONALITY32_O32(ex) \
|
||||
do { \
|
||||
set_thread_flag(TIF_32BIT_REGS); \
|
||||
set_thread_flag(TIF_32BIT_ADDR); \
|
||||
\
|
||||
if (!((ex).e_flags & EF_MIPS_FP64)) \
|
||||
set_thread_flag(TIF_32BIT_FPREGS); \
|
||||
\
|
||||
current->thread.abi = &mips_abi_32; \
|
||||
} while (0)
|
||||
#else
|
||||
#define __SET_PERSONALITY32_O32() \
|
||||
#define __SET_PERSONALITY32_O32(ex) \
|
||||
do { } while (0)
|
||||
#endif
|
||||
|
||||
|
@ -289,7 +313,7 @@ do { \
|
|||
((ex).e_flags & EF_MIPS_ABI) == 0) \
|
||||
__SET_PERSONALITY32_N32(); \
|
||||
else \
|
||||
__SET_PERSONALITY32_O32(); \
|
||||
__SET_PERSONALITY32_O32(ex); \
|
||||
} while (0)
|
||||
#else
|
||||
#define __SET_PERSONALITY32(ex) do { } while (0)
|
||||
|
@ -300,6 +324,7 @@ do { \
|
|||
unsigned int p; \
|
||||
\
|
||||
clear_thread_flag(TIF_32BIT_REGS); \
|
||||
clear_thread_flag(TIF_32BIT_FPREGS); \
|
||||
clear_thread_flag(TIF_32BIT_ADDR); \
|
||||
\
|
||||
if ((ex).e_ident[EI_CLASS] == ELFCLASS32) \
|
||||
|
|
|
@ -33,11 +33,48 @@ extern void _init_fpu(void);
|
|||
extern void _save_fp(struct task_struct *);
|
||||
extern void _restore_fp(struct task_struct *);
|
||||
|
||||
#define __enable_fpu() \
|
||||
do { \
|
||||
set_c0_status(ST0_CU1); \
|
||||
enable_fpu_hazard(); \
|
||||
} while (0)
|
||||
/*
|
||||
* This enum specifies a mode in which we want the FPU to operate, for cores
|
||||
* which implement the Status.FR bit. Note that FPU_32BIT & FPU_64BIT
|
||||
* purposefully have the values 0 & 1 respectively, so that an integer value
|
||||
* of Status.FR can be trivially casted to the corresponding enum fpu_mode.
|
||||
*/
|
||||
enum fpu_mode {
|
||||
FPU_32BIT = 0, /* FR = 0 */
|
||||
FPU_64BIT, /* FR = 1 */
|
||||
FPU_AS_IS,
|
||||
};
|
||||
|
||||
static inline int __enable_fpu(enum fpu_mode mode)
|
||||
{
|
||||
int fr;
|
||||
|
||||
switch (mode) {
|
||||
case FPU_AS_IS:
|
||||
/* just enable the FPU in its current mode */
|
||||
set_c0_status(ST0_CU1);
|
||||
enable_fpu_hazard();
|
||||
return 0;
|
||||
|
||||
case FPU_64BIT:
|
||||
#if !(defined(CONFIG_CPU_MIPS32_R2) || defined(CONFIG_MIPS64))
|
||||
/* we only have a 32-bit FPU */
|
||||
return SIGFPE;
|
||||
#endif
|
||||
/* fall through */
|
||||
case FPU_32BIT:
|
||||
/* set CU1 & change FR appropriately */
|
||||
fr = (int)mode;
|
||||
change_c0_status(ST0_CU1 | ST0_FR, ST0_CU1 | (fr ? ST0_FR : 0));
|
||||
enable_fpu_hazard();
|
||||
|
||||
/* check FR has the desired value */
|
||||
return (!!(read_c0_status() & ST0_FR) == !!fr) ? 0 : SIGFPE;
|
||||
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
}
|
||||
|
||||
#define __disable_fpu() \
|
||||
do { \
|
||||
|
@ -57,27 +94,46 @@ static inline int is_fpu_owner(void)
|
|||
return cpu_has_fpu && __is_fpu_owner();
|
||||
}
|
||||
|
||||
static inline void __own_fpu(void)
|
||||
static inline int __own_fpu(void)
|
||||
{
|
||||
__enable_fpu();
|
||||
enum fpu_mode mode;
|
||||
int ret;
|
||||
|
||||
mode = !test_thread_flag(TIF_32BIT_FPREGS);
|
||||
ret = __enable_fpu(mode);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
KSTK_STATUS(current) |= ST0_CU1;
|
||||
if (mode == FPU_64BIT)
|
||||
KSTK_STATUS(current) |= ST0_FR;
|
||||
else /* mode == FPU_32BIT */
|
||||
KSTK_STATUS(current) &= ~ST0_FR;
|
||||
|
||||
set_thread_flag(TIF_USEDFPU);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void own_fpu_inatomic(int restore)
|
||||
static inline int own_fpu_inatomic(int restore)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (cpu_has_fpu && !__is_fpu_owner()) {
|
||||
__own_fpu();
|
||||
if (restore)
|
||||
ret = __own_fpu();
|
||||
if (restore && !ret)
|
||||
_restore_fp(current);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void own_fpu(int restore)
|
||||
static inline int own_fpu(int restore)
|
||||
{
|
||||
int ret;
|
||||
|
||||
preempt_disable();
|
||||
own_fpu_inatomic(restore);
|
||||
ret = own_fpu_inatomic(restore);
|
||||
preempt_enable();
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void lose_fpu(int save)
|
||||
|
@ -93,16 +149,21 @@ static inline void lose_fpu(int save)
|
|||
preempt_enable();
|
||||
}
|
||||
|
||||
static inline void init_fpu(void)
|
||||
static inline int init_fpu(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
preempt_disable();
|
||||
if (cpu_has_fpu) {
|
||||
__own_fpu();
|
||||
_init_fpu();
|
||||
ret = __own_fpu();
|
||||
if (!ret)
|
||||
_init_fpu();
|
||||
} else {
|
||||
fpu_emulator_init_fpu();
|
||||
}
|
||||
|
||||
preempt_enable();
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void save_fp(struct task_struct *tsk)
|
||||
|
|
|
@ -110,11 +110,12 @@ static inline struct thread_info *current_thread_info(void)
|
|||
#define TIF_NOHZ 19 /* in adaptive nohz mode */
|
||||
#define TIF_FIXADE 20 /* Fix address errors in software */
|
||||
#define TIF_LOGADE 21 /* Log address errors to syslog */
|
||||
#define TIF_32BIT_REGS 22 /* also implies 16/32 fprs */
|
||||
#define TIF_32BIT_REGS 22 /* 32-bit general purpose registers */
|
||||
#define TIF_32BIT_ADDR 23 /* 32-bit address space (o32/n32) */
|
||||
#define TIF_FPUBOUND 24 /* thread bound to FPU-full CPU set */
|
||||
#define TIF_LOAD_WATCH 25 /* If set, load watch registers */
|
||||
#define TIF_SYSCALL_TRACEPOINT 26 /* syscall tracepoint instrumentation */
|
||||
#define TIF_32BIT_FPREGS 27 /* 32-bit floating point registers */
|
||||
#define TIF_SYSCALL_TRACE 31 /* syscall trace active */
|
||||
|
||||
#define _TIF_SYSCALL_TRACE (1<<TIF_SYSCALL_TRACE)
|
||||
|
@ -131,6 +132,7 @@ static inline struct thread_info *current_thread_info(void)
|
|||
#define _TIF_32BIT_ADDR (1<<TIF_32BIT_ADDR)
|
||||
#define _TIF_FPUBOUND (1<<TIF_FPUBOUND)
|
||||
#define _TIF_LOAD_WATCH (1<<TIF_LOAD_WATCH)
|
||||
#define _TIF_32BIT_FPREGS (1<<TIF_32BIT_FPREGS)
|
||||
#define _TIF_SYSCALL_TRACEPOINT (1<<TIF_SYSCALL_TRACEPOINT)
|
||||
|
||||
#define _TIF_WORK_SYSCALL_ENTRY (_TIF_NOHZ | _TIF_SYSCALL_TRACE | \
|
||||
|
|
|
@ -27,6 +27,18 @@ typedef elf_greg_t elf_gregset_t[ELF_NGREG];
|
|||
typedef double elf_fpreg_t;
|
||||
typedef elf_fpreg_t elf_fpregset_t[ELF_NFPREG];
|
||||
|
||||
/*
|
||||
* In order to be sure that we don't attempt to execute an O32 binary which
|
||||
* requires 64 bit FP (FR=1) on a system which does not support it we refuse
|
||||
* to execute any binary which has bits specified by the following macro set
|
||||
* in its ELF header flags.
|
||||
*/
|
||||
#ifdef CONFIG_MIPS_O32_FP64_SUPPORT
|
||||
# define __MIPS_O32_FP64_MUST_BE_ZERO 0
|
||||
#else
|
||||
# define __MIPS_O32_FP64_MUST_BE_ZERO EF_MIPS_FP64
|
||||
#endif
|
||||
|
||||
/*
|
||||
* This is used to ensure we don't load something for the wrong architecture.
|
||||
*/
|
||||
|
@ -43,6 +55,8 @@ typedef elf_fpreg_t elf_fpregset_t[ELF_NFPREG];
|
|||
__res = 0; \
|
||||
if (((__h->e_flags & EF_MIPS_ABI) != 0) && \
|
||||
((__h->e_flags & EF_MIPS_ABI) != EF_MIPS_ABI_O32)) \
|
||||
__res = 0; \
|
||||
if (__h->e_flags & __MIPS_O32_FP64_MUST_BE_ZERO) \
|
||||
__res = 0; \
|
||||
\
|
||||
__res; \
|
||||
|
|
|
@ -112,7 +112,7 @@ static inline unsigned long cpu_get_fpu_id(void)
|
|||
unsigned long tmp, fpu_id;
|
||||
|
||||
tmp = read_c0_status();
|
||||
__enable_fpu();
|
||||
__enable_fpu(FPU_AS_IS);
|
||||
fpu_id = read_32bit_cp1_register(CP1_REVISION);
|
||||
write_c0_status(tmp);
|
||||
return fpu_id;
|
||||
|
|
|
@ -60,9 +60,6 @@ void start_thread(struct pt_regs * regs, unsigned long pc, unsigned long sp)
|
|||
|
||||
/* New thread loses kernel privileges. */
|
||||
status = regs->cp0_status & ~(ST0_CU0|ST0_CU1|ST0_FR|KU_MASK);
|
||||
#ifdef CONFIG_64BIT
|
||||
status |= test_thread_flag(TIF_32BIT_REGS) ? 0 : ST0_FR;
|
||||
#endif
|
||||
status |= KU_USER;
|
||||
regs->cp0_status = status;
|
||||
clear_used_math();
|
||||
|
|
|
@ -137,13 +137,13 @@ int ptrace_getfpregs(struct task_struct *child, __u32 __user *data)
|
|||
if (cpu_has_mipsmt) {
|
||||
unsigned int vpflags = dvpe();
|
||||
flags = read_c0_status();
|
||||
__enable_fpu();
|
||||
__enable_fpu(FPU_AS_IS);
|
||||
__asm__ __volatile__("cfc1\t%0,$0" : "=r" (tmp));
|
||||
write_c0_status(flags);
|
||||
evpe(vpflags);
|
||||
} else {
|
||||
flags = read_c0_status();
|
||||
__enable_fpu();
|
||||
__enable_fpu(FPU_AS_IS);
|
||||
__asm__ __volatile__("cfc1\t%0,$0" : "=r" (tmp));
|
||||
write_c0_status(flags);
|
||||
}
|
||||
|
@ -408,6 +408,7 @@ long arch_ptrace(struct task_struct *child, long request,
|
|||
/* Read the word at location addr in the USER area. */
|
||||
case PTRACE_PEEKUSR: {
|
||||
struct pt_regs *regs;
|
||||
fpureg_t *fregs;
|
||||
unsigned long tmp = 0;
|
||||
|
||||
regs = task_pt_regs(child);
|
||||
|
@ -418,26 +419,28 @@ long arch_ptrace(struct task_struct *child, long request,
|
|||
tmp = regs->regs[addr];
|
||||
break;
|
||||
case FPR_BASE ... FPR_BASE + 31:
|
||||
if (tsk_used_math(child)) {
|
||||
fpureg_t *fregs = get_fpu_regs(child);
|
||||
if (!tsk_used_math(child)) {
|
||||
/* FP not yet used */
|
||||
tmp = -1;
|
||||
break;
|
||||
}
|
||||
fregs = get_fpu_regs(child);
|
||||
|
||||
#ifdef CONFIG_32BIT
|
||||
if (test_thread_flag(TIF_32BIT_FPREGS)) {
|
||||
/*
|
||||
* The odd registers are actually the high
|
||||
* order bits of the values stored in the even
|
||||
* registers - unless we're using r2k_switch.S.
|
||||
*/
|
||||
if (addr & 1)
|
||||
tmp = (unsigned long) (fregs[((addr & ~1) - 32)] >> 32);
|
||||
tmp = fregs[(addr & ~1) - 32] >> 32;
|
||||
else
|
||||
tmp = (unsigned long) (fregs[(addr - 32)] & 0xffffffff);
|
||||
#endif
|
||||
#ifdef CONFIG_64BIT
|
||||
tmp = fregs[addr - FPR_BASE];
|
||||
#endif
|
||||
} else {
|
||||
tmp = -1; /* FP not yet used */
|
||||
tmp = fregs[addr - 32];
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
tmp = fregs[addr - FPR_BASE];
|
||||
break;
|
||||
case PC:
|
||||
tmp = regs->cp0_epc;
|
||||
|
@ -483,13 +486,13 @@ long arch_ptrace(struct task_struct *child, long request,
|
|||
if (cpu_has_mipsmt) {
|
||||
unsigned int vpflags = dvpe();
|
||||
flags = read_c0_status();
|
||||
__enable_fpu();
|
||||
__enable_fpu(FPU_AS_IS);
|
||||
__asm__ __volatile__("cfc1\t%0,$0": "=r" (tmp));
|
||||
write_c0_status(flags);
|
||||
evpe(vpflags);
|
||||
} else {
|
||||
flags = read_c0_status();
|
||||
__enable_fpu();
|
||||
__enable_fpu(FPU_AS_IS);
|
||||
__asm__ __volatile__("cfc1\t%0,$0": "=r" (tmp));
|
||||
write_c0_status(flags);
|
||||
}
|
||||
|
@ -554,22 +557,25 @@ long arch_ptrace(struct task_struct *child, long request,
|
|||
child->thread.fpu.fcr31 = 0;
|
||||
}
|
||||
#ifdef CONFIG_32BIT
|
||||
/*
|
||||
* The odd registers are actually the high order bits
|
||||
* of the values stored in the even registers - unless
|
||||
* we're using r2k_switch.S.
|
||||
*/
|
||||
if (addr & 1) {
|
||||
fregs[(addr & ~1) - FPR_BASE] &= 0xffffffff;
|
||||
fregs[(addr & ~1) - FPR_BASE] |= ((unsigned long long) data) << 32;
|
||||
} else {
|
||||
fregs[addr - FPR_BASE] &= ~0xffffffffLL;
|
||||
fregs[addr - FPR_BASE] |= data;
|
||||
if (test_thread_flag(TIF_32BIT_FPREGS)) {
|
||||
/*
|
||||
* The odd registers are actually the high
|
||||
* order bits of the values stored in the even
|
||||
* registers - unless we're using r2k_switch.S.
|
||||
*/
|
||||
if (addr & 1) {
|
||||
fregs[(addr & ~1) - FPR_BASE] &=
|
||||
0xffffffff;
|
||||
fregs[(addr & ~1) - FPR_BASE] |=
|
||||
((u64)data) << 32;
|
||||
} else {
|
||||
fregs[addr - FPR_BASE] &= ~0xffffffffLL;
|
||||
fregs[addr - FPR_BASE] |= data;
|
||||
}
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#ifdef CONFIG_64BIT
|
||||
fregs[addr - FPR_BASE] = data;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
case PC:
|
||||
|
|
|
@ -80,6 +80,7 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
|
|||
/* Read the word at location addr in the USER area. */
|
||||
case PTRACE_PEEKUSR: {
|
||||
struct pt_regs *regs;
|
||||
fpureg_t *fregs;
|
||||
unsigned int tmp;
|
||||
|
||||
regs = task_pt_regs(child);
|
||||
|
@ -90,21 +91,25 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
|
|||
tmp = regs->regs[addr];
|
||||
break;
|
||||
case FPR_BASE ... FPR_BASE + 31:
|
||||
if (tsk_used_math(child)) {
|
||||
fpureg_t *fregs = get_fpu_regs(child);
|
||||
|
||||
if (!tsk_used_math(child)) {
|
||||
/* FP not yet used */
|
||||
tmp = -1;
|
||||
break;
|
||||
}
|
||||
fregs = get_fpu_regs(child);
|
||||
if (test_thread_flag(TIF_32BIT_FPREGS)) {
|
||||
/*
|
||||
* The odd registers are actually the high
|
||||
* order bits of the values stored in the even
|
||||
* registers - unless we're using r2k_switch.S.
|
||||
*/
|
||||
if (addr & 1)
|
||||
tmp = (unsigned long) (fregs[((addr & ~1) - 32)] >> 32);
|
||||
tmp = fregs[(addr & ~1) - 32] >> 32;
|
||||
else
|
||||
tmp = (unsigned long) (fregs[(addr - 32)] & 0xffffffff);
|
||||
} else {
|
||||
tmp = -1; /* FP not yet used */
|
||||
tmp = fregs[addr - 32];
|
||||
break;
|
||||
}
|
||||
tmp = fregs[addr - FPR_BASE];
|
||||
break;
|
||||
case PC:
|
||||
tmp = regs->cp0_epc;
|
||||
|
@ -147,13 +152,13 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
|
|||
if (cpu_has_mipsmt) {
|
||||
unsigned int vpflags = dvpe();
|
||||
flags = read_c0_status();
|
||||
__enable_fpu();
|
||||
__enable_fpu(FPU_AS_IS);
|
||||
__asm__ __volatile__("cfc1\t%0,$0": "=r" (tmp));
|
||||
write_c0_status(flags);
|
||||
evpe(vpflags);
|
||||
} else {
|
||||
flags = read_c0_status();
|
||||
__enable_fpu();
|
||||
__enable_fpu(FPU_AS_IS);
|
||||
__asm__ __volatile__("cfc1\t%0,$0": "=r" (tmp));
|
||||
write_c0_status(flags);
|
||||
}
|
||||
|
@ -236,20 +241,24 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
|
|||
sizeof(child->thread.fpu));
|
||||
child->thread.fpu.fcr31 = 0;
|
||||
}
|
||||
/*
|
||||
* The odd registers are actually the high order bits
|
||||
* of the values stored in the even registers - unless
|
||||
* we're using r2k_switch.S.
|
||||
*/
|
||||
if (addr & 1) {
|
||||
fregs[(addr & ~1) - FPR_BASE] &= 0xffffffff;
|
||||
fregs[(addr & ~1) - FPR_BASE] |= ((unsigned long long) data) << 32;
|
||||
} else {
|
||||
fregs[addr - FPR_BASE] &= ~0xffffffffLL;
|
||||
/* Must cast, lest sign extension fill upper
|
||||
bits! */
|
||||
fregs[addr - FPR_BASE] |= (unsigned int)data;
|
||||
if (test_thread_flag(TIF_32BIT_FPREGS)) {
|
||||
/*
|
||||
* The odd registers are actually the high
|
||||
* order bits of the values stored in the even
|
||||
* registers - unless we're using r2k_switch.S.
|
||||
*/
|
||||
if (addr & 1) {
|
||||
fregs[(addr & ~1) - FPR_BASE] &=
|
||||
0xffffffff;
|
||||
fregs[(addr & ~1) - FPR_BASE] |=
|
||||
((u64)data) << 32;
|
||||
} else {
|
||||
fregs[addr - FPR_BASE] &= ~0xffffffffLL;
|
||||
fregs[addr - FPR_BASE] |= data;
|
||||
}
|
||||
break;
|
||||
}
|
||||
fregs[addr - FPR_BASE] = data;
|
||||
break;
|
||||
}
|
||||
case PC:
|
||||
|
|
|
@ -35,7 +35,15 @@
|
|||
LEAF(_save_fp_context)
|
||||
cfc1 t1, fcr31
|
||||
|
||||
#ifdef CONFIG_64BIT
|
||||
#if defined(CONFIG_64BIT) || defined(CONFIG_MIPS32_R2)
|
||||
.set push
|
||||
#ifdef CONFIG_MIPS32_R2
|
||||
.set mips64r2
|
||||
mfc0 t0, CP0_STATUS
|
||||
sll t0, t0, 5
|
||||
bgez t0, 1f # skip storing odd if FR=0
|
||||
nop
|
||||
#endif
|
||||
/* Store the 16 odd double precision registers */
|
||||
EX sdc1 $f1, SC_FPREGS+8(a0)
|
||||
EX sdc1 $f3, SC_FPREGS+24(a0)
|
||||
|
@ -53,6 +61,7 @@ LEAF(_save_fp_context)
|
|||
EX sdc1 $f27, SC_FPREGS+216(a0)
|
||||
EX sdc1 $f29, SC_FPREGS+232(a0)
|
||||
EX sdc1 $f31, SC_FPREGS+248(a0)
|
||||
1: .set pop
|
||||
#endif
|
||||
|
||||
/* Store the 16 even double precision registers */
|
||||
|
@ -82,7 +91,31 @@ LEAF(_save_fp_context)
|
|||
LEAF(_save_fp_context32)
|
||||
cfc1 t1, fcr31
|
||||
|
||||
EX sdc1 $f0, SC32_FPREGS+0(a0)
|
||||
mfc0 t0, CP0_STATUS
|
||||
sll t0, t0, 5
|
||||
bgez t0, 1f # skip storing odd if FR=0
|
||||
nop
|
||||
|
||||
/* Store the 16 odd double precision registers */
|
||||
EX sdc1 $f1, SC32_FPREGS+8(a0)
|
||||
EX sdc1 $f3, SC32_FPREGS+24(a0)
|
||||
EX sdc1 $f5, SC32_FPREGS+40(a0)
|
||||
EX sdc1 $f7, SC32_FPREGS+56(a0)
|
||||
EX sdc1 $f9, SC32_FPREGS+72(a0)
|
||||
EX sdc1 $f11, SC32_FPREGS+88(a0)
|
||||
EX sdc1 $f13, SC32_FPREGS+104(a0)
|
||||
EX sdc1 $f15, SC32_FPREGS+120(a0)
|
||||
EX sdc1 $f17, SC32_FPREGS+136(a0)
|
||||
EX sdc1 $f19, SC32_FPREGS+152(a0)
|
||||
EX sdc1 $f21, SC32_FPREGS+168(a0)
|
||||
EX sdc1 $f23, SC32_FPREGS+184(a0)
|
||||
EX sdc1 $f25, SC32_FPREGS+200(a0)
|
||||
EX sdc1 $f27, SC32_FPREGS+216(a0)
|
||||
EX sdc1 $f29, SC32_FPREGS+232(a0)
|
||||
EX sdc1 $f31, SC32_FPREGS+248(a0)
|
||||
|
||||
/* Store the 16 even double precision registers */
|
||||
1: EX sdc1 $f0, SC32_FPREGS+0(a0)
|
||||
EX sdc1 $f2, SC32_FPREGS+16(a0)
|
||||
EX sdc1 $f4, SC32_FPREGS+32(a0)
|
||||
EX sdc1 $f6, SC32_FPREGS+48(a0)
|
||||
|
@ -114,7 +147,16 @@ LEAF(_save_fp_context32)
|
|||
*/
|
||||
LEAF(_restore_fp_context)
|
||||
EX lw t0, SC_FPC_CSR(a0)
|
||||
#ifdef CONFIG_64BIT
|
||||
|
||||
#if defined(CONFIG_64BIT) || defined(CONFIG_MIPS32_R2)
|
||||
.set push
|
||||
#ifdef CONFIG_MIPS32_R2
|
||||
.set mips64r2
|
||||
mfc0 t0, CP0_STATUS
|
||||
sll t0, t0, 5
|
||||
bgez t0, 1f # skip loading odd if FR=0
|
||||
nop
|
||||
#endif
|
||||
EX ldc1 $f1, SC_FPREGS+8(a0)
|
||||
EX ldc1 $f3, SC_FPREGS+24(a0)
|
||||
EX ldc1 $f5, SC_FPREGS+40(a0)
|
||||
|
@ -131,6 +173,7 @@ LEAF(_restore_fp_context)
|
|||
EX ldc1 $f27, SC_FPREGS+216(a0)
|
||||
EX ldc1 $f29, SC_FPREGS+232(a0)
|
||||
EX ldc1 $f31, SC_FPREGS+248(a0)
|
||||
1: .set pop
|
||||
#endif
|
||||
EX ldc1 $f0, SC_FPREGS+0(a0)
|
||||
EX ldc1 $f2, SC_FPREGS+16(a0)
|
||||
|
@ -157,7 +200,30 @@ LEAF(_restore_fp_context)
|
|||
LEAF(_restore_fp_context32)
|
||||
/* Restore an o32 sigcontext. */
|
||||
EX lw t0, SC32_FPC_CSR(a0)
|
||||
EX ldc1 $f0, SC32_FPREGS+0(a0)
|
||||
|
||||
mfc0 t0, CP0_STATUS
|
||||
sll t0, t0, 5
|
||||
bgez t0, 1f # skip loading odd if FR=0
|
||||
nop
|
||||
|
||||
EX ldc1 $f1, SC32_FPREGS+8(a0)
|
||||
EX ldc1 $f3, SC32_FPREGS+24(a0)
|
||||
EX ldc1 $f5, SC32_FPREGS+40(a0)
|
||||
EX ldc1 $f7, SC32_FPREGS+56(a0)
|
||||
EX ldc1 $f9, SC32_FPREGS+72(a0)
|
||||
EX ldc1 $f11, SC32_FPREGS+88(a0)
|
||||
EX ldc1 $f13, SC32_FPREGS+104(a0)
|
||||
EX ldc1 $f15, SC32_FPREGS+120(a0)
|
||||
EX ldc1 $f17, SC32_FPREGS+136(a0)
|
||||
EX ldc1 $f19, SC32_FPREGS+152(a0)
|
||||
EX ldc1 $f21, SC32_FPREGS+168(a0)
|
||||
EX ldc1 $f23, SC32_FPREGS+184(a0)
|
||||
EX ldc1 $f25, SC32_FPREGS+200(a0)
|
||||
EX ldc1 $f27, SC32_FPREGS+216(a0)
|
||||
EX ldc1 $f29, SC32_FPREGS+232(a0)
|
||||
EX ldc1 $f31, SC32_FPREGS+248(a0)
|
||||
|
||||
1: EX ldc1 $f0, SC32_FPREGS+0(a0)
|
||||
EX ldc1 $f2, SC32_FPREGS+16(a0)
|
||||
EX ldc1 $f4, SC32_FPREGS+32(a0)
|
||||
EX ldc1 $f6, SC32_FPREGS+48(a0)
|
||||
|
|
|
@ -123,7 +123,7 @@
|
|||
* Save a thread's fp context.
|
||||
*/
|
||||
LEAF(_save_fp)
|
||||
#ifdef CONFIG_64BIT
|
||||
#if defined(CONFIG_64BIT) || defined(CONFIG_CPU_MIPS32_R2)
|
||||
mfc0 t0, CP0_STATUS
|
||||
#endif
|
||||
fpu_save_double a0 t0 t1 # clobbers t1
|
||||
|
@ -134,7 +134,7 @@ LEAF(_save_fp)
|
|||
* Restore a thread's fp context.
|
||||
*/
|
||||
LEAF(_restore_fp)
|
||||
#ifdef CONFIG_64BIT
|
||||
#if defined(CONFIG_64BIT) || defined(CONFIG_CPU_MIPS32_R2)
|
||||
mfc0 t0, CP0_STATUS
|
||||
#endif
|
||||
fpu_restore_double a0 t0 t1 # clobbers t1
|
||||
|
@ -228,6 +228,47 @@ LEAF(_init_fpu)
|
|||
mtc1 t1, $f29
|
||||
mtc1 t1, $f30
|
||||
mtc1 t1, $f31
|
||||
|
||||
#ifdef CONFIG_CPU_MIPS32_R2
|
||||
.set push
|
||||
.set mips64r2
|
||||
sll t0, t0, 5 # is Status.FR set?
|
||||
bgez t0, 1f # no: skip setting upper 32b
|
||||
|
||||
mthc1 t1, $f0
|
||||
mthc1 t1, $f1
|
||||
mthc1 t1, $f2
|
||||
mthc1 t1, $f3
|
||||
mthc1 t1, $f4
|
||||
mthc1 t1, $f5
|
||||
mthc1 t1, $f6
|
||||
mthc1 t1, $f7
|
||||
mthc1 t1, $f8
|
||||
mthc1 t1, $f9
|
||||
mthc1 t1, $f10
|
||||
mthc1 t1, $f11
|
||||
mthc1 t1, $f12
|
||||
mthc1 t1, $f13
|
||||
mthc1 t1, $f14
|
||||
mthc1 t1, $f15
|
||||
mthc1 t1, $f16
|
||||
mthc1 t1, $f17
|
||||
mthc1 t1, $f18
|
||||
mthc1 t1, $f19
|
||||
mthc1 t1, $f20
|
||||
mthc1 t1, $f21
|
||||
mthc1 t1, $f22
|
||||
mthc1 t1, $f23
|
||||
mthc1 t1, $f24
|
||||
mthc1 t1, $f25
|
||||
mthc1 t1, $f26
|
||||
mthc1 t1, $f27
|
||||
mthc1 t1, $f28
|
||||
mthc1 t1, $f29
|
||||
mthc1 t1, $f30
|
||||
mthc1 t1, $f31
|
||||
1: .set pop
|
||||
#endif /* CONFIG_CPU_MIPS32_R2 */
|
||||
#else
|
||||
.set mips3
|
||||
dmtc1 t1, $f0
|
||||
|
|
|
@ -71,8 +71,9 @@ static int protected_save_fp_context(struct sigcontext __user *sc)
|
|||
int err;
|
||||
while (1) {
|
||||
lock_fpu_owner();
|
||||
own_fpu_inatomic(1);
|
||||
err = save_fp_context(sc); /* this might fail */
|
||||
err = own_fpu_inatomic(1);
|
||||
if (!err)
|
||||
err = save_fp_context(sc); /* this might fail */
|
||||
unlock_fpu_owner();
|
||||
if (likely(!err))
|
||||
break;
|
||||
|
@ -91,8 +92,9 @@ static int protected_restore_fp_context(struct sigcontext __user *sc)
|
|||
int err, tmp __maybe_unused;
|
||||
while (1) {
|
||||
lock_fpu_owner();
|
||||
own_fpu_inatomic(0);
|
||||
err = restore_fp_context(sc); /* this might fail */
|
||||
err = own_fpu_inatomic(0);
|
||||
if (!err)
|
||||
err = restore_fp_context(sc); /* this might fail */
|
||||
unlock_fpu_owner();
|
||||
if (likely(!err))
|
||||
break;
|
||||
|
|
|
@ -85,8 +85,9 @@ static int protected_save_fp_context32(struct sigcontext32 __user *sc)
|
|||
int err;
|
||||
while (1) {
|
||||
lock_fpu_owner();
|
||||
own_fpu_inatomic(1);
|
||||
err = save_fp_context32(sc); /* this might fail */
|
||||
err = own_fpu_inatomic(1);
|
||||
if (!err)
|
||||
err = save_fp_context32(sc); /* this might fail */
|
||||
unlock_fpu_owner();
|
||||
if (likely(!err))
|
||||
break;
|
||||
|
@ -105,8 +106,9 @@ static int protected_restore_fp_context32(struct sigcontext32 __user *sc)
|
|||
int err, tmp __maybe_unused;
|
||||
while (1) {
|
||||
lock_fpu_owner();
|
||||
own_fpu_inatomic(0);
|
||||
err = restore_fp_context32(sc); /* this might fail */
|
||||
err = own_fpu_inatomic(0);
|
||||
if (!err)
|
||||
err = restore_fp_context32(sc); /* this might fail */
|
||||
unlock_fpu_owner();
|
||||
if (likely(!err))
|
||||
break;
|
||||
|
|
|
@ -1080,7 +1080,7 @@ asmlinkage void do_cpu(struct pt_regs *regs)
|
|||
unsigned long old_epc, old31;
|
||||
unsigned int opcode;
|
||||
unsigned int cpid;
|
||||
int status;
|
||||
int status, err;
|
||||
unsigned long __maybe_unused flags;
|
||||
|
||||
prev_state = exception_enter();
|
||||
|
@ -1153,19 +1153,19 @@ asmlinkage void do_cpu(struct pt_regs *regs)
|
|||
|
||||
case 1:
|
||||
if (used_math()) /* Using the FPU again. */
|
||||
own_fpu(1);
|
||||
err = own_fpu(1);
|
||||
else { /* First time FPU user. */
|
||||
init_fpu();
|
||||
err = init_fpu();
|
||||
set_used_math();
|
||||
}
|
||||
|
||||
if (!raw_cpu_has_fpu) {
|
||||
if (!raw_cpu_has_fpu || err) {
|
||||
int sig;
|
||||
void __user *fault_addr = NULL;
|
||||
sig = fpu_emulator_cop1Handler(regs,
|
||||
¤t->thread.fpu,
|
||||
0, &fault_addr);
|
||||
if (!process_fpemu_return(sig, fault_addr))
|
||||
if (!process_fpemu_return(sig, fault_addr) && !err)
|
||||
mt_ase_fp_affinity();
|
||||
}
|
||||
|
||||
|
|
|
@ -859,20 +859,20 @@ static int isBranchInstr(struct pt_regs *regs, struct mm_decoded_insn dec_insn,
|
|||
* In the Linux kernel, we support selection of FPR format on the
|
||||
* basis of the Status.FR bit. If an FPU is not present, the FR bit
|
||||
* is hardwired to zero, which would imply a 32-bit FPU even for
|
||||
* 64-bit CPUs so we rather look at TIF_32BIT_REGS.
|
||||
* 64-bit CPUs so we rather look at TIF_32BIT_FPREGS.
|
||||
* FPU emu is slow and bulky and optimizing this function offers fairly
|
||||
* sizeable benefits so we try to be clever and make this function return
|
||||
* a constant whenever possible, that is on 64-bit kernels without O32
|
||||
* compatibility enabled and on 32-bit kernels.
|
||||
* compatibility enabled and on 32-bit without 64-bit FPU support.
|
||||
*/
|
||||
static inline int cop1_64bit(struct pt_regs *xcp)
|
||||
{
|
||||
#if defined(CONFIG_64BIT) && !defined(CONFIG_MIPS32_O32)
|
||||
return 1;
|
||||
#elif defined(CONFIG_64BIT) && defined(CONFIG_MIPS32_O32)
|
||||
return !test_thread_flag(TIF_32BIT_REGS);
|
||||
#else
|
||||
#elif defined(CONFIG_32BIT) && !defined(CONFIG_MIPS_O32_FP64_SUPPORT)
|
||||
return 0;
|
||||
#else
|
||||
return !test_thread_flag(TIF_32BIT_FPREGS);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -89,8 +89,9 @@ int fpu_emulator_save_context32(struct sigcontext32 __user *sc)
|
|||
{
|
||||
int i;
|
||||
int err = 0;
|
||||
int inc = test_thread_flag(TIF_32BIT_FPREGS) ? 2 : 1;
|
||||
|
||||
for (i = 0; i < 32; i+=2) {
|
||||
for (i = 0; i < 32; i += inc) {
|
||||
err |=
|
||||
__put_user(current->thread.fpu.fpr[i], &sc->sc_fpregs[i]);
|
||||
}
|
||||
|
@ -103,8 +104,9 @@ int fpu_emulator_restore_context32(struct sigcontext32 __user *sc)
|
|||
{
|
||||
int i;
|
||||
int err = 0;
|
||||
int inc = test_thread_flag(TIF_32BIT_FPREGS) ? 2 : 1;
|
||||
|
||||
for (i = 0; i < 32; i+=2) {
|
||||
for (i = 0; i < 32; i += inc) {
|
||||
err |=
|
||||
__get_user(current->thread.fpu.fpr[i], &sc->sc_fpregs[i]);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue