Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Denys Vlasenko and committed by
Ingo Molnar
b8e4a910 4aef363e

+147 -12
+15 -12
arch/x86/math-emu/fpu_entry.c
··· 40 40 41 41 #define __BAD__ FPU_illegal /* Illegal on an 80486, causes SIGILL */ 42 42 43 + /* f(u)comi(p) are enabled if CPUID(1).EDX(15) "cmov" is set */ 44 + 43 45 /* WARNING: "u" entries are not documented by Intel in their 80486 manual 44 46 and may not work on FPU clones or later Intel FPUs. 45 47 Changes to support them provided by Linus Torvalds. */ ··· 59 57 /* d8..f */ fcompst,/*u*/ fstp_i, fcompp, fstp_i,/*u*/ 60 58 /* e0..7 */ fsub__, FPU_etc, __BAD__, finit_, 61 59 /* e0..7 */ fsubri, fucom_, fsubrp, fstsw_, 62 - /* e8..f */ fsubr_, fconst, fucompp, __BAD__, 63 - /* e8..f */ fsub_i, fucomp, fsubp_, __BAD__, 64 - /* f0..7 */ fdiv__, FPU_triga, __BAD__, __BAD__, 65 - /* f0..7 */ fdivri, __BAD__, fdivrp, __BAD__, 60 + /* e8..f */ fsubr_, fconst, fucompp, fucomi_, 61 + /* e8..f */ fsub_i, fucomp, fsubp_, fucomip, 62 + /* f0..7 */ fdiv__, FPU_triga, __BAD__, fcomi_, 63 + /* f0..7 */ fdivri, __BAD__, fdivrp, fcomip, 66 64 /* f8..f */ fdivr_, FPU_trigb, __BAD__, __BAD__, 67 65 /* f8..f */ fdiv_i, __BAD__, fdivp_, __BAD__, 68 66 }; ··· 79 77 #define _REGIn 0 /* Uses st(0) and st(rm), but handle checks later */ 80 78 81 79 static u_char const type_table[64] = { 82 - _REGI_, _NONE_, _null_, _null_, _REGIi, _REGi_, _REGIp, _REGi_, 83 - _REGI_, _REGIn, _null_, _null_, _REGIi, _REGI_, _REGIp, _REGI_, 84 - _REGIc, _NONE_, _null_, _null_, _REGIc, _REG0_, _REGIc, _REG0_, 85 - _REGIc, _REG0_, _null_, _null_, _REGIc, _REG0_, _REGIc, _REG0_, 86 - _REGI_, _NONE_, _null_, _NONE_, _REGIi, _REGIc, _REGIp, _NONE_, 87 - _REGI_, _NONE_, _REGIc, _null_, _REGIi, _REGIc, _REGIp, _null_, 88 - _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_, 89 - _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_ 80 + /* Opcode: d8 d9 da db dc dd de df */ 81 + /* c0..7 */ _REGI_, _NONE_, _null_, _null_, _REGIi, _REGi_, _REGIp, _REGi_, 82 + /* c8..f */ _REGI_, _REGIn, _null_, _null_, _REGIi, _REGI_, _REGIp, _REGI_, 83 + /* d0..7 */ _REGIc, _NONE_, _null_, _null_, _REGIc, _REG0_, _REGIc, _REG0_, 84 + /* d8..f */ _REGIc, _REG0_, _null_, _null_, _REGIc, _REG0_, _REGIc, _REG0_, 85 + /* e0..7 */ _REGI_, _NONE_, _null_, _NONE_, _REGIi, _REGIc, _REGIp, _NONE_, 86 + /* e8..f */ _REGI_, _NONE_, _REGIc, _REGIc, _REGIi, _REGIc, _REGIp, _REGIc, 87 + /* f0..7 */ _REGI_, _NONE_, _null_, _REGIc, _REGIi, _null_, _REGIp, _REGIc, 88 + /* f8..f */ _REGI_, _NONE_, _null_, _null_, _REGIi, _null_, _REGIp, _null_, 90 89 }; 91 90 92 91 #ifdef RE_ENTRANT_CHECKING
+4
arch/x86/math-emu/fpu_proto.h
··· 108 108 extern void fucom_(void); 109 109 extern void fucomp(void); 110 110 extern void fucompp(void); 111 + extern void fcomi_(void); 112 + extern void fcomip(void); 113 + extern void fucomi_(void); 114 + extern void fucomip(void); 111 115 /* reg_constant.c */ 112 116 extern void fconst(void); 113 117 /* reg_ld_str.c */
+128
arch/x86/math-emu/reg_compare.c
··· 249 249 return 0; 250 250 } 251 251 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 + 252 300 static int compare_u_st_st(int nr) 253 301 { 254 302 int f = 0, c; ··· 341 293 #endif /* PARANOID */ 342 294 } 343 295 setcc(f); 296 + if (c & COMP_Denormal) { 297 + return denormal_operand() < 0; 298 + } 299 + return 0; 300 + } 301 + 302 + static int compare_ui_st_st(int nr) 303 + { 304 + int f = 0, c; 305 + FPU_REG *st_ptr; 306 + 307 + if (!NOT_EMPTY(0) || !NOT_EMPTY(nr)) { 308 + FPU_EFLAGS |= (X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF); 309 + /* Stack fault */ 310 + EXCEPTION(EX_StackUnder); 311 + return !(control_word & CW_Invalid); 312 + } 313 + 314 + partial_status &= ~SW_C0; 315 + st_ptr = &st(nr); 316 + c = compare(st_ptr, FPU_gettagi(nr)); 317 + if (c & COMP_NaN) { 318 + FPU_EFLAGS |= (X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF); 319 + if (c & COMP_SNaN) { /* This is the only difference between 320 + un-ordered and ordinary comparisons */ 321 + EXCEPTION(EX_Invalid); 322 + return !(control_word & CW_Invalid); 323 + } 324 + return 0; 325 + } 326 + 327 + switch (c & 7) { 328 + case COMP_A_lt_B: 329 + f = X86_EFLAGS_CF; 330 + break; 331 + case COMP_A_eq_B: 332 + f = X86_EFLAGS_ZF; 333 + break; 334 + case COMP_A_gt_B: 335 + f = 0; 336 + break; 337 + case COMP_No_Comp: 338 + f = X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF; 339 + break; 340 + #ifdef PARANOID 341 + default: 342 + EXCEPTION(EX_INTERNAL | 0x123); 343 + f = 0; 344 + break; 345 + #endif /* PARANOID */ 346 + } 347 + FPU_EFLAGS = (FPU_EFLAGS & ~(X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF)) | f; 344 348 if (c & COMP_Denormal) { 345 349 return denormal_operand() < 0; 346 350 } ··· 447 347 poppop(); 448 348 } else 449 349 FPU_illegal(); 350 + } 351 + 352 + /* P6+ compare-to-EFLAGS ops */ 353 + 354 + void fcomi_(void) 355 + { 356 + /* fcomi st(i) */ 357 + compare_i_st_st(FPU_rm); 358 + } 359 + 360 + void fcomip(void) 361 + { 362 + /* fcomip st(i) */ 363 + if (!compare_i_st_st(FPU_rm)) 364 + FPU_pop(); 365 + } 366 + 367 + void fucomi_(void) 368 + { 369 + /* fucomi st(i) */ 370 + compare_ui_st_st(FPU_rm); 371 + } 372 + 373 + void fucomip(void) 374 + { 375 + /* fucomip st(i) */ 376 + if (!compare_ui_st_st(FPU_rm)) 377 + FPU_pop(); 450 378 }