Commit b8e4a910 authored by Denys Vlasenko's avatar Denys Vlasenko Committed by Ingo Molnar

x86/fpu/math-emu: Add support for F[U]COMI[P] insns

Run-tested by booting with "no387 nofxsr" and running test
program:

  [RUN]   Testing f[u]comi[p] instructions

  [OK]    f[u]comi[p]
Signed-off-by: default avatarDenys Vlasenko <dvlasenk@redhat.com>
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Brian Gerst <brgerst@gmail.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Kees Cook <keescook@chromium.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: linux-kernel@vger.kernel.org
Link: http://lkml.kernel.org/r/1442588010-20055-2-git-send-email-dvlasenk@redhat.comSigned-off-by: default avatarIngo Molnar <mingo@kernel.org>
parent 4aef363e
...@@ -40,6 +40,8 @@ ...@@ -40,6 +40,8 @@
#define __BAD__ FPU_illegal /* Illegal on an 80486, causes SIGILL */ #define __BAD__ FPU_illegal /* Illegal on an 80486, causes SIGILL */
/* 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 /* WARNING: "u" entries are not documented by Intel in their 80486 manual
and may not work on FPU clones or later Intel FPUs. and may not work on FPU clones or later Intel FPUs.
Changes to support them provided by Linus Torvalds. */ Changes to support them provided by Linus Torvalds. */
...@@ -57,10 +59,10 @@ static FUNC const st_instr_table[64] = { ...@@ -57,10 +59,10 @@ static FUNC const st_instr_table[64] = {
/* d8..f */ fcompst,/*u*/ fstp_i, fcompp, fstp_i,/*u*/ /* d8..f */ fcompst,/*u*/ fstp_i, fcompp, fstp_i,/*u*/
/* e0..7 */ fsub__, FPU_etc, __BAD__, finit_, /* e0..7 */ fsub__, FPU_etc, __BAD__, finit_,
/* e0..7 */ fsubri, fucom_, fsubrp, fstsw_, /* e0..7 */ fsubri, fucom_, fsubrp, fstsw_,
/* e8..f */ fsubr_, fconst, fucompp, __BAD__, /* e8..f */ fsubr_, fconst, fucompp, fucomi_,
/* e8..f */ fsub_i, fucomp, fsubp_, __BAD__, /* e8..f */ fsub_i, fucomp, fsubp_, fucomip,
/* f0..7 */ fdiv__, FPU_triga, __BAD__, __BAD__, /* f0..7 */ fdiv__, FPU_triga, __BAD__, fcomi_,
/* f0..7 */ fdivri, __BAD__, fdivrp, __BAD__, /* f0..7 */ fdivri, __BAD__, fdivrp, fcomip,
/* f8..f */ fdivr_, FPU_trigb, __BAD__, __BAD__, /* f8..f */ fdivr_, FPU_trigb, __BAD__, __BAD__,
/* f8..f */ fdiv_i, __BAD__, fdivp_, __BAD__, /* f8..f */ fdiv_i, __BAD__, fdivp_, __BAD__,
}; };
...@@ -77,14 +79,15 @@ static FUNC const st_instr_table[64] = { ...@@ -77,14 +79,15 @@ static FUNC const st_instr_table[64] = {
#define _REGIn 0 /* Uses st(0) and st(rm), but handle checks later */ #define _REGIn 0 /* Uses st(0) and st(rm), but handle checks later */
static u_char const type_table[64] = { static u_char const type_table[64] = {
_REGI_, _NONE_, _null_, _null_, _REGIi, _REGi_, _REGIp, _REGi_, /* Opcode: d8 d9 da db dc dd de df */
_REGI_, _REGIn, _null_, _null_, _REGIi, _REGI_, _REGIp, _REGI_, /* c0..7 */ _REGI_, _NONE_, _null_, _null_, _REGIi, _REGi_, _REGIp, _REGi_,
_REGIc, _NONE_, _null_, _null_, _REGIc, _REG0_, _REGIc, _REG0_, /* c8..f */ _REGI_, _REGIn, _null_, _null_, _REGIi, _REGI_, _REGIp, _REGI_,
_REGIc, _REG0_, _null_, _null_, _REGIc, _REG0_, _REGIc, _REG0_, /* d0..7 */ _REGIc, _NONE_, _null_, _null_, _REGIc, _REG0_, _REGIc, _REG0_,
_REGI_, _NONE_, _null_, _NONE_, _REGIi, _REGIc, _REGIp, _NONE_, /* d8..f */ _REGIc, _REG0_, _null_, _null_, _REGIc, _REG0_, _REGIc, _REG0_,
_REGI_, _NONE_, _REGIc, _null_, _REGIi, _REGIc, _REGIp, _null_, /* e0..7 */ _REGI_, _NONE_, _null_, _NONE_, _REGIi, _REGIc, _REGIp, _NONE_,
_REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_, /* e8..f */ _REGI_, _NONE_, _REGIc, _REGIc, _REGIi, _REGIc, _REGIp, _REGIc,
_REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_ /* f0..7 */ _REGI_, _NONE_, _null_, _REGIc, _REGIi, _null_, _REGIp, _REGIc,
/* f8..f */ _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_,
}; };
#ifdef RE_ENTRANT_CHECKING #ifdef RE_ENTRANT_CHECKING
......
...@@ -108,6 +108,10 @@ extern void fcompp(void); ...@@ -108,6 +108,10 @@ extern void fcompp(void);
extern void fucom_(void); extern void fucom_(void);
extern void fucomp(void); extern void fucomp(void);
extern void fucompp(void); extern void fucompp(void);
extern void fcomi_(void);
extern void fcomip(void);
extern void fucomi_(void);
extern void fucomip(void);
/* reg_constant.c */ /* reg_constant.c */
extern void fconst(void); extern void fconst(void);
/* reg_ld_str.c */ /* reg_ld_str.c */
......
...@@ -249,6 +249,54 @@ static int compare_st_st(int nr) ...@@ -249,6 +249,54 @@ static int compare_st_st(int nr)
return 0; 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) static int compare_u_st_st(int nr)
{ {
int f = 0, c; int f = 0, c;
...@@ -299,6 +347,58 @@ static int compare_u_st_st(int nr) ...@@ -299,6 +347,58 @@ static int compare_u_st_st(int nr)
return 0; 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) void fcom_st(void)
...@@ -348,3 +448,31 @@ void fucompp(void) ...@@ -348,3 +448,31 @@ void fucompp(void)
} else } else
FPU_illegal(); 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();
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment