diff -U 6 -p ./arch/x86/configs/i386_defconfig.v21 ./arch/x86/configs/i386_defconfig --- ./arch/x86/configs/i386_defconfig.v21 2015-09-24 02:07:37.000000000 +0900 +++ ./arch/x86/configs/i386_defconfig 2015-09-29 12:57:21.000000000 +0900 @@ -253,12 +253,13 @@ CONFIG_X86_XADD=y # CONFIG_X86_PPRO_FENCE is not set # CONFIG_CPU_EMU486 is not set # CONFIG_CPU_EMU686 is not set # CONFIG_CPU_EMU486_DEBUG is not set # CONFIG_GEODE_NOPL is not set # CONFIG_CPU_EMU_FUCOMI is not set +# CONFIG_CPU_PROC_EMULATED_OPS is not set CONFIG_X86_WP_WORKS_OK=y CONFIG_X86_INVLPG=y CONFIG_X86_BSWAP=y CONFIG_X86_POPAD_OK=y CONFIG_X86_INTEL_USERCOPY=y CONFIG_X86_USE_PPRO_CHECKSUM=y diff -U 6 -p ./arch/x86/Kconfig.cpu.v21 ./arch/x86/Kconfig.cpu --- ./arch/x86/Kconfig.cpu.v21 2015-09-24 02:07:37.000000000 +0900 +++ ./arch/x86/Kconfig.cpu 2015-09-29 13:21:53.000000000 +0900 @@ -388,12 +388,21 @@ config CPU_EMU_FUCOMI This option should not be left on by default, because it means that you execute a program not targetted for your CPU. You should recompile your applications whenever possible. If you are not sure, say N. +config CPU_PROC_EMULATED_OPS + bool "/proc/emulated_ops support" + depends on X86_32 && CPU_EMU486 + ---help--- + /proc/emulated_ops will show counts of emulated + CMOV, NOPL, FCOMI/FCOMIP, FUCOMI/FUCOMIP and FCMOVcc instructions. + + If you are not sure, say N. + config CPU_EMU486_DEBUG bool "Emulation debug" depends on X86_32 && CPU_EMU486 ---help--- Shows hexcode of instruction we could not handle. diff -U 6 -p ./arch/x86/kernel/traps.c.v21 ./arch/x86/kernel/traps.c --- ./arch/x86/kernel/traps.c.v21 2015-09-24 02:07:37.000000000 +0900 +++ ./arch/x86/kernel/traps.c 2015-09-29 17:08:16.000000000 +0900 @@ -42,12 +42,17 @@ #endif #if defined(CONFIG_EDAC) #include #endif +#ifdef CONFIG_CPU_PROC_EMULATED_OPS +#include +#include +#endif + #include #include #include #include #include #include @@ -826,12 +831,56 @@ static inline void *reg_address(struct p return ((reg & 4) >> 2) + (u8 *)REG_PTR(regs, reg & 3); /* this is set just to prevent the compiler from complaining */ return NULL; } +#ifdef CONFIG_CPU_PROC_EMULATED_OPS +static struct { + unsigned long cmov; + unsigned long nopl; + unsigned long fcomi; + unsigned long fucomi; + unsigned long fcmov; +} emulated_ops_counter = { + 0, 0, 0, 0, 0 +}; +static int emulated_ops_proc_show(struct seq_file *m, void *v) +{ +#if defined(CONFIG_CPU_EMU686) + seq_printf(m, "cmov: %lu\n", emulated_ops_counter.cmov); +#endif +#ifdef CONFIG_GEODE_NOPL + seq_printf(m, "nopl: %lu\n", emulated_ops_counter.nopl); +#endif +#ifdef CONFIG_CPU_EMU_FUCOMI + seq_printf(m, "fcomi: %lu\n" "fucomi: %lu\n" "fcmov: %lu\n", + emulated_ops_counter.fcomi, + emulated_ops_counter.fucomi, + emulated_ops_counter.fcmov); +#endif + return 0; +} +static int emulated_ops_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, emulated_ops_proc_show, NULL); +} +static const struct file_operations emulated_ops_proc_fops = { + .open = emulated_ops_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; +static int __init proc_emulated_ops_init(void) +{ + proc_create("emulated_ops", 0, NULL, &emulated_ops_proc_fops); + return 0; +} +module_init(proc_emulated_ops_init); +#endif /* CONFIG_CPU_PROC_EMULATED_OPS */ + /* [do_invalid_op] is called by exception 6 after an invalid opcode has been * encountered. It will decode the prefixes and the instruction code, to try * to emulate it, and will send a SIGILL or SIGSEGV to the process if not * possible. * REP/REPN prefixes are not supported anymore because it didn't make sense * to emulate instructions prefixed with such opcodes since no arch-specific @@ -861,12 +910,15 @@ dotraplinkage void do_invalid_op(struct /*do_nopl_emu*/ { int res = is_nopl(eip); if (res) { int i = 0; do { +#ifdef CONFIG_CPU_PROC_EMULATED_OPS + emulated_ops_counter.nopl++; +#endif eip += res; i++; res = is_nopl(eip); } while (res); #ifdef CONFIG_GEODE_NOPL_DEBUG printk(KERN_DEBUG "geode_nopl: emulated %d instructions\n", i); @@ -880,52 +932,67 @@ dotraplinkage void do_invalid_op(struct #ifdef CONFIG_CPU_EMU_FUCOMI /* FCOMI, FCOMIP, FUCOMI, FUCOMIP emulation */ if (eip[0] == 0xDB) { switch (eip[1] & 0xF8) { case 0xF0: /* FCOMI */ if (do_fcomi(regs)) { +#ifdef CONFIG_CPU_PROC_EMULATED_OPS + emulated_ops_counter.fcomi++; +#endif #ifdef CONFIG_CPU_FUCOMI_DEBUG printk(KERN_DEBUG "fcomi: emulated@%08x\n", eip); #endif return; } break; case 0xE8: /* FUCOMI */ if (do_fucomi(regs)) { +#ifdef CONFIG_CPU_PROC_EMULATED_OPS + emulated_ops_counter.fucomi++; +#endif #ifdef CONFIG_CPU_FUCOMI_DEBUG printk(KERN_DEBUG "fucomi: emulated@%08x\n", eip); #endif return; } break; } } if (eip[0] == 0xDF) { switch (eip[1] & 0xF8) { case 0xF0: /* FCOMIP */ if (do_fcomip(regs)) { +#ifdef CONFIG_CPU_PROC_EMULATED_OPS + emulated_ops_counter.fcomi++; +#endif #ifdef CONFIG_CPU_FUCOMI_DEBUG printk(KERN_DEBUG "fcomip: emulated@%08x\n", eip); #endif return; } break; case 0xE8: /* FUCOMIP */ if (do_fucomip(regs)) { +#ifdef CONFIG_CPU_PROC_EMULATED_OPS + emulated_ops_counter.fucomi++; +#endif #ifdef CONFIG_CPU_FUCOMI_DEBUG printk(KERN_DEBUG "fucomip: emulated@%08x\n", eip); #endif return; } break; } } /* FCMOVcc emulation */ if ((eip[0] & 0xFE) == 0xDA && (eip[1] & 0xE0) == 0xC0) { /* FCMOVcc */ int res = do_fcmov(regs); if (res) { +#ifdef CONFIG_CPU_PROC_EMULATED_OPS + emulated_ops_counter.fcmov++; +#endif #ifdef CONFIG_CPU_FUCOMI_DEBUG printk(KERN_DEBUG "fcmov: emulated@%08x %02x %02x\n", eip, eip[0], eip[1]); #endif return; } } @@ -978,12 +1045,15 @@ dotraplinkage void do_invalid_op(struct cond >>= 1; ncond ^= !!(flags & cmov_flags[cond]); /* ncond is now true if the cond matches the opcode */ modrm = *(eip + 1); eip += 2; /* skips all the opcodes */ +#ifdef CONFIG_CPU_PROC_EMULATED_OPS + emulated_ops_counter.cmov++; +#endif if (!ncond) { /* condition is not valid, skip the instruction and do nothing */ regs->ip = (u32)skip_modrm(eip, !(prefixes & PREFIX_A32), modrm); return; }