diff -up ./arch/x86/Kconfig.cpu.fucomi ./arch/x86/Kconfig.cpu --- ./arch/x86/Kconfig.cpu.fucomi 2026-05-29 18:50:49.969150158 +0900 +++ ./arch/x86/Kconfig.cpu 2026-05-29 19:00:46.315560066 +0900 @@ -395,6 +395,23 @@ config IGNORE_SSP_OPCODE 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 diff -up ./arch/x86/configs/i386_defconfig.fucomi ./arch/x86/configs/i386_defconfig --- ./arch/x86/configs/i386_defconfig.fucomi 2026-05-29 18:50:49.970150160 +0900 +++ ./arch/x86/configs/i386_defconfig 2026-05-29 19:00:46.315560066 +0900 @@ -34,6 +34,7 @@ CONFIG_SMP=y # CONFIG_CPU_EMU486_DEBUG is not set # CONFIG_GEODE_NOPL is not set # CONFIG_IGNORE_SSP_OPCODE is not set +# CONFIG_CPU_EMU_FUCOMI is not set CONFIG_HYPERVISOR_GUEST=y CONFIG_PARAVIRT=y CONFIG_NR_CPUS=8 diff -up ./arch/x86/kernel/traps.c.fucomi ./arch/x86/kernel/traps.c --- ./arch/x86/kernel/traps.c.fucomi 2026-05-29 18:50:49.971150161 +0900 +++ ./arch/x86/kernel/traps.c 2026-05-29 19:00:46.316560069 +0900 @@ -410,6 +410,210 @@ static inline int is_nopl(u8 *ip) #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 __user *eip = (u8 *)regs->ip; + int stidx; + u16 ret; + u8 op2; + get_user(op2, eip+1); + stidx = (op2 & 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 __user *eip = (u8 *)regs->ip; + int stidx; + u16 ret; + u8 op2; + get_user(op2, eip+1); + stidx = (op2 & 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 __user *eip = (u8 *)regs->ip; + int stidx; + u16 ret; + u8 op2; + get_user(op2, eip+1); + stidx = (op2 & 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 __user *eip = (u8 *)regs->ip; + int stidx; + u16 ret; + u8 op2; + get_user(op2, eip+1); + stidx = (op2 & 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 __user *eip = (u8 *)regs->ip; + long cond; + int stidx; + u8 op1, op2; + /* 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 */ + }; + get_user(op1, eip); get_user(op2, eip+1); + /* extract the interested flags */ + cond = regs->flags & code2flags[(op2 & 0x18) >> 3]; + /* reverse the condition if this is FCMOV_Ncc */ + if (op1 == 0xDB) cond = !cond; + + if (!cond) { + /* do nothing, skip over */ + regs->ip = (u32)(eip + 2); + return 2; + } + /* do it */ + stidx = (op2 & 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 @@ -638,7 +842,9 @@ static void handle_invalid_op(struct pt_ PREFIX_REP = 1024 } prefixes = 0; u32 *src, *dst; - u8 *eip = (u8 *)regs->ip; + u8 __user *eip = (u8 *)regs->ip; + u8 op1, op2; + get_user(op1, eip); get_user(op2, eip+1); #ifdef CONFIG_IGNORE_SSP_OPCODE { @@ -684,6 +890,60 @@ static void handle_invalid_op(struct pt_ } #endif /* CONFIG_GEODE_NOPL */ +#ifdef CONFIG_CPU_EMU_FUCOMI + /* FCOMI, FCOMIP, FUCOMI, FUCOMIP emulation */ + if (op1 == 0xDB) { + switch (op2 & 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 (op1 == 0xDF) { + switch (op2 & 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 ((op1 & 0xFE) == 0xDA && (op2 & 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, op1, op2); +#endif + return; + } + } +#endif /* CONFIG_CPU_EMU_FUCOMI */ + #ifdef BENCH_CPU_EXCEPTION_BUT_NOT_THE_CODE regs->ip += 3; return; @@ -692,11 +952,9 @@ static void handle_invalid_op(struct pt_ /* we'll first read all known opcode prefixes, and discard obviously invalid combinations.*/ while (1) { - u8 op1; get_user(op1, eip); /* prefix for CMOV, BSWAP, CMPXCHG, XADD */ if (op1 == 0x0F) { - u8 op2; eip++; get_user(op2, eip); #if defined(CONFIG_CPU_EMU686)