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_u_div.S (12455B)


      1/* SPDX-License-Identifier: GPL-2.0 */
      2	.file	"reg_u_div.S"
      3/*---------------------------------------------------------------------------+
      4 |  reg_u_div.S                                                              |
      5 |                                                                           |
      6 | Divide one FPU_REG by another and put the result in a destination FPU_REG.|
      7 |                                                                           |
      8 | Copyright (C) 1992,1993,1995,1997                                         |
      9 |                  W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
     10 |                  E-mail   billm@suburbia.net                              |
     11 |                                                                           |
     12 |                                                                           |
     13 +---------------------------------------------------------------------------*/
     14
     15/*---------------------------------------------------------------------------+
     16 | Call from C as:                                                           |
     17 |    int FPU_u_div(FPU_REG *a, FPU_REG *b, FPU_REG *dest,                   |
     18 |                unsigned int control_word, char *sign)                     |
     19 |                                                                           |
     20 |  Does not compute the destination exponent, but does adjust it.           |
     21 |                                                                           |
     22 |    Return value is the tag of the answer, or-ed with FPU_Exception if     |
     23 |    one was raised, or -1 on internal error.                               |
     24 +---------------------------------------------------------------------------*/
     25
     26#include "exception.h"
     27#include "fpu_emu.h"
     28#include "control_w.h"
     29
     30
     31/* #define	dSIGL(x)	(x) */
     32/* #define	dSIGH(x)	4(x) */
     33
     34
     35#ifndef NON_REENTRANT_FPU
     36/*
     37	Local storage on the stack:
     38	Result:		FPU_accum_3:FPU_accum_2:FPU_accum_1:FPU_accum_0
     39	Overflow flag:	ovfl_flag
     40 */
     41#define FPU_accum_3	-4(%ebp)
     42#define FPU_accum_2	-8(%ebp)
     43#define FPU_accum_1	-12(%ebp)
     44#define FPU_accum_0	-16(%ebp)
     45#define FPU_result_1	-20(%ebp)
     46#define FPU_result_2	-24(%ebp)
     47#define FPU_ovfl_flag	-28(%ebp)
     48
     49#else
     50.data
     51/*
     52	Local storage in a static area:
     53	Result:		FPU_accum_3:FPU_accum_2:FPU_accum_1:FPU_accum_0
     54	Overflow flag:	ovfl_flag
     55 */
     56	.align 4,0
     57FPU_accum_3:
     58	.long	0
     59FPU_accum_2:
     60	.long	0
     61FPU_accum_1:
     62	.long	0
     63FPU_accum_0:
     64	.long	0
     65FPU_result_1:
     66	.long	0
     67FPU_result_2:
     68	.long	0
     69FPU_ovfl_flag:
     70	.byte	0
     71#endif /* NON_REENTRANT_FPU */
     72
     73#define REGA	PARAM1
     74#define REGB	PARAM2
     75#define DEST	PARAM3
     76
     77.text
     78SYM_FUNC_START(FPU_u_div)
     79	pushl	%ebp
     80	movl	%esp,%ebp
     81#ifndef NON_REENTRANT_FPU
     82	subl	$28,%esp
     83#endif /* NON_REENTRANT_FPU */
     84
     85	pushl	%esi
     86	pushl	%edi
     87	pushl	%ebx
     88
     89	movl	REGA,%esi
     90	movl	REGB,%ebx
     91	movl	DEST,%edi
     92
     93	movswl	EXP(%esi),%edx
     94	movswl	EXP(%ebx),%eax
     95	subl	%eax,%edx
     96	addl	EXP_BIAS,%edx
     97
     98	/* A denormal and a large number can cause an exponent underflow */
     99	cmpl	EXP_WAY_UNDER,%edx
    100	jg	xExp_not_underflow
    101
    102	/* Set to a really low value allow correct handling */
    103	movl	EXP_WAY_UNDER,%edx
    104
    105xExp_not_underflow:
    106
    107	movw    %dx,EXP(%edi)
    108
    109#ifdef PARANOID
    110/*	testl	$0x80000000, SIGH(%esi)	// Dividend */
    111/*	je	L_bugged */
    112	testl	$0x80000000, SIGH(%ebx)	/* Divisor */
    113	je	L_bugged
    114#endif /* PARANOID */ 
    115
    116/* Check if the divisor can be treated as having just 32 bits */
    117	cmpl	$0,SIGL(%ebx)
    118	jnz	L_Full_Division	/* Can't do a quick divide */
    119
    120/* We should be able to zip through the division here */
    121	movl	SIGH(%ebx),%ecx	/* The divisor */
    122	movl	SIGH(%esi),%edx	/* Dividend */
    123	movl	SIGL(%esi),%eax	/* Dividend */
    124
    125	cmpl	%ecx,%edx
    126	setaeb	FPU_ovfl_flag	/* Keep a record */
    127	jb	L_no_adjust
    128
    129	subl	%ecx,%edx	/* Prevent the overflow */
    130
    131L_no_adjust:
    132	/* Divide the 64 bit number by the 32 bit denominator */
    133	divl	%ecx
    134	movl	%eax,FPU_result_2
    135
    136	/* Work on the remainder of the first division */
    137	xorl	%eax,%eax
    138	divl	%ecx
    139	movl	%eax,FPU_result_1
    140
    141	/* Work on the remainder of the 64 bit division */
    142	xorl	%eax,%eax
    143	divl	%ecx
    144
    145	testb	$255,FPU_ovfl_flag	/* was the num > denom ? */
    146	je	L_no_overflow
    147
    148	/* Do the shifting here */
    149	/* increase the exponent */
    150	incw	EXP(%edi)
    151
    152	/* shift the mantissa right one bit */
    153	stc			/* To set the ms bit */
    154	rcrl	FPU_result_2
    155	rcrl	FPU_result_1
    156	rcrl	%eax
    157
    158L_no_overflow:
    159	jmp	LRound_precision	/* Do the rounding as required */
    160
    161
    162/*---------------------------------------------------------------------------+
    163 |  Divide:   Return  arg1/arg2 to arg3.                                     |
    164 |                                                                           |
    165 |  This routine does not use the exponents of arg1 and arg2, but does       |
    166 |  adjust the exponent of arg3.                                             |
    167 |                                                                           |
    168 |  The maximum returned value is (ignoring exponents)                       |
    169 |               .ffffffff ffffffff                                          |
    170 |               ------------------  =  1.ffffffff fffffffe                  |
    171 |               .80000000 00000000                                          |
    172 | and the minimum is                                                        |
    173 |               .80000000 00000000                                          |
    174 |               ------------------  =  .80000000 00000001   (rounded)       |
    175 |               .ffffffff ffffffff                                          |
    176 |                                                                           |
    177 +---------------------------------------------------------------------------*/
    178
    179
    180L_Full_Division:
    181	/* Save extended dividend in local register */
    182	movl	SIGL(%esi),%eax
    183	movl	%eax,FPU_accum_2
    184	movl	SIGH(%esi),%eax
    185	movl	%eax,FPU_accum_3
    186	xorl	%eax,%eax
    187	movl	%eax,FPU_accum_1	/* zero the extension */
    188	movl	%eax,FPU_accum_0	/* zero the extension */
    189
    190	movl	SIGL(%esi),%eax	/* Get the current num */
    191	movl	SIGH(%esi),%edx
    192
    193/*----------------------------------------------------------------------*/
    194/* Initialization done.
    195   Do the first 32 bits. */
    196
    197	movb	$0,FPU_ovfl_flag
    198	cmpl	SIGH(%ebx),%edx	/* Test for imminent overflow */
    199	jb	LLess_than_1
    200	ja	LGreater_than_1
    201
    202	cmpl	SIGL(%ebx),%eax
    203	jb	LLess_than_1
    204
    205LGreater_than_1:
    206/* The dividend is greater or equal, would cause overflow */
    207	setaeb	FPU_ovfl_flag		/* Keep a record */
    208
    209	subl	SIGL(%ebx),%eax
    210	sbbl	SIGH(%ebx),%edx	/* Prevent the overflow */
    211	movl	%eax,FPU_accum_2
    212	movl	%edx,FPU_accum_3
    213
    214LLess_than_1:
    215/* At this point, we have a dividend < divisor, with a record of
    216   adjustment in FPU_ovfl_flag */
    217
    218	/* We will divide by a number which is too large */
    219	movl	SIGH(%ebx),%ecx
    220	addl	$1,%ecx
    221	jnc	LFirst_div_not_1
    222
    223	/* here we need to divide by 100000000h,
    224	   i.e., no division at all.. */
    225	mov	%edx,%eax
    226	jmp	LFirst_div_done
    227
    228LFirst_div_not_1:
    229	divl	%ecx		/* Divide the numerator by the augmented
    230				   denom ms dw */
    231
    232LFirst_div_done:
    233	movl	%eax,FPU_result_2	/* Put the result in the answer */
    234
    235	mull	SIGH(%ebx)	/* mul by the ms dw of the denom */
    236
    237	subl	%eax,FPU_accum_2	/* Subtract from the num local reg */
    238	sbbl	%edx,FPU_accum_3
    239
    240	movl	FPU_result_2,%eax	/* Get the result back */
    241	mull	SIGL(%ebx)	/* now mul the ls dw of the denom */
    242
    243	subl	%eax,FPU_accum_1	/* Subtract from the num local reg */
    244	sbbl	%edx,FPU_accum_2
    245	sbbl	$0,FPU_accum_3
    246	je	LDo_2nd_32_bits		/* Must check for non-zero result here */
    247
    248#ifdef PARANOID
    249	jb	L_bugged_1
    250#endif /* PARANOID */ 
    251
    252	/* need to subtract another once of the denom */
    253	incl	FPU_result_2	/* Correct the answer */
    254
    255	movl	SIGL(%ebx),%eax
    256	movl	SIGH(%ebx),%edx
    257	subl	%eax,FPU_accum_1	/* Subtract from the num local reg */
    258	sbbl	%edx,FPU_accum_2
    259
    260#ifdef PARANOID
    261	sbbl	$0,FPU_accum_3
    262	jne	L_bugged_1	/* Must check for non-zero result here */
    263#endif /* PARANOID */ 
    264
    265/*----------------------------------------------------------------------*/
    266/* Half of the main problem is done, there is just a reduced numerator
    267   to handle now.
    268   Work with the second 32 bits, FPU_accum_0 not used from now on */
    269LDo_2nd_32_bits:
    270	movl	FPU_accum_2,%edx	/* get the reduced num */
    271	movl	FPU_accum_1,%eax
    272
    273	/* need to check for possible subsequent overflow */
    274	cmpl	SIGH(%ebx),%edx
    275	jb	LDo_2nd_div
    276	ja	LPrevent_2nd_overflow
    277
    278	cmpl	SIGL(%ebx),%eax
    279	jb	LDo_2nd_div
    280
    281LPrevent_2nd_overflow:
    282/* The numerator is greater or equal, would cause overflow */
    283	/* prevent overflow */
    284	subl	SIGL(%ebx),%eax
    285	sbbl	SIGH(%ebx),%edx
    286	movl	%edx,FPU_accum_2
    287	movl	%eax,FPU_accum_1
    288
    289	incl	FPU_result_2	/* Reflect the subtraction in the answer */
    290
    291#ifdef PARANOID
    292	je	L_bugged_2	/* Can't bump the result to 1.0 */
    293#endif /* PARANOID */ 
    294
    295LDo_2nd_div:
    296	cmpl	$0,%ecx		/* augmented denom msw */
    297	jnz	LSecond_div_not_1
    298
    299	/* %ecx == 0, we are dividing by 1.0 */
    300	mov	%edx,%eax
    301	jmp	LSecond_div_done
    302
    303LSecond_div_not_1:
    304	divl	%ecx		/* Divide the numerator by the denom ms dw */
    305
    306LSecond_div_done:
    307	movl	%eax,FPU_result_1	/* Put the result in the answer */
    308
    309	mull	SIGH(%ebx)	/* mul by the ms dw of the denom */
    310
    311	subl	%eax,FPU_accum_1	/* Subtract from the num local reg */
    312	sbbl	%edx,FPU_accum_2
    313
    314#ifdef PARANOID
    315	jc	L_bugged_2
    316#endif /* PARANOID */ 
    317
    318	movl	FPU_result_1,%eax	/* Get the result back */
    319	mull	SIGL(%ebx)	/* now mul the ls dw of the denom */
    320
    321	subl	%eax,FPU_accum_0	/* Subtract from the num local reg */
    322	sbbl	%edx,FPU_accum_1	/* Subtract from the num local reg */
    323	sbbl	$0,FPU_accum_2
    324
    325#ifdef PARANOID
    326	jc	L_bugged_2
    327#endif /* PARANOID */ 
    328
    329	jz	LDo_3rd_32_bits
    330
    331#ifdef PARANOID
    332	cmpl	$1,FPU_accum_2
    333	jne	L_bugged_2
    334#endif /* PARANOID */
    335
    336	/* need to subtract another once of the denom */
    337	movl	SIGL(%ebx),%eax
    338	movl	SIGH(%ebx),%edx
    339	subl	%eax,FPU_accum_0	/* Subtract from the num local reg */
    340	sbbl	%edx,FPU_accum_1
    341	sbbl	$0,FPU_accum_2
    342
    343#ifdef PARANOID
    344	jc	L_bugged_2
    345	jne	L_bugged_2
    346#endif /* PARANOID */ 
    347
    348	addl	$1,FPU_result_1	/* Correct the answer */
    349	adcl	$0,FPU_result_2
    350
    351#ifdef PARANOID
    352	jc	L_bugged_2	/* Must check for non-zero result here */
    353#endif /* PARANOID */
    354
    355/*----------------------------------------------------------------------*/
    356/* The division is essentially finished here, we just need to perform
    357   tidying operations.
    358   Deal with the 3rd 32 bits */
    359LDo_3rd_32_bits:
    360	movl	FPU_accum_1,%edx		/* get the reduced num */
    361	movl	FPU_accum_0,%eax
    362
    363	/* need to check for possible subsequent overflow */
    364	cmpl	SIGH(%ebx),%edx	/* denom */
    365	jb	LRound_prep
    366	ja	LPrevent_3rd_overflow
    367
    368	cmpl	SIGL(%ebx),%eax	/* denom */
    369	jb	LRound_prep
    370
    371LPrevent_3rd_overflow:
    372	/* prevent overflow */
    373	subl	SIGL(%ebx),%eax
    374	sbbl	SIGH(%ebx),%edx
    375	movl	%edx,FPU_accum_1
    376	movl	%eax,FPU_accum_0
    377
    378	addl	$1,FPU_result_1	/* Reflect the subtraction in the answer */
    379	adcl	$0,FPU_result_2
    380	jne	LRound_prep
    381	jnc	LRound_prep
    382
    383	/* This is a tricky spot, there is an overflow of the answer */
    384	movb	$255,FPU_ovfl_flag		/* Overflow -> 1.000 */
    385
    386LRound_prep:
    387/*
    388 * Prepare for rounding.
    389 * To test for rounding, we just need to compare 2*accum with the
    390 * denom.
    391 */
    392	movl	FPU_accum_0,%ecx
    393	movl	FPU_accum_1,%edx
    394	movl	%ecx,%eax
    395	orl	%edx,%eax
    396	jz	LRound_ovfl		/* The accumulator contains zero. */
    397
    398	/* Multiply by 2 */
    399	clc
    400	rcll	$1,%ecx
    401	rcll	$1,%edx
    402	jc	LRound_large		/* No need to compare, denom smaller */
    403
    404	subl	SIGL(%ebx),%ecx
    405	sbbl	SIGH(%ebx),%edx
    406	jnc	LRound_not_small
    407
    408	movl	$0x70000000,%eax	/* Denom was larger */
    409	jmp	LRound_ovfl
    410
    411LRound_not_small:
    412	jnz	LRound_large
    413
    414	movl	$0x80000000,%eax	/* Remainder was exactly 1/2 denom */
    415	jmp	LRound_ovfl
    416
    417LRound_large:
    418	movl	$0xff000000,%eax	/* Denom was smaller */
    419
    420LRound_ovfl:
    421/* We are now ready to deal with rounding, but first we must get
    422   the bits properly aligned */
    423	testb	$255,FPU_ovfl_flag	/* was the num > denom ? */
    424	je	LRound_precision
    425
    426	incw	EXP(%edi)
    427
    428	/* shift the mantissa right one bit */
    429	stc			/* Will set the ms bit */
    430	rcrl	FPU_result_2
    431	rcrl	FPU_result_1
    432	rcrl	%eax
    433
    434/* Round the result as required */
    435LRound_precision:
    436	decw	EXP(%edi)	/* binary point between 1st & 2nd bits */
    437
    438	movl	%eax,%edx
    439	movl	FPU_result_1,%ebx
    440	movl	FPU_result_2,%eax
    441	jmp	fpu_reg_round
    442
    443
    444#ifdef PARANOID
    445/* The logic is wrong if we got here */
    446L_bugged:
    447	pushl	EX_INTERNAL|0x202
    448	call	EXCEPTION
    449	pop	%ebx
    450	jmp	L_exit
    451
    452L_bugged_1:
    453	pushl	EX_INTERNAL|0x203
    454	call	EXCEPTION
    455	pop	%ebx
    456	jmp	L_exit
    457
    458L_bugged_2:
    459	pushl	EX_INTERNAL|0x204
    460	call	EXCEPTION
    461	pop	%ebx
    462	jmp	L_exit
    463
    464L_exit:
    465	movl	$-1,%eax
    466	popl	%ebx
    467	popl	%edi
    468	popl	%esi
    469
    470	leave
    471	RET
    472#endif /* PARANOID */ 
    473
    474SYM_FUNC_END(FPU_u_div)