cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

reg_compare.c (10796B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*---------------------------------------------------------------------------+
      3 |  reg_compare.c                                                            |
      4 |                                                                           |
      5 | Compare two floating point registers                                      |
      6 |                                                                           |
      7 | Copyright (C) 1992,1993,1994,1997                                         |
      8 |                  W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
      9 |                  E-mail   billm@suburbia.net                              |
     10 |                                                                           |
     11 |                                                                           |
     12 +---------------------------------------------------------------------------*/
     13
     14/*---------------------------------------------------------------------------+
     15 | compare() is the core FPU_REG comparison function                         |
     16 +---------------------------------------------------------------------------*/
     17
     18#include "fpu_system.h"
     19#include "exception.h"
     20#include "fpu_emu.h"
     21#include "control_w.h"
     22#include "status_w.h"
     23
     24static int compare(FPU_REG const *b, int tagb)
     25{
     26	int diff, exp0, expb;
     27	u_char st0_tag;
     28	FPU_REG *st0_ptr;
     29	FPU_REG x, y;
     30	u_char st0_sign, signb = getsign(b);
     31
     32	st0_ptr = &st(0);
     33	st0_tag = FPU_gettag0();
     34	st0_sign = getsign(st0_ptr);
     35
     36	if (tagb == TAG_Special)
     37		tagb = FPU_Special(b);
     38	if (st0_tag == TAG_Special)
     39		st0_tag = FPU_Special(st0_ptr);
     40
     41	if (((st0_tag != TAG_Valid) && (st0_tag != TW_Denormal))
     42	    || ((tagb != TAG_Valid) && (tagb != TW_Denormal))) {
     43		if (st0_tag == TAG_Zero) {
     44			if (tagb == TAG_Zero)
     45				return COMP_A_eq_B;
     46			if (tagb == TAG_Valid)
     47				return ((signb ==
     48					 SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B);
     49			if (tagb == TW_Denormal)
     50				return ((signb ==
     51					 SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B)
     52				    | COMP_Denormal;
     53		} else if (tagb == TAG_Zero) {
     54			if (st0_tag == TAG_Valid)
     55				return ((st0_sign ==
     56					 SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B);
     57			if (st0_tag == TW_Denormal)
     58				return ((st0_sign ==
     59					 SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
     60				    | COMP_Denormal;
     61		}
     62
     63		if (st0_tag == TW_Infinity) {
     64			if ((tagb == TAG_Valid) || (tagb == TAG_Zero))
     65				return ((st0_sign ==
     66					 SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B);
     67			else if (tagb == TW_Denormal)
     68				return ((st0_sign ==
     69					 SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
     70				    | COMP_Denormal;
     71			else if (tagb == TW_Infinity) {
     72				/* The 80486 book says that infinities can be equal! */
     73				return (st0_sign == signb) ? COMP_A_eq_B :
     74				    ((st0_sign ==
     75				      SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B);
     76			}
     77			/* Fall through to the NaN code */
     78		} else if (tagb == TW_Infinity) {
     79			if ((st0_tag == TAG_Valid) || (st0_tag == TAG_Zero))
     80				return ((signb ==
     81					 SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B);
     82			if (st0_tag == TW_Denormal)
     83				return ((signb ==
     84					 SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B)
     85				    | COMP_Denormal;
     86			/* Fall through to the NaN code */
     87		}
     88
     89		/* The only possibility now should be that one of the arguments
     90		   is a NaN */
     91		if ((st0_tag == TW_NaN) || (tagb == TW_NaN)) {
     92			int signalling = 0, unsupported = 0;
     93			if (st0_tag == TW_NaN) {
     94				signalling =
     95				    (st0_ptr->sigh & 0xc0000000) == 0x80000000;
     96				unsupported = !((exponent(st0_ptr) == EXP_OVER)
     97						&& (st0_ptr->
     98						    sigh & 0x80000000));
     99			}
    100			if (tagb == TW_NaN) {
    101				signalling |=
    102				    (b->sigh & 0xc0000000) == 0x80000000;
    103				unsupported |= !((exponent(b) == EXP_OVER)
    104						 && (b->sigh & 0x80000000));
    105			}
    106			if (signalling || unsupported)
    107				return COMP_No_Comp | COMP_SNaN | COMP_NaN;
    108			else
    109				/* Neither is a signaling NaN */
    110				return COMP_No_Comp | COMP_NaN;
    111		}
    112
    113		EXCEPTION(EX_Invalid);
    114	}
    115
    116	if (st0_sign != signb) {
    117		return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
    118		    | (((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ?
    119		       COMP_Denormal : 0);
    120	}
    121
    122	if ((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) {
    123		FPU_to_exp16(st0_ptr, &x);
    124		FPU_to_exp16(b, &y);
    125		st0_ptr = &x;
    126		b = &y;
    127		exp0 = exponent16(st0_ptr);
    128		expb = exponent16(b);
    129	} else {
    130		exp0 = exponent(st0_ptr);
    131		expb = exponent(b);
    132	}
    133
    134#ifdef PARANOID
    135	if (!(st0_ptr->sigh & 0x80000000))
    136		EXCEPTION(EX_Invalid);
    137	if (!(b->sigh & 0x80000000))
    138		EXCEPTION(EX_Invalid);
    139#endif /* PARANOID */
    140
    141	diff = exp0 - expb;
    142	if (diff == 0) {
    143		diff = st0_ptr->sigh - b->sigh;	/* Works only if ms bits are
    144						   identical */
    145		if (diff == 0) {
    146			diff = st0_ptr->sigl > b->sigl;
    147			if (diff == 0)
    148				diff = -(st0_ptr->sigl < b->sigl);
    149		}
    150	}
    151
    152	if (diff > 0) {
    153		return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
    154		    | (((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ?
    155		       COMP_Denormal : 0);
    156	}
    157	if (diff < 0) {
    158		return ((st0_sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B)
    159		    | (((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ?
    160		       COMP_Denormal : 0);
    161	}
    162
    163	return COMP_A_eq_B
    164	    | (((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ?
    165	       COMP_Denormal : 0);
    166
    167}
    168
    169/* This function requires that st(0) is not empty */
    170int FPU_compare_st_data(FPU_REG const *loaded_data, u_char loaded_tag)
    171{
    172	int f, c;
    173
    174	c = compare(loaded_data, loaded_tag);
    175
    176	if (c & COMP_NaN) {
    177		EXCEPTION(EX_Invalid);
    178		f = SW_C3 | SW_C2 | SW_C0;
    179	} else
    180		switch (c & 7) {
    181		case COMP_A_lt_B:
    182			f = SW_C0;
    183			break;
    184		case COMP_A_eq_B:
    185			f = SW_C3;
    186			break;
    187		case COMP_A_gt_B:
    188			f = 0;
    189			break;
    190		case COMP_No_Comp:
    191			f = SW_C3 | SW_C2 | SW_C0;
    192			break;
    193		default:
    194#ifdef PARANOID
    195			EXCEPTION(EX_INTERNAL | 0x121);
    196#endif /* PARANOID */
    197			f = SW_C3 | SW_C2 | SW_C0;
    198			break;
    199		}
    200	setcc(f);
    201	if (c & COMP_Denormal) {
    202		return denormal_operand() < 0;
    203	}
    204	return 0;
    205}
    206
    207static int compare_st_st(int nr)
    208{
    209	int f, c;
    210	FPU_REG *st_ptr;
    211
    212	if (!NOT_EMPTY(0) || !NOT_EMPTY(nr)) {
    213		setcc(SW_C3 | SW_C2 | SW_C0);
    214		/* Stack fault */
    215		EXCEPTION(EX_StackUnder);
    216		return !(control_word & CW_Invalid);
    217	}
    218
    219	st_ptr = &st(nr);
    220	c = compare(st_ptr, FPU_gettagi(nr));
    221	if (c & COMP_NaN) {
    222		setcc(SW_C3 | SW_C2 | SW_C0);
    223		EXCEPTION(EX_Invalid);
    224		return !(control_word & CW_Invalid);
    225	} else
    226		switch (c & 7) {
    227		case COMP_A_lt_B:
    228			f = SW_C0;
    229			break;
    230		case COMP_A_eq_B:
    231			f = SW_C3;
    232			break;
    233		case COMP_A_gt_B:
    234			f = 0;
    235			break;
    236		case COMP_No_Comp:
    237			f = SW_C3 | SW_C2 | SW_C0;
    238			break;
    239		default:
    240#ifdef PARANOID
    241			EXCEPTION(EX_INTERNAL | 0x122);
    242#endif /* PARANOID */
    243			f = SW_C3 | SW_C2 | SW_C0;
    244			break;
    245		}
    246	setcc(f);
    247	if (c & COMP_Denormal) {
    248		return denormal_operand() < 0;
    249	}
    250	return 0;
    251}
    252
    253static int compare_i_st_st(int nr)
    254{
    255	int f, c;
    256	FPU_REG *st_ptr;
    257
    258	if (!NOT_EMPTY(0) || !NOT_EMPTY(nr)) {
    259		FPU_EFLAGS |= (X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF);
    260		/* Stack fault */
    261		EXCEPTION(EX_StackUnder);
    262		return !(control_word & CW_Invalid);
    263	}
    264
    265	partial_status &= ~SW_C0;
    266	st_ptr = &st(nr);
    267	c = compare(st_ptr, FPU_gettagi(nr));
    268	if (c & COMP_NaN) {
    269		FPU_EFLAGS |= (X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF);
    270		EXCEPTION(EX_Invalid);
    271		return !(control_word & CW_Invalid);
    272	}
    273
    274	switch (c & 7) {
    275	case COMP_A_lt_B:
    276		f = X86_EFLAGS_CF;
    277		break;
    278	case COMP_A_eq_B:
    279		f = X86_EFLAGS_ZF;
    280		break;
    281	case COMP_A_gt_B:
    282		f = 0;
    283		break;
    284	case COMP_No_Comp:
    285		f = X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF;
    286		break;
    287	default:
    288#ifdef PARANOID
    289		EXCEPTION(EX_INTERNAL | 0x122);
    290#endif /* PARANOID */
    291		f = 0;
    292		break;
    293	}
    294	FPU_EFLAGS = (FPU_EFLAGS & ~(X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF)) | f;
    295	if (c & COMP_Denormal) {
    296		return denormal_operand() < 0;
    297	}
    298	return 0;
    299}
    300
    301static int compare_u_st_st(int nr)
    302{
    303	int f = 0, c;
    304	FPU_REG *st_ptr;
    305
    306	if (!NOT_EMPTY(0) || !NOT_EMPTY(nr)) {
    307		setcc(SW_C3 | SW_C2 | SW_C0);
    308		/* Stack fault */
    309		EXCEPTION(EX_StackUnder);
    310		return !(control_word & CW_Invalid);
    311	}
    312
    313	st_ptr = &st(nr);
    314	c = compare(st_ptr, FPU_gettagi(nr));
    315	if (c & COMP_NaN) {
    316		setcc(SW_C3 | SW_C2 | SW_C0);
    317		if (c & COMP_SNaN) {	/* This is the only difference between
    318					   un-ordered and ordinary comparisons */
    319			EXCEPTION(EX_Invalid);
    320			return !(control_word & CW_Invalid);
    321		}
    322		return 0;
    323	} else
    324		switch (c & 7) {
    325		case COMP_A_lt_B:
    326			f = SW_C0;
    327			break;
    328		case COMP_A_eq_B:
    329			f = SW_C3;
    330			break;
    331		case COMP_A_gt_B:
    332			f = 0;
    333			break;
    334		case COMP_No_Comp:
    335			f = SW_C3 | SW_C2 | SW_C0;
    336			break;
    337#ifdef PARANOID
    338		default:
    339			EXCEPTION(EX_INTERNAL | 0x123);
    340			f = SW_C3 | SW_C2 | SW_C0;
    341			break;
    342#endif /* PARANOID */
    343		}
    344	setcc(f);
    345	if (c & COMP_Denormal) {
    346		return denormal_operand() < 0;
    347	}
    348	return 0;
    349}
    350
    351static int compare_ui_st_st(int nr)
    352{
    353	int f = 0, c;
    354	FPU_REG *st_ptr;
    355
    356	if (!NOT_EMPTY(0) || !NOT_EMPTY(nr)) {
    357		FPU_EFLAGS |= (X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF);
    358		/* Stack fault */
    359		EXCEPTION(EX_StackUnder);
    360		return !(control_word & CW_Invalid);
    361	}
    362
    363	partial_status &= ~SW_C0;
    364	st_ptr = &st(nr);
    365	c = compare(st_ptr, FPU_gettagi(nr));
    366	if (c & COMP_NaN) {
    367		FPU_EFLAGS |= (X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF);
    368		if (c & COMP_SNaN) {	/* This is the only difference between
    369					   un-ordered and ordinary comparisons */
    370			EXCEPTION(EX_Invalid);
    371			return !(control_word & CW_Invalid);
    372		}
    373		return 0;
    374	}
    375
    376	switch (c & 7) {
    377	case COMP_A_lt_B:
    378		f = X86_EFLAGS_CF;
    379		break;
    380	case COMP_A_eq_B:
    381		f = X86_EFLAGS_ZF;
    382		break;
    383	case COMP_A_gt_B:
    384		f = 0;
    385		break;
    386	case COMP_No_Comp:
    387		f = X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF;
    388		break;
    389#ifdef PARANOID
    390	default:
    391		EXCEPTION(EX_INTERNAL | 0x123);
    392		f = 0;
    393		break;
    394#endif /* PARANOID */
    395	}
    396	FPU_EFLAGS = (FPU_EFLAGS & ~(X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF)) | f;
    397	if (c & COMP_Denormal) {
    398		return denormal_operand() < 0;
    399	}
    400	return 0;
    401}
    402
    403/*---------------------------------------------------------------------------*/
    404
    405void fcom_st(void)
    406{
    407	/* fcom st(i) */
    408	compare_st_st(FPU_rm);
    409}
    410
    411void fcompst(void)
    412{
    413	/* fcomp st(i) */
    414	if (!compare_st_st(FPU_rm))
    415		FPU_pop();
    416}
    417
    418void fcompp(void)
    419{
    420	/* fcompp */
    421	if (FPU_rm != 1) {
    422		FPU_illegal();
    423		return;
    424	}
    425	if (!compare_st_st(1))
    426		poppop();
    427}
    428
    429void fucom_(void)
    430{
    431	/* fucom st(i) */
    432	compare_u_st_st(FPU_rm);
    433
    434}
    435
    436void fucomp(void)
    437{
    438	/* fucomp st(i) */
    439	if (!compare_u_st_st(FPU_rm))
    440		FPU_pop();
    441}
    442
    443void fucompp(void)
    444{
    445	/* fucompp */
    446	if (FPU_rm == 1) {
    447		if (!compare_u_st_st(1))
    448			poppop();
    449	} else
    450		FPU_illegal();
    451}
    452
    453/* P6+ compare-to-EFLAGS ops */
    454
    455void fcomi_(void)
    456{
    457	/* fcomi st(i) */
    458	compare_i_st_st(FPU_rm);
    459}
    460
    461void fcomip(void)
    462{
    463	/* fcomip st(i) */
    464	if (!compare_i_st_st(FPU_rm))
    465		FPU_pop();
    466}
    467
    468void fucomi_(void)
    469{
    470	/* fucomi st(i) */
    471	compare_ui_st_st(FPU_rm);
    472}
    473
    474void fucomip(void)
    475{
    476	/* fucomip st(i) */
    477	if (!compare_ui_st_st(FPU_rm))
    478		FPU_pop();
    479}