diff options
author | David Daney <ddaney@caviumnetworks.com> | 2009-11-02 11:33:46 -0800 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2009-11-13 18:10:37 +0100 |
commit | da0bac33413b2888d3623dad3ad19ce76b688f07 (patch) | |
tree | 6d95ce81145b0a11868dd38f5571f58d9d8a79e6 | |
parent | 156171c71a0dc4bce12b4408bb1591f8fe32dc1a (diff) |
MIPS: Fix emulation of 64-bit FPU on FPU-less 64-bit CPUs.
Running a 64-bit kernel on a 64-bit CPU without an FPU would cause the
emulator to run in 32-bit mode. The c0_Status.FR bit is wired to zero
on systems without an FPU, so using that bit to decide how the emulator
behaves doesn't allow for proper emulation on 64-bit FPU-less
processors.
Instead, we need to select the emulator mode based on the user-space
ABI. Since the thread flag TIF_32BIT_REGS is used to set c0_Status.FR,
we can just use it to decide if the emulator should be in 32-bit or
64-bit mode.
Signed-off-by: David Daney <ddaney@caviumnetworks.com>
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
-rw-r--r-- | arch/mips/math-emu/cp1emu.c | 41 |
1 files changed, 21 insertions, 20 deletions
diff --git a/arch/mips/math-emu/cp1emu.c b/arch/mips/math-emu/cp1emu.c index 890f77927d62..454b53924490 100644 --- a/arch/mips/math-emu/cp1emu.c +++ b/arch/mips/math-emu/cp1emu.c @@ -163,33 +163,34 @@ static int isBranchInstr(mips_instruction * i) /* * In the Linux kernel, we support selection of FPR format on the - * basis of the Status.FR bit. This does imply that, if a full 32 - * FPRs are desired, there needs to be a flip-flop that can be written - * to one at that bit position. In any case, O32 MIPS ABI uses - * only the even FPRs (Status.FR = 0). + * 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. For 64-bit kernels with no FPU we use TIF_32BIT_REGS + * as a proxy for the FR bit so that a 64-bit FPU is emulated. In any + * case, for a 32-bit kernel which uses the O32 MIPS ABI, only the + * even FPRs are used (Status.FR = 0). */ - -#define CP0_STATUS_FR_SUPPORT - -#ifdef CP0_STATUS_FR_SUPPORT -#define FR_BIT ST0_FR +static inline int cop1_64bit(struct pt_regs *xcp) +{ + if (cpu_has_fpu) + return xcp->cp0_status & ST0_FR; +#ifdef CONFIG_64BIT + return !test_thread_flag(TIF_32BIT_REGS); #else -#define FR_BIT 0 + return 0; #endif +} + +#define SIFROMREG(si, x) ((si) = cop1_64bit(xcp) || !(x & 1) ? \ + (int)ctx->fpr[x] : (int)(ctx->fpr[x & ~1] >> 32)) -#define SIFROMREG(si, x) ((si) = \ - (xcp->cp0_status & FR_BIT) || !(x & 1) ? \ - (int)ctx->fpr[x] : \ - (int)(ctx->fpr[x & ~1] >> 32 )) -#define SITOREG(si, x) (ctx->fpr[x & ~((xcp->cp0_status & FR_BIT) == 0)] = \ - (xcp->cp0_status & FR_BIT) || !(x & 1) ? \ +#define SITOREG(si, x) (ctx->fpr[x & ~(cop1_64bit(xcp) == 0)] = \ + cop1_64bit(xcp) || !(x & 1) ? \ ctx->fpr[x & ~1] >> 32 << 32 | (u32)(si) : \ ctx->fpr[x & ~1] << 32 >> 32 | (u64)(si) << 32) -#define DIFROMREG(di, x) ((di) = \ - ctx->fpr[x & ~((xcp->cp0_status & FR_BIT) == 0)]) -#define DITOREG(di, x) (ctx->fpr[x & ~((xcp->cp0_status & FR_BIT) == 0)] \ - = (di)) +#define DIFROMREG(di, x) ((di) = ctx->fpr[x & ~(cop1_64bit(xcp) == 0)]) +#define DITOREG(di, x) (ctx->fpr[x & ~(cop1_64bit(xcp) == 0)] = (di)) #define SPFROMREG(sp, x) SIFROMREG((sp).bits, x) #define SPTOREG(sp, x) SITOREG((sp).bits, x) |