diff -p -U6 linux-2.6.32-431.el6.v18.i586/arch/x86/configs/i386_defconfig.distv13 linux-2.6.32-431.el6.v18.i586/arch/x86/configs/i386_defconfig --- linux-2.6.32-431.el6.v18.i586/arch/x86/configs/i386_defconfig.distv13 2014-06-03 20:09:21.000000000 +0900 +++ linux-2.6.32-431.el6.v18.i586/arch/x86/configs/i386_defconfig 2014-06-03 20:09:21.000000000 +0900 @@ -252,12 +252,13 @@ CONFIG_X86_L1_CACHE_SHIFT=5 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_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 -p -U6 linux-2.6.32-431.el6.v18.i586/arch/x86/Kconfig.cpu.distv13 linux-2.6.32-431.el6.v18.i586/arch/x86/Kconfig.cpu --- linux-2.6.32-431.el6.v18.i586/arch/x86/Kconfig.cpu.distv13 2014-06-03 20:09:21.000000000 +0900 +++ linux-2.6.32-431.el6.v18.i586/arch/x86/Kconfig.cpu 2014-06-03 20:21:51.000000000 +0900 @@ -371,12 +371,29 @@ config GEODE_NOPL This code can be used to allow the AMD Geode to hopefully correctly execute some code which was originally compiled for an i686, by emulating NOPL, the only missing i686 instruction in the CPU. If you are not sure, say N. +config CPU_EMU_FUCOMI + bool "Pentium-Pro FCOMI,FCMOVcc emulation" + depends on X86_32 && CPU_EMU486 + ---help--- + This code emulates FCOMI,FCOMIP,FUCOMI,FUCOMIP and FCMOVcc instructions + which gcc emits on -march=i686. + Needed when running .i686 codes on K6-2, WinChip or VIA C3 CPUs. + + With i387 this may not work, as i387 does not raise illegal trap and + just treats FCMOVcc as FNOP. + + 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_EMU486_DEBUG bool "Emulation debug" depends on X86_32 && CPU_EMU486 ---help--- Shows hexcode of instruction we could not handle. diff -p -U6 linux-2.6.32-431.el6.v18.i586/arch/x86/kernel/traps.c.distv13 linux-2.6.32-431.el6.v18.i586/arch/x86/kernel/traps.c --- linux-2.6.32-431.el6.v18.i586/arch/x86/kernel/traps.c.distv13 2014-06-03 20:09:21.000000000 +0900 +++ linux-2.6.32-431.el6.v18.i586/arch/x86/kernel/traps.c 2014-06-03 20:09:21.000000000 +0900 @@ -463,12 +463,206 @@ static inline int is_nopl(u8 *ip) return do_66(ip + 1); return 0; } #endif /* CONFIG_GEODE_NOPL */ +#ifdef CONFIG_CPU_EMU_FUCOMI +/* Here, we try to emulate FCOMI ST(i) family which gcc generates on + * -march=i686 . + * Taketo Kabe 2014 + */ +/* copy C3,C2,C0 flags to ZF,PF,CF */ +static inline long flags_CtoS(u16 fpustat, long flags) +{ + flags = flags & ~(X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF | + /* OF,SF,AF flags are cleared */ + X86_EFLAGS_OF | X86_EFLAGS_SF | X86_EFLAGS_AF); + flags = flags + | ((!!(fpustat & 0x4000)) << 6) /* ZF:=C3 */ + | ((!!(fpustat & 0x400)) << 2) /* PF:=C2 */ + | ((!!(fpustat & 0x100)) << 0); /* CF:=C0 */ + return flags; +} +/* Here, we try to emulate FCOMI ST(i) which gcc generates on -march=i686 . */ +static int do_fcomi(struct pt_regs *regs) /* return bytes to inc eip, 0 for "not found" */ +{ + u8 *eip = (u8 *)regs->ip; + int stidx; + u16 ret; + stidx = (*(eip+1) & 0x07); /* lower 3 bits */ +#define EMU_FCOMI_ASM(i, ret) \ + __asm__ __volatile__ ( \ + "fcom %%st(" #i ")\n\t" /*XXX clobbers FPU C3,C2,C0*/ \ + "fnstsw %%ax \n\t" \ + "mov %%ax,%0 \n\t" \ + : "=r" (ret) \ + : /* no input */ \ + : "ax" ) + switch (stidx) { + case 0: EMU_FCOMI_ASM(0, ret); break; + case 1: EMU_FCOMI_ASM(1, ret); break; + case 2: EMU_FCOMI_ASM(2, ret); break; + case 3: EMU_FCOMI_ASM(3, ret); break; + case 4: EMU_FCOMI_ASM(4, ret); break; + case 5: EMU_FCOMI_ASM(5, ret); break; + case 6: EMU_FCOMI_ASM(6, ret); break; + case 7: EMU_FCOMI_ASM(7, ret); break; + default: /*cannnot happen*/ + return 0; + } + /* now ret has FPU status flags, copy them into ZF,PF,CF */ + regs->flags = flags_CtoS(ret, regs->flags); + regs->ip = (u32)(eip + 2); + return 2; +} +/* Here, we try to emulate FCOMIP ST(i) which gcc generates on -march=i686 . */ +static int do_fcomip(struct pt_regs *regs) /* return bytes to inc eip, 0 for "not found" */ +{ + u8 *eip = (u8 *)regs->ip; + int stidx; + u16 ret; + stidx = (*(eip+1) & 0x07); /* lower 3 bits */ +#define EMU_FCOMIP_ASM(i, ret) \ + __asm__ __volatile__ ( \ + "fcomp %%st(" #i ")\n\t" /*XXX clobbers FPU C3,C2,C0*/ \ + "fnstsw %%ax \n\t" \ + "mov %%ax,%0 \n\t" \ + : "=r" (ret) \ + : /* no input */ \ + : "ax" ) + switch (stidx) { + case 0: EMU_FCOMIP_ASM(0, ret); break; + case 1: EMU_FCOMIP_ASM(1, ret); break; + case 2: EMU_FCOMIP_ASM(2, ret); break; + case 3: EMU_FCOMIP_ASM(3, ret); break; + case 4: EMU_FCOMIP_ASM(4, ret); break; + case 5: EMU_FCOMIP_ASM(5, ret); break; + case 6: EMU_FCOMIP_ASM(6, ret); break; + case 7: EMU_FCOMIP_ASM(7, ret); break; + default: /*cannnot happen*/ + return 0; + } + /* now ret has FPU status flags, copy them into ZF,PF,CF */ + regs->flags = flags_CtoS(ret, regs->flags); + regs->ip = (u32)(eip + 2); + return 2; +} +/* Here, we try to emulate FUCOMI ST(i) which gcc generates on -march=i686 . */ +static int do_fucomi(struct pt_regs *regs) /* return bytes to inc eip, 0 for "not found" */ +{ + u8 *eip = (u8 *)regs->ip; + int stidx; + u16 ret; + stidx = (*(eip+1) & 0x07); /* lower 3 bits */ +#define EMU_FUCOMI_ASM(i, ret) \ + __asm__ __volatile__ ( \ + "fucom %%st(" #i ")\n\t" /*XXX clobbers FPU C3,C2,C0*/ \ + "fnstsw %%ax \n\t" \ + "mov %%ax,%0 \n\t" \ + : "=r" (ret) \ + : /* no input */ \ + : "ax" ) + switch (stidx) { + case 0: EMU_FUCOMI_ASM(0, ret); break; + case 1: EMU_FUCOMI_ASM(1, ret); break; + case 2: EMU_FUCOMI_ASM(2, ret); break; + case 3: EMU_FUCOMI_ASM(3, ret); break; + case 4: EMU_FUCOMI_ASM(4, ret); break; + case 5: EMU_FUCOMI_ASM(5, ret); break; + case 6: EMU_FUCOMI_ASM(6, ret); break; + case 7: EMU_FUCOMI_ASM(7, ret); break; + default: /*cannnot happen*/ + return 0; + } + /* now ret has FPU status flags, copy them into ZF,PF,CF */ + regs->flags = flags_CtoS(ret, regs->flags); + regs->ip = (u32)(eip + 2); + return 2; +} +/* Here, we try to emulate FUCOMIP ST(i) which gcc generates on -march=i686 . */ +static int do_fucomip(struct pt_regs *regs) /* return bytes to inc eip, 0 for "not found" */ +{ + u8 *eip = (u8 *)regs->ip; + int stidx; + u16 ret; + stidx = (*(eip+1) & 0x07); /* lower 3 bits */ +#define EMU_FUCOMIP_ASM(i, ret) \ + __asm__ __volatile__ ( \ + "fucomp %%st(" #i ")\n\t" /*XXX clobbers FPU C3,C2,C0*/ \ + "fnstsw %%ax \n\t" \ + "mov %%ax,%0 \n\t" \ + : "=r" (ret) \ + : /* no input */ \ + : "ax" ) + switch (stidx) { + case 0: EMU_FUCOMIP_ASM(0, ret); break; + case 1: EMU_FUCOMIP_ASM(1, ret); break; + case 2: EMU_FUCOMIP_ASM(2, ret); break; + case 3: EMU_FUCOMIP_ASM(3, ret); break; + case 4: EMU_FUCOMIP_ASM(4, ret); break; + case 5: EMU_FUCOMIP_ASM(5, ret); break; + case 6: EMU_FUCOMIP_ASM(6, ret); break; + case 7: EMU_FUCOMIP_ASM(7, ret); break; + default: /*cannnot happen*/ + return 0; + } + /* now ret has FPU status flags, copy them into ZF,PF,CF */ + regs->flags = flags_CtoS(ret, regs->flags); + regs->ip = (u32)(eip + 2); + return 2; +} +/* Here, we try to emulate FCMOVcc ST(i) which gcc generates on -march=i686 . */ +static int do_fcmov(struct pt_regs *regs) /* return bytes to inc eip, 0 for "not found" */ +{ + u8 *eip = (u8 *)regs->ip; + long cond; + int stidx; + /* lookup table for opcode bit4-3 to eflags mask */ + static long code2flags[4] = { + X86_EFLAGS_CF, /* 0x01 */ + X86_EFLAGS_ZF, /* 0x40 */ + X86_EFLAGS_CF | X86_EFLAGS_ZF, /* 0x41 */ + X86_EFLAGS_PF /* 0x04 */ + }; + /* extract the interested flags */ + cond = regs->flags & code2flags[(eip[1] & 0x18) >> 3]; + /* reverse the condition if this is FCMOV_Ncc */ + if (eip[0] == 0xDB) cond = !cond; + + if (!cond) { + /* do nothing, skip over */ + regs->ip = (u32)(eip + 2); + return 2; + } + /* do it */ + stidx = (eip[1] & 0x07); /* lower 3 bits */ +#define EMU_FCMOVCC_ASM(i) \ + __asm__ __volatile__ ( \ + "fstp %%st(0) \n\t" /* pop */ \ + "fld %%st(" #i ") \n\t" /* push val */ \ + : /* no output */ \ + : /* no input */ \ + ) + switch (stidx) { + case 0: /* nop */ break; + case 1: EMU_FCMOVCC_ASM(0); break; + case 2: EMU_FCMOVCC_ASM(1); break; + case 3: EMU_FCMOVCC_ASM(2); break; + case 4: EMU_FCMOVCC_ASM(3); break; + case 5: EMU_FCMOVCC_ASM(4); break; + case 6: EMU_FCMOVCC_ASM(5); break; + case 7: EMU_FCMOVCC_ASM(6); break; + default: /*cannnot happen*/ + return 0; + } + regs->ip = (u32)(eip + 2); + return 2; +} +#endif /* CONFIG_CPU_EMU_FUCOMI */ + #if defined(CONFIG_CPU_EMU486) || defined(CONFIG_CPU_EMU686) /* This code can be used to allow old 386's to hopefully correctly execute some * code which was originally compiled for a 486, and to allow CMOV-disabled * processors to emulate CMOV instructions. In user space, only 3 instructions * have been added between the 386 the 486 : * - BSWAP reg performs exactly htonl()) @@ -502,20 +696,25 @@ static void *modrm_address(struct pt_reg */ rm = modrm & 7; mod = modrm & 0xC0; if (bit32) { /* 32-bits addressing mode (default) */ if (mod == 0 && rm == 5) { /* 32 bits offset and nothing more */ + /* the "ugly line" didn't compile correctly; + * the "clean" code does generate good enough asm. + * -- kabe + */ /*return (void *) (*((u32 *)*from))++;*/ offset = *((u32 *)*from); (*from) += 4; return (void *)offset; } if (rm == 4) { /* SIB byte is present and must be used */ - sib = *(*from)++; /* SS(7-6) IDX(5-3) BASE(2-0) */ + sib = **from; /* SS(7-6) IDX(5-3) BASE(2-0) */ + (*from)++; /* index * scale */ if (((sib >> 3) & 7) != 4) offset += *REG_PTR(regs, (sib >> 3) & 7) << (sib >> 6); rm = (sib & 7); /* base replaces rm from now */ @@ -686,12 +885,66 @@ dotraplinkage void do_invalid_op(struct regs->ip = (typeof(regs->ip))eip; return; } } #endif /* CONFIG_GEODE_NOPL */ +#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_FUCOMI_DEBUG + printk(KERN_DEBUG "fcomi: emulated@%08x\n", eip); +#endif + return; + } + break; + case 0xE8: /* FUCOMI */ + if (do_fucomi(regs)) { +#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_FUCOMI_DEBUG + printk(KERN_DEBUG "fcomip: emulated@%08x\n", eip); +#endif + return; + } + break; + case 0xE8: /* FUCOMIP */ + if (do_fucomip(regs)) { +#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_FUCOMI_DEBUG + printk(KERN_DEBUG "fcmov: emulated@%08x %02x %02x\n", eip, eip[0], eip[1]); +#endif + return; + } + } +#endif /* CONFIG_CPU_EMU_FUCOMI */ + #ifdef BENCH_CPU_EXCEPTION_BUT_NOT_THE_CODE regs->ip += 3; return; #endif /* we'll first read all known opcode prefixes, and discard obviously invalid combinations.*/