Only compile-tested.

Signed-off-by: Denys Vlasenko <dvlas...@redhat.com>
CC: Ingo Molnar <mi...@kernel.org>
CC: Borislav Petkov <b...@alien8.de>
CC: "H. Peter Anvin" <h...@zytor.com>
CC: Andy Lutomirski <l...@amacapital.net>
CC: Kees Cook <keesc...@chromium.org>
CC: x...@kernel.org
CC: linux-kernel@vger.kernel.org
---
 arch/x86/math-emu/fpu_aux.c     |  70 ++++++++++++++++++++++
 arch/x86/math-emu/fpu_entry.c   |  14 +++--
 arch/x86/math-emu/fpu_proto.h   |  12 ++++
 arch/x86/math-emu/reg_compare.c | 128 ++++++++++++++++++++++++++++++++++++++++
 4 files changed, 218 insertions(+), 6 deletions(-)

diff --git a/arch/x86/math-emu/fpu_aux.c b/arch/x86/math-emu/fpu_aux.c
index dd76a05..6539cb2 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 f37e84a..c5dfd59 100644
--- a/arch/x86/math-emu/fpu_entry.c
+++ b/arch/x86/math-emu/fpu_entry.c
@@ -58,14 +58,16 @@
 #define _df_d0_ fstp_i         /* unofficial code (17) */
 #define _df_d8_ fstp_i         /* unofficial code (1f) */
 
+/* fcmovCC and f(u)comi(p) are enabled if CPUID(1).EDX(15) "cmov" is set */
+
 static FUNC const st_instr_table[64] = {
-       fadd__, fld_i_, __BAD__, __BAD__, fadd_i, ffree_, faddp_, _df_c0_,
-       fmul__, fxch_i, __BAD__, __BAD__, fmul_i, _dd_c8_, fmulp_, _df_c8_,
-       fcom_st, fp_nop, __BAD__, __BAD__, _dc_d0_, fst_i_, _de_d0_, _df_d0_,
-       fcompst, _d9_d8_, __BAD__, __BAD__, _dc_d8_, fstp_i, fcompp, _df_d8_,
+       fadd__,  fld_i_,  fcmovb,   fcmovnb,  fadd_i,  ffree_,  faddp_,  
_df_c0_,
+       fmul__,  fxch_i,  fcmove,   fcmovne,  fmul_i,  _dd_c8_, fmulp_,  
_df_c8_,
+       fcom_st, fp_nop,  fcmovbe,  fcmovnbe, _dc_d0_, fst_i_,  _de_d0_, 
_df_d0_,
+       fcompst, _d9_d8_, fcmovu,   fcmovnu,  _dc_d8_, fstp_i,  fcompp,  
_df_d8_,
        fsub__, FPU_etc, __BAD__, finit_, fsubri, fucom_, fsubrp, fstsw_,
-       fsubr_, fconst, fucompp, __BAD__, fsub_i, fucomp, fsubp_, __BAD__,
-       fdiv__, FPU_triga, __BAD__, __BAD__, fdivri, __BAD__, fdivrp, __BAD__,
+       fsubr_, fconst,   fucompp,  fucomi_,  fsub_i,  fucomp,  fsubp_,  
fucomip,
+       fdiv__, FPU_triga, __BAD__, fcomi_,   fdivri,  __BAD__, fdivrp,  fcomip,
        fdivr_, FPU_trigb, __BAD__, __BAD__, fdiv_i, __BAD__, fdivp_, __BAD__,
 };
 
diff --git a/arch/x86/math-emu/fpu_proto.h b/arch/x86/math-emu/fpu_proto.h
index 9779df4..caff438 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);
@@ -108,6 +116,10 @@ extern void fcompp(void);
 extern void fucom_(void);
 extern void fucomp(void);
 extern void fucompp(void);
+extern void fcomi_(void);
+extern void fcomip(void);
+extern void fucomi_(void);
+extern void fucomip(void);
 /* reg_constant.c */
 extern void fconst(void);
 /* reg_ld_str.c */
diff --git a/arch/x86/math-emu/reg_compare.c b/arch/x86/math-emu/reg_compare.c
index ecce55f..b77360f 100644
--- a/arch/x86/math-emu/reg_compare.c
+++ b/arch/x86/math-emu/reg_compare.c
@@ -249,6 +249,54 @@ static int compare_st_st(int nr)
        return 0;
 }
 
+static int compare_i_st_st(int nr)
+{
+       int f, c;
+       FPU_REG *st_ptr;
+
+       if (!NOT_EMPTY(0) || !NOT_EMPTY(nr)) {
+               FPU_EFLAGS |= (X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF);
+               /* Stack fault */
+               EXCEPTION(EX_StackUnder);
+               return !(control_word & CW_Invalid);
+       }
+
+       partial_status &= ~SW_C0;
+       st_ptr = &st(nr);
+       c = compare(st_ptr, FPU_gettagi(nr));
+       if (c & COMP_NaN) {
+               FPU_EFLAGS |= (X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF);
+               EXCEPTION(EX_Invalid);
+               return !(control_word & CW_Invalid);
+       }
+
+       switch (c & 7) {
+       case COMP_A_lt_B:
+               f = X86_EFLAGS_CF;
+               break;
+       case COMP_A_eq_B:
+               f = X86_EFLAGS_ZF;
+               break;
+       case COMP_A_gt_B:
+               f = 0;
+               break;
+       case COMP_No_Comp:
+               f = X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF;
+               break;
+#ifdef PARANOID
+       default:
+               EXCEPTION(EX_INTERNAL | 0x122);
+               f = 0;
+               break;
+#endif /* PARANOID */
+       }
+       FPU_EFLAGS = (FPU_EFLAGS & ~(X86_EFLAGS_ZF | X86_EFLAGS_PF | 
X86_EFLAGS_CF)) | f;
+       if (c & COMP_Denormal) {
+               return denormal_operand() < 0;
+       }
+       return 0;
+}
+
 static int compare_u_st_st(int nr)
 {
        int f = 0, c;
@@ -299,6 +347,58 @@ static int compare_u_st_st(int nr)
        return 0;
 }
 
+static int compare_ui_st_st(int nr)
+{
+       int f = 0, c;
+       FPU_REG *st_ptr;
+
+       if (!NOT_EMPTY(0) || !NOT_EMPTY(nr)) {
+               FPU_EFLAGS |= (X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF);
+               /* Stack fault */
+               EXCEPTION(EX_StackUnder);
+               return !(control_word & CW_Invalid);
+       }
+
+       partial_status &= ~SW_C0;
+       st_ptr = &st(nr);
+       c = compare(st_ptr, FPU_gettagi(nr));
+       if (c & COMP_NaN) {
+               FPU_EFLAGS |= (X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF);
+               if (c & COMP_SNaN) {    /* This is the only difference between
+                                          un-ordered and ordinary comparisons 
*/
+                       EXCEPTION(EX_Invalid);
+                       return !(control_word & CW_Invalid);
+               }
+               return 0;
+       }
+
+       switch (c & 7) {
+       case COMP_A_lt_B:
+               f = X86_EFLAGS_CF;
+               break;
+       case COMP_A_eq_B:
+               f = X86_EFLAGS_ZF;
+               break;
+       case COMP_A_gt_B:
+               f = 0;
+               break;
+       case COMP_No_Comp:
+               f = X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF;
+               break;
+#ifdef PARANOID
+       default:
+               EXCEPTION(EX_INTERNAL | 0x123);
+               f = 0;
+               break;
+#endif /* PARANOID */
+       }
+       FPU_EFLAGS = (FPU_EFLAGS & ~(X86_EFLAGS_ZF | X86_EFLAGS_PF | 
X86_EFLAGS_CF)) | f;
+       if (c & COMP_Denormal) {
+               return denormal_operand() < 0;
+       }
+       return 0;
+}
+
 /*---------------------------------------------------------------------------*/
 
 void fcom_st(void)
@@ -348,3 +448,31 @@ void fucompp(void)
        } else
                FPU_illegal();
 }
+
+/* P6+ compare-to-EFLAGS ops */
+
+void fcomi_(void)
+{
+       /* fcomi st(i) */
+       compare_i_st_st(FPU_rm);
+}
+
+void fcomip(void)
+{
+       /* fcomip st(i) */
+       if (!compare_i_st_st(FPU_rm))
+               FPU_pop();
+}
+
+void fucomi_(void)
+{
+       /* fucomi st(i) */
+       compare_ui_st_st(FPU_rm);
+}
+
+void fucomip(void)
+{
+       /* fucomip st(i) */
+       if (!compare_ui_st_st(FPU_rm))
+               FPU_pop();
+}
-- 
1.8.1.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to