diff options
Diffstat (limited to 'arch/powerpc/kernel/traps.c')
| -rw-r--r-- | arch/powerpc/kernel/traps.c | 96 | 
1 files changed, 94 insertions, 2 deletions
diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c index 678fbff0d20..6a5b2b731f4 100644 --- a/arch/powerpc/kernel/traps.c +++ b/arch/powerpc/kernel/traps.c @@ -33,7 +33,9 @@  #include <linux/backlight.h>  #include <linux/bug.h>  #include <linux/kdebug.h> +#include <linux/debugfs.h> +#include <asm/emulated_ops.h>  #include <asm/pgtable.h>  #include <asm/uaccess.h>  #include <asm/system.h> @@ -757,36 +759,44 @@ static int emulate_instruction(struct pt_regs *regs)  	/* Emulate the mfspr rD, PVR. */  	if ((instword & PPC_INST_MFSPR_PVR_MASK) == PPC_INST_MFSPR_PVR) { +		PPC_WARN_EMULATED(mfpvr);  		rd = (instword >> 21) & 0x1f;  		regs->gpr[rd] = mfspr(SPRN_PVR);  		return 0;  	}  	/* Emulating the dcba insn is just a no-op.  */ -	if ((instword & PPC_INST_DCBA_MASK) == PPC_INST_DCBA) +	if ((instword & PPC_INST_DCBA_MASK) == PPC_INST_DCBA) { +		PPC_WARN_EMULATED(dcba);  		return 0; +	}  	/* Emulate the mcrxr insn.  */  	if ((instword & PPC_INST_MCRXR_MASK) == PPC_INST_MCRXR) {  		int shift = (instword >> 21) & 0x1c;  		unsigned long msk = 0xf0000000UL >> shift; +		PPC_WARN_EMULATED(mcrxr);  		regs->ccr = (regs->ccr & ~msk) | ((regs->xer >> shift) & msk);  		regs->xer &= ~0xf0000000UL;  		return 0;  	}  	/* Emulate load/store string insn. */ -	if ((instword & PPC_INST_STRING_GEN_MASK) == PPC_INST_STRING) +	if ((instword & PPC_INST_STRING_GEN_MASK) == PPC_INST_STRING) { +		PPC_WARN_EMULATED(string);  		return emulate_string_inst(regs, instword); +	}  	/* Emulate the popcntb (Population Count Bytes) instruction. */  	if ((instword & PPC_INST_POPCNTB_MASK) == PPC_INST_POPCNTB) { +		PPC_WARN_EMULATED(popcntb);  		return emulate_popcntb_inst(regs, instword);  	}  	/* Emulate isel (Integer Select) instruction */  	if ((instword & PPC_INST_ISEL_MASK) == PPC_INST_ISEL) { +		PPC_WARN_EMULATED(isel);  		return emulate_isel(regs, instword);  	} @@ -984,6 +994,8 @@ void SoftwareEmulation(struct pt_regs *regs)  #ifdef CONFIG_MATH_EMULATION  	errcode = do_mathemu(regs); +	if (errcode >= 0) +		PPC_WARN_EMULATED(math);  	switch (errcode) {  	case 0: @@ -1005,6 +1017,9 @@ void SoftwareEmulation(struct pt_regs *regs)  #elif defined(CONFIG_8XX_MINIMAL_FPEMU)  	errcode = Soft_emulate_8xx(regs); +	if (errcode >= 0) +		PPC_WARN_EMULATED(8xx); +  	switch (errcode) {  	case 0:  		emulate_single_step(regs); @@ -1088,6 +1103,7 @@ void altivec_assist_exception(struct pt_regs *regs)  	flush_altivec_to_thread(current); +	PPC_WARN_EMULATED(altivec);  	err = emulate_altivec(regs);  	if (err == 0) {  		regs->nip += 4;		/* skip emulated instruction */ @@ -1286,3 +1302,79 @@ void kernel_bad_stack(struct pt_regs *regs)  void __init trap_init(void)  {  } + + +#ifdef CONFIG_PPC_EMULATED_STATS + +#define WARN_EMULATED_SETUP(type)	.type = { .name = #type } + +struct ppc_emulated ppc_emulated = { +#ifdef CONFIG_ALTIVEC +	WARN_EMULATED_SETUP(altivec), +#endif +	WARN_EMULATED_SETUP(dcba), +	WARN_EMULATED_SETUP(dcbz), +	WARN_EMULATED_SETUP(fp_pair), +	WARN_EMULATED_SETUP(isel), +	WARN_EMULATED_SETUP(mcrxr), +	WARN_EMULATED_SETUP(mfpvr), +	WARN_EMULATED_SETUP(multiple), +	WARN_EMULATED_SETUP(popcntb), +	WARN_EMULATED_SETUP(spe), +	WARN_EMULATED_SETUP(string), +	WARN_EMULATED_SETUP(unaligned), +#ifdef CONFIG_MATH_EMULATION +	WARN_EMULATED_SETUP(math), +#elif defined(CONFIG_8XX_MINIMAL_FPEMU) +	WARN_EMULATED_SETUP(8xx), +#endif +#ifdef CONFIG_VSX +	WARN_EMULATED_SETUP(vsx), +#endif +}; + +u32 ppc_warn_emulated; + +void ppc_warn_emulated_print(const char *type) +{ +	if (printk_ratelimit()) +		pr_warning("%s used emulated %s instruction\n", current->comm, +			   type); +} + +static int __init ppc_warn_emulated_init(void) +{ +	struct dentry *dir, *d; +	unsigned int i; +	struct ppc_emulated_entry *entries = (void *)&ppc_emulated; + +	if (!powerpc_debugfs_root) +		return -ENODEV; + +	dir = debugfs_create_dir("emulated_instructions", +				 powerpc_debugfs_root); +	if (!dir) +		return -ENOMEM; + +	d = debugfs_create_u32("do_warn", S_IRUGO | S_IWUSR, dir, +			       &ppc_warn_emulated); +	if (!d) +		goto fail; + +	for (i = 0; i < sizeof(ppc_emulated)/sizeof(*entries); i++) { +		d = debugfs_create_u32(entries[i].name, S_IRUGO | S_IWUSR, dir, +				       (u32 *)&entries[i].val.counter); +		if (!d) +			goto fail; +	} + +	return 0; + +fail: +	debugfs_remove_recursive(dir); +	return -ENOMEM; +} + +device_initcall(ppc_warn_emulated_init); + +#endif /* CONFIG_PPC_EMULATED_STATS */  |