Skip to content

Commit b8e4a91

Browse files
dvlasenkIngo Molnar
authored andcommitted
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: Denys 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.com Signed-off-by: Ingo Molnar <mingo@kernel.org>
1 parent 4aef363 commit b8e4a91

File tree

3 files changed

+147
-12
lines changed

3 files changed

+147
-12
lines changed

arch/x86/math-emu/fpu_entry.c

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@
4040

4141
#define __BAD__ FPU_illegal /* Illegal on an 80486, causes SIGILL */
4242

43+
/* f(u)comi(p) are enabled if CPUID(1).EDX(15) "cmov" is set */
44+
4345
/* WARNING: "u" entries are not documented by Intel in their 80486 manual
4446
and may not work on FPU clones or later Intel FPUs.
4547
Changes to support them provided by Linus Torvalds. */
@@ -57,10 +59,10 @@ static FUNC const st_instr_table[64] = {
5759
/* d8..f */ fcompst,/*u*/ fstp_i, fcompp, fstp_i,/*u*/
5860
/* e0..7 */ fsub__, FPU_etc, __BAD__, finit_,
5961
/* e0..7 */ fsubri, fucom_, fsubrp, fstsw_,
60-
/* e8..f */ fsubr_, fconst, fucompp, __BAD__,
61-
/* e8..f */ fsub_i, fucomp, fsubp_, __BAD__,
62-
/* f0..7 */ fdiv__, FPU_triga, __BAD__, __BAD__,
63-
/* f0..7 */ fdivri, __BAD__, fdivrp, __BAD__,
62+
/* e8..f */ fsubr_, fconst, fucompp, fucomi_,
63+
/* e8..f */ fsub_i, fucomp, fsubp_, fucomip,
64+
/* f0..7 */ fdiv__, FPU_triga, __BAD__, fcomi_,
65+
/* f0..7 */ fdivri, __BAD__, fdivrp, fcomip,
6466
/* f8..f */ fdivr_, FPU_trigb, __BAD__, __BAD__,
6567
/* f8..f */ fdiv_i, __BAD__, fdivp_, __BAD__,
6668
};
@@ -77,14 +79,15 @@ static FUNC const st_instr_table[64] = {
7779
#define _REGIn 0 /* Uses st(0) and st(rm), but handle checks later */
7880

7981
static u_char const type_table[64] = {
80-
_REGI_, _NONE_, _null_, _null_, _REGIi, _REGi_, _REGIp, _REGi_,
81-
_REGI_, _REGIn, _null_, _null_, _REGIi, _REGI_, _REGIp, _REGI_,
82-
_REGIc, _NONE_, _null_, _null_, _REGIc, _REG0_, _REGIc, _REG0_,
83-
_REGIc, _REG0_, _null_, _null_, _REGIc, _REG0_, _REGIc, _REG0_,
84-
_REGI_, _NONE_, _null_, _NONE_, _REGIi, _REGIc, _REGIp, _NONE_,
85-
_REGI_, _NONE_, _REGIc, _null_, _REGIi, _REGIc, _REGIp, _null_,
86-
_REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_,
87-
_REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_
82+
/* Opcode: d8 d9 da db dc dd de df */
83+
/* c0..7 */ _REGI_, _NONE_, _null_, _null_, _REGIi, _REGi_, _REGIp, _REGi_,
84+
/* c8..f */ _REGI_, _REGIn, _null_, _null_, _REGIi, _REGI_, _REGIp, _REGI_,
85+
/* d0..7 */ _REGIc, _NONE_, _null_, _null_, _REGIc, _REG0_, _REGIc, _REG0_,
86+
/* d8..f */ _REGIc, _REG0_, _null_, _null_, _REGIc, _REG0_, _REGIc, _REG0_,
87+
/* e0..7 */ _REGI_, _NONE_, _null_, _NONE_, _REGIi, _REGIc, _REGIp, _NONE_,
88+
/* e8..f */ _REGI_, _NONE_, _REGIc, _REGIc, _REGIi, _REGIc, _REGIp, _REGIc,
89+
/* f0..7 */ _REGI_, _NONE_, _null_, _REGIc, _REGIi, _null_, _REGIp, _REGIc,
90+
/* f8..f */ _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_,
8891
};
8992

9093
#ifdef RE_ENTRANT_CHECKING

arch/x86/math-emu/fpu_proto.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,10 @@ extern void fcompp(void);
108108
extern void fucom_(void);
109109
extern void fucomp(void);
110110
extern void fucompp(void);
111+
extern void fcomi_(void);
112+
extern void fcomip(void);
113+
extern void fucomi_(void);
114+
extern void fucomip(void);
111115
/* reg_constant.c */
112116
extern void fconst(void);
113117
/* reg_ld_str.c */

arch/x86/math-emu/reg_compare.c

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,54 @@ static int compare_st_st(int nr)
249249
return 0;
250250
}
251251

252+
static int compare_i_st_st(int nr)
253+
{
254+
int f, c;
255+
FPU_REG *st_ptr;
256+
257+
if (!NOT_EMPTY(0) || !NOT_EMPTY(nr)) {
258+
FPU_EFLAGS |= (X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF);
259+
/* Stack fault */
260+
EXCEPTION(EX_StackUnder);
261+
return !(control_word & CW_Invalid);
262+
}
263+
264+
partial_status &= ~SW_C0;
265+
st_ptr = &st(nr);
266+
c = compare(st_ptr, FPU_gettagi(nr));
267+
if (c & COMP_NaN) {
268+
FPU_EFLAGS |= (X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF);
269+
EXCEPTION(EX_Invalid);
270+
return !(control_word & CW_Invalid);
271+
}
272+
273+
switch (c & 7) {
274+
case COMP_A_lt_B:
275+
f = X86_EFLAGS_CF;
276+
break;
277+
case COMP_A_eq_B:
278+
f = X86_EFLAGS_ZF;
279+
break;
280+
case COMP_A_gt_B:
281+
f = 0;
282+
break;
283+
case COMP_No_Comp:
284+
f = X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF;
285+
break;
286+
#ifdef PARANOID
287+
default:
288+
EXCEPTION(EX_INTERNAL | 0x122);
289+
f = 0;
290+
break;
291+
#endif /* PARANOID */
292+
}
293+
FPU_EFLAGS = (FPU_EFLAGS & ~(X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF)) | f;
294+
if (c & COMP_Denormal) {
295+
return denormal_operand() < 0;
296+
}
297+
return 0;
298+
}
299+
252300
static int compare_u_st_st(int nr)
253301
{
254302
int f = 0, c;
@@ -299,6 +347,58 @@ static int compare_u_st_st(int nr)
299347
return 0;
300348
}
301349

350+
static int compare_ui_st_st(int nr)
351+
{
352+
int f = 0, c;
353+
FPU_REG *st_ptr;
354+
355+
if (!NOT_EMPTY(0) || !NOT_EMPTY(nr)) {
356+
FPU_EFLAGS |= (X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF);
357+
/* Stack fault */
358+
EXCEPTION(EX_StackUnder);
359+
return !(control_word & CW_Invalid);
360+
}
361+
362+
partial_status &= ~SW_C0;
363+
st_ptr = &st(nr);
364+
c = compare(st_ptr, FPU_gettagi(nr));
365+
if (c & COMP_NaN) {
366+
FPU_EFLAGS |= (X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF);
367+
if (c & COMP_SNaN) { /* This is the only difference between
368+
un-ordered and ordinary comparisons */
369+
EXCEPTION(EX_Invalid);
370+
return !(control_word & CW_Invalid);
371+
}
372+
return 0;
373+
}
374+
375+
switch (c & 7) {
376+
case COMP_A_lt_B:
377+
f = X86_EFLAGS_CF;
378+
break;
379+
case COMP_A_eq_B:
380+
f = X86_EFLAGS_ZF;
381+
break;
382+
case COMP_A_gt_B:
383+
f = 0;
384+
break;
385+
case COMP_No_Comp:
386+
f = X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF;
387+
break;
388+
#ifdef PARANOID
389+
default:
390+
EXCEPTION(EX_INTERNAL | 0x123);
391+
f = 0;
392+
break;
393+
#endif /* PARANOID */
394+
}
395+
FPU_EFLAGS = (FPU_EFLAGS & ~(X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF)) | f;
396+
if (c & COMP_Denormal) {
397+
return denormal_operand() < 0;
398+
}
399+
return 0;
400+
}
401+
302402
/*---------------------------------------------------------------------------*/
303403

304404
void fcom_st(void)
@@ -348,3 +448,31 @@ void fucompp(void)
348448
} else
349449
FPU_illegal();
350450
}
451+
452+
/* P6+ compare-to-EFLAGS ops */
453+
454+
void fcomi_(void)
455+
{
456+
/* fcomi st(i) */
457+
compare_i_st_st(FPU_rm);
458+
}
459+
460+
void fcomip(void)
461+
{
462+
/* fcomip st(i) */
463+
if (!compare_i_st_st(FPU_rm))
464+
FPU_pop();
465+
}
466+
467+
void fucomi_(void)
468+
{
469+
/* fucomi st(i) */
470+
compare_ui_st_st(FPU_rm);
471+
}
472+
473+
void fucomip(void)
474+
{
475+
/* fucomip st(i) */
476+
if (!compare_ui_st_st(FPU_rm))
477+
FPU_pop();
478+
}

0 commit comments

Comments
 (0)