MIPS: kernel: mips-r2-to-r6-emul: Add R2 emulator for MIPS R6

MIPS R6 removed quite a few R2 instructions. However, there
is plenty of <R6 userland code so we add an in-kernel emulator
so we can still be able to execute all R2 userland out there.

The emulator comes with a handy debugfs under /mips/ directory
(r2-emul-stats) to provide some basic statistics of the
instructions that are being emulated.

Below are some statistics from booting a minimal buildroot image:

Instruction     Total   BDslot
------------------------------
movs            236969  0
hilo            56686   0
muls            55279   0
divs            10941   0
dsps            0       0
bops            1       0
traps           0       0
fpus            0       0
loads           214981  17
stores          103364  0
llsc            56898   0
dsemul          150418  0
jr              370158
bltzl           43
bgezl           1594
bltzll          0
bgezll          0
bltzal          39
bgezal          39
beql            14503
bnel            138741
blezl           0
bgtzl           3988

Signed-off-by: Leonid Yegoshin <Leonid.Yegoshin@imgtec.com>
Signed-off-by: Markos Chandras <markos.chandras@imgtec.com>
This commit is contained in:
Leonid Yegoshin 2014-12-03 15:47:03 +00:00 committed by Markos Chandras
parent b55b9e2715
commit b0a668fb20
8 changed files with 2518 additions and 5 deletions

View File

@ -2063,6 +2063,19 @@ config MIPS_MT_FPAFF
default y
depends on MIPS_MT_SMP
config MIPSR2_TO_R6_EMULATOR
bool "MIPS R2-to-R6 emulator"
depends on CPU_MIPSR6 && !SMP
default y
help
Choose this option if you want to run non-R6 MIPS userland code.
Even if you say 'Y' here, the emulator will still be disabled by
default. You can enable it using the 'mipsr2emul' kernel option.
The only reason this is a build-time option is to save ~14K from the
final kernel image.
comment "MIPS R2-to-R6 emulator is only available for UP kernels"
depends on SMP && CPU_MIPSR6
config MIPS_VPE_LOADER
bool "VPE loader support."
depends on SYS_SUPPORTS_MULTITHREADING && MODULES

View File

@ -13,9 +13,6 @@
#include <asm/ptrace.h>
#include <asm/inst.h>
static int mipsr2_emulation = 0;
#define NO_R6EMU (cpu_has_mips_r6 && !mipsr2_emulation)
extern int __isa_exception_epc(struct pt_regs *regs);
extern int __compute_return_epc(struct pt_regs *regs);
extern int __compute_return_epc_for_insn(struct pt_regs *regs,

View File

@ -0,0 +1,96 @@
/*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (c) 2014 Imagination Technologies Ltd.
* Author: Markos Chandras <markos.chandras@imgtec.com>
*/
#ifndef __ASM_MIPS_R2_TO_R6_EMUL_H
#define __ASM_MIPS_R2_TO_R6_EMUL_H
struct mips_r2_emulator_stats {
u64 movs;
u64 hilo;
u64 muls;
u64 divs;
u64 dsps;
u64 bops;
u64 traps;
u64 fpus;
u64 loads;
u64 stores;
u64 llsc;
u64 dsemul;
};
struct mips_r2br_emulator_stats {
u64 jrs;
u64 bltzl;
u64 bgezl;
u64 bltzll;
u64 bgezll;
u64 bltzall;
u64 bgezall;
u64 bltzal;
u64 bgezal;
u64 beql;
u64 bnel;
u64 blezl;
u64 bgtzl;
};
#ifdef CONFIG_DEBUG_FS
#define MIPS_R2_STATS(M) \
do { \
u32 nir; \
int err; \
\
preempt_disable(); \
__this_cpu_inc(mipsr2emustats.M); \
err = __get_user(nir, (u32 __user *)regs->cp0_epc); \
if (!err) { \
if (nir == BREAK_MATH) \
__this_cpu_inc(mipsr2bdemustats.M); \
} \
preempt_enable(); \
} while (0)
#define MIPS_R2BR_STATS(M) \
do { \
preempt_disable(); \
__this_cpu_inc(mipsr2bremustats.M); \
preempt_enable(); \
} while (0)
#else
#define MIPS_R2_STATS(M) do { } while (0)
#define MIPS_R2BR_STATS(M) do { } while (0)
#endif /* CONFIG_DEBUG_FS */
struct r2_decoder_table {
u32 mask;
u32 code;
int (*func)(struct pt_regs *regs, u32 inst);
};
extern void do_trap_or_bp(struct pt_regs *regs, unsigned int code,
const char *str);
#ifndef CONFIG_MIPSR2_TO_R6_EMULATOR
static int mipsr2_emulation;
static __maybe_unused int mipsr2_decoder(struct pt_regs *regs, u32 inst) { return 0; };
#else
/* MIPS R2 Emulator ON/OFF */
extern int mipsr2_emulation;
extern int mipsr2_decoder(struct pt_regs *regs, u32 inst);
#endif /* CONFIG_MIPSR2_TO_R6_EMULATOR */
#define NO_R6EMU (cpu_has_mips_r6 && !mipsr2_emulation)
#endif /* __ASM_MIPS_R2_TO_R6_EMUL_H */

View File

@ -90,6 +90,7 @@ obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
obj-$(CONFIG_EARLY_PRINTK_8250) += early_printk_8250.o
obj-$(CONFIG_SPINLOCK_TEST) += spinlock_test.o
obj-$(CONFIG_MIPS_MACHINE) += mips_machine.o
obj-$(CONFIG_MIPSR2_TO_R6_EMULATOR) += mips-r2-to-r6-emul.o
CFLAGS_cpu-bugs64.o = $(shell if $(CC) $(KBUILD_CFLAGS) -Wa,-mdaddi -c -o /dev/null -x c /dev/null >/dev/null 2>&1; then echo "-DHAVE_AS_SET_DADDI"; fi)

View File

@ -16,6 +16,7 @@
#include <asm/fpu.h>
#include <asm/fpu_emulator.h>
#include <asm/inst.h>
#include <asm/mips-r2-to-r6-emul.h>
#include <asm/ptrace.h>
#include <asm/uaccess.h>

File diff suppressed because it is too large Load Diff

View File

@ -46,6 +46,7 @@
#include <asm/fpu.h>
#include <asm/fpu_emulator.h>
#include <asm/idle.h>
#include <asm/mips-r2-to-r6-emul.h>
#include <asm/mipsregs.h>
#include <asm/mipsmtregs.h>
#include <asm/module.h>
@ -837,7 +838,7 @@ out:
exception_exit(prev_state);
}
static void do_trap_or_bp(struct pt_regs *regs, unsigned int code,
void do_trap_or_bp(struct pt_regs *regs, unsigned int code,
const char *str)
{
siginfo_t info;
@ -1027,7 +1028,32 @@ asmlinkage void do_ri(struct pt_regs *regs)
unsigned int opcode = 0;
int status = -1;
/*
* Avoid any kernel code. Just emulate the R2 instruction
* as quickly as possible.
*/
if (mipsr2_emulation && cpu_has_mips_r6 &&
likely(user_mode(regs))) {
if (likely(get_user(opcode, epc) >= 0)) {
status = mipsr2_decoder(regs, opcode);
switch (status) {
case 0:
case SIGEMT:
return;
case SIGILL:
goto no_r2_instr;
default:
process_fpemu_return(status,
&current->thread.cp0_baduaddr);
return;
}
}
}
no_r2_instr:
prev_state = exception_enter();
if (notify_die(DIE_RI, "RI Fault", regs, 0, regs_to_trapnr(regs),
SIGILL) == NOTIFY_STOP)
goto out;

View File

@ -48,6 +48,7 @@
#include <asm/processor.h>
#include <asm/fpu_emulator.h>
#include <asm/fpu.h>
#include <asm/mips-r2-to-r6-emul.h>
#include "ieee754.h"
@ -68,7 +69,7 @@ static int fpux_emu(struct pt_regs *,
#define modeindex(v) ((v) & FPU_CSR_RM)
/* convert condition code register number to csr bit */
static const unsigned int fpucondbit[8] = {
const unsigned int fpucondbit[8] = {
FPU_CSR_COND0,
FPU_CSR_COND1,
FPU_CSR_COND2,