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:
parent
b55b9e2715
commit
b0a668fb20
@ -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
|
||||
|
@ -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,
|
||||
|
96
arch/mips/include/asm/mips-r2-to-r6-emul.h
Normal file
96
arch/mips/include/asm/mips-r2-to-r6-emul.h
Normal 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 */
|
@ -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)
|
||||
|
||||
|
@ -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>
|
||||
|
||||
|
2378
arch/mips/kernel/mips-r2-to-r6-emul.c
Normal file
2378
arch/mips/kernel/mips-r2-to-r6-emul.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -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 @@ asmlinkage void do_fpe(struct pt_regs *regs, unsigned long fcr31)
|
||||
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,
|
||||
¤t->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;
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user