Browse Source

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 <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-3-git-send-email-dvlasenk@redhat.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Denys Vlasenko 10 years ago
parent
commit
9a9d8642d0
3 changed files with 87 additions and 9 deletions
  1. 70 0
      arch/x86/math-emu/fpu_aux.c
  2. 9 9
      arch/x86/math-emu/fpu_entry.c
  3. 8 0
      arch/x86/math-emu/fpu_proto.h

+ 70 - 0
arch/x86/math-emu/fpu_aux.c

@@ -169,6 +169,76 @@ void fxch_i(void)
 	fpu_tag_word = tag_word;
 	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)
 void ffree_(void)
 {
 {
 	/* ffree st(i) */
 	/* ffree st(i) */

+ 9 - 9
arch/x86/math-emu/fpu_entry.c

@@ -40,7 +40,7 @@
 
 
 #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 */
+/* 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
 /* 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.
@@ -49,13 +49,13 @@
 static FUNC const st_instr_table[64] = {
 static FUNC const st_instr_table[64] = {
 /* Opcode:	d8		d9		da		db */
 /* Opcode:	d8		d9		da		db */
 /*		dc		dd		de		df */
 /*		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*/
 /* 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*/
 /* 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*/
 /* 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*/
 /* 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_,
@@ -80,10 +80,10 @@ static FUNC const st_instr_table[64] = {
 
 
 static u_char const type_table[64] = {
 static u_char const type_table[64] = {
 /* Opcode:	d8	d9	da	db	dc	dd	de	df */
 /* 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_,
 /* e0..7 */	_REGI_, _NONE_, _null_, _NONE_, _REGIi, _REGIc, _REGIp, _NONE_,
 /* e8..f */	_REGI_, _NONE_, _REGIc, _REGIc, _REGIi, _REGIc, _REGIp, _REGIc,
 /* e8..f */	_REGI_, _NONE_, _REGIc, _REGIc, _REGIi, _REGIc, _REGIp, _REGIc,
 /* f0..7 */	_REGI_, _NONE_, _null_, _REGIc, _REGIi, _null_, _REGIp, _REGIc,
 /* f0..7 */	_REGI_, _NONE_, _null_, _REGIc, _REGIi, _null_, _REGIp, _REGIc,

+ 8 - 0
arch/x86/math-emu/fpu_proto.h

@@ -46,6 +46,14 @@ extern void fstsw_(void);
 extern void fp_nop(void);
 extern void fp_nop(void);
 extern void fld_i_(void);
 extern void fld_i_(void);
 extern void fxch_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 ffree_(void);
 extern void ffreep(void);
 extern void ffreep(void);
 extern void fst_i_(void);
 extern void fst_i_(void);