From 9a9d8642d03a7512f78cbe7ed6a2011fad3cbca3 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Fri, 18 Sep 2015 16:53:30 +0200 Subject: x86/fpu/math-emu: Add support for FCMOVcc insns Run-tested by booting with "no387 nofxsr" and running test program: [RUN] Testing fcmovCC instructions [OK] fcmovCC Signed-off-by: Denys Vlasenko Cc: Andy Lutomirski Cc: Borislav Petkov Cc: Brian Gerst Cc: H. Peter Anvin Cc: Kees Cook Cc: Linus Torvalds Cc: Peter Zijlstra Cc: Thomas Gleixner Cc: linux-kernel@vger.kernel.org Link: http://lkml.kernel.org/r/1442588010-20055-3-git-send-email-dvlasenk@redhat.com Signed-off-by: Ingo Molnar --- arch/x86/math-emu/fpu_aux.c | 70 +++++++++++++++++++++++++++++++++++++++++++ arch/x86/math-emu/fpu_entry.c | 18 +++++------ arch/x86/math-emu/fpu_proto.h | 8 +++++ 3 files changed, 87 insertions(+), 9 deletions(-) (limited to 'arch/x86/math-emu') diff --git a/arch/x86/math-emu/fpu_aux.c b/arch/x86/math-emu/fpu_aux.c index dd76a05729b0..024f6e971174 100644 --- a/arch/x86/math-emu/fpu_aux.c +++ b/arch/x86/math-emu/fpu_aux.c @@ -169,6 +169,76 @@ void fxch_i(void) fpu_tag_word = tag_word; } +static void fcmovCC(void) +{ + /* fcmovCC st(i) */ + int i = FPU_rm; + FPU_REG *st0_ptr = &st(0); + FPU_REG *sti_ptr = &st(i); + long tag_word = fpu_tag_word; + int regnr = top & 7; + int regnri = (top + i) & 7; + u_char sti_tag = (tag_word >> (regnri * 2)) & 3; + + if (sti_tag == TAG_Empty) { + FPU_stack_underflow(); + clear_C1(); + return; + } + reg_copy(sti_ptr, st0_ptr); + tag_word &= ~(3 << (regnr * 2)); + tag_word |= (sti_tag << (regnr * 2)); + fpu_tag_word = tag_word; +} + +void fcmovb(void) +{ + if (FPU_EFLAGS & X86_EFLAGS_CF) + fcmovCC(); +} + +void fcmove(void) +{ + if (FPU_EFLAGS & X86_EFLAGS_ZF) + fcmovCC(); +} + +void fcmovbe(void) +{ + if (FPU_EFLAGS & (X86_EFLAGS_CF|X86_EFLAGS_ZF)) + fcmovCC(); +} + +void fcmovu(void) +{ + if (FPU_EFLAGS & X86_EFLAGS_PF) + fcmovCC(); +} + +void fcmovnb(void) +{ + if (!(FPU_EFLAGS & X86_EFLAGS_CF)) + fcmovCC(); +} + +void fcmovne(void) +{ + if (!(FPU_EFLAGS & X86_EFLAGS_ZF)) + fcmovCC(); +} + +void fcmovnbe(void) +{ + if (!(FPU_EFLAGS & (X86_EFLAGS_CF|X86_EFLAGS_ZF))) + fcmovCC(); +} + +void fcmovnu(void) +{ + if (!(FPU_EFLAGS & X86_EFLAGS_PF)) + fcmovCC(); +} + void ffree_(void) { /* ffree st(i) */ diff --git a/arch/x86/math-emu/fpu_entry.c b/arch/x86/math-emu/fpu_entry.c index 4ecf68342d7b..e945fedf1de2 100644 --- a/arch/x86/math-emu/fpu_entry.c +++ b/arch/x86/math-emu/fpu_entry.c @@ -40,7 +40,7 @@ #define __BAD__ FPU_illegal /* Illegal on an 80486, causes SIGILL */ -/* f(u)comi(p) are enabled if CPUID(1).EDX(15) "cmov" is set */ +/* fcmovCC and f(u)comi(p) are enabled if CPUID(1).EDX(15) "cmov" is set */ /* WARNING: "u" entries are not documented by Intel in their 80486 manual and may not work on FPU clones or later Intel FPUs. @@ -49,13 +49,13 @@ static FUNC const st_instr_table[64] = { /* Opcode: d8 d9 da db */ /* dc dd de df */ -/* c0..7 */ fadd__, fld_i_, __BAD__, __BAD__, +/* c0..7 */ fadd__, fld_i_, fcmovb, fcmovnb, /* c0..7 */ fadd_i, ffree_, faddp_, ffreep,/*u*/ -/* c8..f */ fmul__, fxch_i, __BAD__, __BAD__, +/* c8..f */ fmul__, fxch_i, fcmove, fcmovne, /* c8..f */ fmul_i, fxch_i,/*u*/ fmulp_, fxch_i,/*u*/ -/* d0..7 */ fcom_st, fp_nop, __BAD__, __BAD__, +/* d0..7 */ fcom_st, fp_nop, fcmovbe, fcmovnbe, /* d0..7 */ fcom_st,/*u*/ fst_i_, fcompst,/*u*/ fstp_i,/*u*/ -/* d8..f */ fcompst, fstp_i,/*u*/ __BAD__, __BAD__, +/* d8..f */ fcompst, fstp_i,/*u*/ fcmovu, fcmovnu, /* d8..f */ fcompst,/*u*/ fstp_i, fcompp, fstp_i,/*u*/ /* e0..7 */ fsub__, FPU_etc, __BAD__, finit_, /* e0..7 */ fsubri, fucom_, fsubrp, fstsw_, @@ -80,10 +80,10 @@ static FUNC const st_instr_table[64] = { static u_char const type_table[64] = { /* Opcode: d8 d9 da db dc dd de df */ -/* c0..7 */ _REGI_, _NONE_, _null_, _null_, _REGIi, _REGi_, _REGIp, _REGi_, -/* c8..f */ _REGI_, _REGIn, _null_, _null_, _REGIi, _REGI_, _REGIp, _REGI_, -/* d0..7 */ _REGIc, _NONE_, _null_, _null_, _REGIc, _REG0_, _REGIc, _REG0_, -/* d8..f */ _REGIc, _REG0_, _null_, _null_, _REGIc, _REG0_, _REGIc, _REG0_, +/* c0..7 */ _REGI_, _NONE_, _REGIn, _REGIn, _REGIi, _REGi_, _REGIp, _REGi_, +/* c8..f */ _REGI_, _REGIn, _REGIn, _REGIn, _REGIi, _REGI_, _REGIp, _REGI_, +/* d0..7 */ _REGIc, _NONE_, _REGIn, _REGIn, _REGIc, _REG0_, _REGIc, _REG0_, +/* d8..f */ _REGIc, _REG0_, _REGIn, _REGIn, _REGIc, _REG0_, _REGIc, _REG0_, /* e0..7 */ _REGI_, _NONE_, _null_, _NONE_, _REGIi, _REGIc, _REGIp, _NONE_, /* e8..f */ _REGI_, _NONE_, _REGIc, _REGIc, _REGIi, _REGIc, _REGIp, _REGIc, /* f0..7 */ _REGI_, _NONE_, _null_, _REGIc, _REGIi, _null_, _REGIp, _REGIc, diff --git a/arch/x86/math-emu/fpu_proto.h b/arch/x86/math-emu/fpu_proto.h index 1f8d130663ad..caff438b9c1d 100644 --- a/arch/x86/math-emu/fpu_proto.h +++ b/arch/x86/math-emu/fpu_proto.h @@ -46,6 +46,14 @@ extern void fstsw_(void); extern void fp_nop(void); extern void fld_i_(void); extern void fxch_i(void); +extern void fcmovb(void); +extern void fcmove(void); +extern void fcmovbe(void); +extern void fcmovu(void); +extern void fcmovnb(void); +extern void fcmovne(void); +extern void fcmovnbe(void); +extern void fcmovnu(void); extern void ffree_(void); extern void ffreep(void); extern void fst_i_(void); -- cgit v1.2.3