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

fpu.c (12311B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Save/restore floating point context for signal handlers.
      4 *
      5 * Copyright (C) 1999, 2000  Kaz Kojima & Niibe Yutaka
      6 * Copyright (C) 2006  ST Microelectronics Ltd. (denorm support)
      7 *
      8 * FIXME! These routines have not been tested for big endian case.
      9 */
     10#include <linux/sched/signal.h>
     11#include <linux/io.h>
     12#include <cpu/fpu.h>
     13#include <asm/processor.h>
     14#include <asm/fpu.h>
     15#include <asm/traps.h>
     16
     17/* The PR (precision) bit in the FP Status Register must be clear when
     18 * an frchg instruction is executed, otherwise the instruction is undefined.
     19 * Executing frchg with PR set causes a trap on some SH4 implementations.
     20 */
     21
     22#define FPSCR_RCHG 0x00000000
     23extern unsigned long long float64_div(unsigned long long a,
     24				      unsigned long long b);
     25extern unsigned long int float32_div(unsigned long int a, unsigned long int b);
     26extern unsigned long long float64_mul(unsigned long long a,
     27				      unsigned long long b);
     28extern unsigned long int float32_mul(unsigned long int a, unsigned long int b);
     29extern unsigned long long float64_add(unsigned long long a,
     30				      unsigned long long b);
     31extern unsigned long int float32_add(unsigned long int a, unsigned long int b);
     32extern unsigned long long float64_sub(unsigned long long a,
     33				      unsigned long long b);
     34extern unsigned long int float32_sub(unsigned long int a, unsigned long int b);
     35extern unsigned long int float64_to_float32(unsigned long long a);
     36static unsigned int fpu_exception_flags;
     37
     38/*
     39 * Save FPU registers onto task structure.
     40 */
     41void save_fpu(struct task_struct *tsk)
     42{
     43	unsigned long dummy;
     44
     45	enable_fpu();
     46	asm volatile ("sts.l	fpul, @-%0\n\t"
     47		      "sts.l	fpscr, @-%0\n\t"
     48		      "lds	%2, fpscr\n\t"
     49		      "frchg\n\t"
     50		      "fmov.s	fr15, @-%0\n\t"
     51		      "fmov.s	fr14, @-%0\n\t"
     52		      "fmov.s	fr13, @-%0\n\t"
     53		      "fmov.s	fr12, @-%0\n\t"
     54		      "fmov.s	fr11, @-%0\n\t"
     55		      "fmov.s	fr10, @-%0\n\t"
     56		      "fmov.s	fr9, @-%0\n\t"
     57		      "fmov.s	fr8, @-%0\n\t"
     58		      "fmov.s	fr7, @-%0\n\t"
     59		      "fmov.s	fr6, @-%0\n\t"
     60		      "fmov.s	fr5, @-%0\n\t"
     61		      "fmov.s	fr4, @-%0\n\t"
     62		      "fmov.s	fr3, @-%0\n\t"
     63		      "fmov.s	fr2, @-%0\n\t"
     64		      "fmov.s	fr1, @-%0\n\t"
     65		      "fmov.s	fr0, @-%0\n\t"
     66		      "frchg\n\t"
     67		      "fmov.s	fr15, @-%0\n\t"
     68		      "fmov.s	fr14, @-%0\n\t"
     69		      "fmov.s	fr13, @-%0\n\t"
     70		      "fmov.s	fr12, @-%0\n\t"
     71		      "fmov.s	fr11, @-%0\n\t"
     72		      "fmov.s	fr10, @-%0\n\t"
     73		      "fmov.s	fr9, @-%0\n\t"
     74		      "fmov.s	fr8, @-%0\n\t"
     75		      "fmov.s	fr7, @-%0\n\t"
     76		      "fmov.s	fr6, @-%0\n\t"
     77		      "fmov.s	fr5, @-%0\n\t"
     78		      "fmov.s	fr4, @-%0\n\t"
     79		      "fmov.s	fr3, @-%0\n\t"
     80		      "fmov.s	fr2, @-%0\n\t"
     81		      "fmov.s	fr1, @-%0\n\t"
     82		      "fmov.s	fr0, @-%0\n\t"
     83		      "lds	%3, fpscr\n\t":"=r" (dummy)
     84		      :"0"((char *)(&tsk->thread.xstate->hardfpu.status)),
     85		      "r"(FPSCR_RCHG), "r"(FPSCR_INIT)
     86		      :"memory");
     87
     88	disable_fpu();
     89}
     90
     91void restore_fpu(struct task_struct *tsk)
     92{
     93	unsigned long dummy;
     94
     95	enable_fpu();
     96	asm volatile ("lds	%2, fpscr\n\t"
     97		      "fmov.s	@%0+, fr0\n\t"
     98		      "fmov.s	@%0+, fr1\n\t"
     99		      "fmov.s	@%0+, fr2\n\t"
    100		      "fmov.s	@%0+, fr3\n\t"
    101		      "fmov.s	@%0+, fr4\n\t"
    102		      "fmov.s	@%0+, fr5\n\t"
    103		      "fmov.s	@%0+, fr6\n\t"
    104		      "fmov.s	@%0+, fr7\n\t"
    105		      "fmov.s	@%0+, fr8\n\t"
    106		      "fmov.s	@%0+, fr9\n\t"
    107		      "fmov.s	@%0+, fr10\n\t"
    108		      "fmov.s	@%0+, fr11\n\t"
    109		      "fmov.s	@%0+, fr12\n\t"
    110		      "fmov.s	@%0+, fr13\n\t"
    111		      "fmov.s	@%0+, fr14\n\t"
    112		      "fmov.s	@%0+, fr15\n\t"
    113		      "frchg\n\t"
    114		      "fmov.s	@%0+, fr0\n\t"
    115		      "fmov.s	@%0+, fr1\n\t"
    116		      "fmov.s	@%0+, fr2\n\t"
    117		      "fmov.s	@%0+, fr3\n\t"
    118		      "fmov.s	@%0+, fr4\n\t"
    119		      "fmov.s	@%0+, fr5\n\t"
    120		      "fmov.s	@%0+, fr6\n\t"
    121		      "fmov.s	@%0+, fr7\n\t"
    122		      "fmov.s	@%0+, fr8\n\t"
    123		      "fmov.s	@%0+, fr9\n\t"
    124		      "fmov.s	@%0+, fr10\n\t"
    125		      "fmov.s	@%0+, fr11\n\t"
    126		      "fmov.s	@%0+, fr12\n\t"
    127		      "fmov.s	@%0+, fr13\n\t"
    128		      "fmov.s	@%0+, fr14\n\t"
    129		      "fmov.s	@%0+, fr15\n\t"
    130		      "frchg\n\t"
    131		      "lds.l	@%0+, fpscr\n\t"
    132		      "lds.l	@%0+, fpul\n\t"
    133		      :"=r" (dummy)
    134		      :"0" (tsk->thread.xstate), "r" (FPSCR_RCHG)
    135		      :"memory");
    136	disable_fpu();
    137}
    138
    139/**
    140 *      denormal_to_double - Given denormalized float number,
    141 *                           store double float
    142 *
    143 *      @fpu: Pointer to sh_fpu_hard structure
    144 *      @n: Index to FP register
    145 */
    146static void denormal_to_double(struct sh_fpu_hard_struct *fpu, int n)
    147{
    148	unsigned long du, dl;
    149	unsigned long x = fpu->fpul;
    150	int exp = 1023 - 126;
    151
    152	if (x != 0 && (x & 0x7f800000) == 0) {
    153		du = (x & 0x80000000);
    154		while ((x & 0x00800000) == 0) {
    155			x <<= 1;
    156			exp--;
    157		}
    158		x &= 0x007fffff;
    159		du |= (exp << 20) | (x >> 3);
    160		dl = x << 29;
    161
    162		fpu->fp_regs[n] = du;
    163		fpu->fp_regs[n + 1] = dl;
    164	}
    165}
    166
    167/**
    168 *	ieee_fpe_handler - Handle denormalized number exception
    169 *
    170 *	@regs: Pointer to register structure
    171 *
    172 *	Returns 1 when it's handled (should not cause exception).
    173 */
    174static int ieee_fpe_handler(struct pt_regs *regs)
    175{
    176	unsigned short insn = *(unsigned short *)regs->pc;
    177	unsigned short finsn;
    178	unsigned long nextpc;
    179	int nib[4] = {
    180		(insn >> 12) & 0xf,
    181		(insn >> 8) & 0xf,
    182		(insn >> 4) & 0xf,
    183		insn & 0xf
    184	};
    185
    186	if (nib[0] == 0xb || (nib[0] == 0x4 && nib[2] == 0x0 && nib[3] == 0xb))
    187		regs->pr = regs->pc + 4;  /* bsr & jsr */
    188
    189	if (nib[0] == 0xa || nib[0] == 0xb) {
    190		/* bra & bsr */
    191		nextpc = regs->pc + 4 + ((short)((insn & 0xfff) << 4) >> 3);
    192		finsn = *(unsigned short *)(regs->pc + 2);
    193	} else if (nib[0] == 0x8 && nib[1] == 0xd) {
    194		/* bt/s */
    195		if (regs->sr & 1)
    196			nextpc = regs->pc + 4 + ((char)(insn & 0xff) << 1);
    197		else
    198			nextpc = regs->pc + 4;
    199		finsn = *(unsigned short *)(regs->pc + 2);
    200	} else if (nib[0] == 0x8 && nib[1] == 0xf) {
    201		/* bf/s */
    202		if (regs->sr & 1)
    203			nextpc = regs->pc + 4;
    204		else
    205			nextpc = regs->pc + 4 + ((char)(insn & 0xff) << 1);
    206		finsn = *(unsigned short *)(regs->pc + 2);
    207	} else if (nib[0] == 0x4 && nib[3] == 0xb &&
    208		   (nib[2] == 0x0 || nib[2] == 0x2)) {
    209		/* jmp & jsr */
    210		nextpc = regs->regs[nib[1]];
    211		finsn = *(unsigned short *)(regs->pc + 2);
    212	} else if (nib[0] == 0x0 && nib[3] == 0x3 &&
    213		   (nib[2] == 0x0 || nib[2] == 0x2)) {
    214		/* braf & bsrf */
    215		nextpc = regs->pc + 4 + regs->regs[nib[1]];
    216		finsn = *(unsigned short *)(regs->pc + 2);
    217	} else if (insn == 0x000b) {
    218		/* rts */
    219		nextpc = regs->pr;
    220		finsn = *(unsigned short *)(regs->pc + 2);
    221	} else {
    222		nextpc = regs->pc + instruction_size(insn);
    223		finsn = insn;
    224	}
    225
    226	if ((finsn & 0xf1ff) == 0xf0ad) {
    227		/* fcnvsd */
    228		struct task_struct *tsk = current;
    229
    230		if ((tsk->thread.xstate->hardfpu.fpscr & FPSCR_CAUSE_ERROR))
    231			/* FPU error */
    232			denormal_to_double(&tsk->thread.xstate->hardfpu,
    233					   (finsn >> 8) & 0xf);
    234		else
    235			return 0;
    236
    237		regs->pc = nextpc;
    238		return 1;
    239	} else if ((finsn & 0xf00f) == 0xf002) {
    240		/* fmul */
    241		struct task_struct *tsk = current;
    242		int fpscr;
    243		int n, m, prec;
    244		unsigned int hx, hy;
    245
    246		n = (finsn >> 8) & 0xf;
    247		m = (finsn >> 4) & 0xf;
    248		hx = tsk->thread.xstate->hardfpu.fp_regs[n];
    249		hy = tsk->thread.xstate->hardfpu.fp_regs[m];
    250		fpscr = tsk->thread.xstate->hardfpu.fpscr;
    251		prec = fpscr & FPSCR_DBL_PRECISION;
    252
    253		if ((fpscr & FPSCR_CAUSE_ERROR)
    254		    && (prec && ((hx & 0x7fffffff) < 0x00100000
    255				 || (hy & 0x7fffffff) < 0x00100000))) {
    256			long long llx, lly;
    257
    258			/* FPU error because of denormal (doubles) */
    259			llx = ((long long)hx << 32)
    260			    | tsk->thread.xstate->hardfpu.fp_regs[n + 1];
    261			lly = ((long long)hy << 32)
    262			    | tsk->thread.xstate->hardfpu.fp_regs[m + 1];
    263			llx = float64_mul(llx, lly);
    264			tsk->thread.xstate->hardfpu.fp_regs[n] = llx >> 32;
    265			tsk->thread.xstate->hardfpu.fp_regs[n + 1] = llx & 0xffffffff;
    266		} else if ((fpscr & FPSCR_CAUSE_ERROR)
    267			   && (!prec && ((hx & 0x7fffffff) < 0x00800000
    268					 || (hy & 0x7fffffff) < 0x00800000))) {
    269			/* FPU error because of denormal (floats) */
    270			hx = float32_mul(hx, hy);
    271			tsk->thread.xstate->hardfpu.fp_regs[n] = hx;
    272		} else
    273			return 0;
    274
    275		regs->pc = nextpc;
    276		return 1;
    277	} else if ((finsn & 0xf00e) == 0xf000) {
    278		/* fadd, fsub */
    279		struct task_struct *tsk = current;
    280		int fpscr;
    281		int n, m, prec;
    282		unsigned int hx, hy;
    283
    284		n = (finsn >> 8) & 0xf;
    285		m = (finsn >> 4) & 0xf;
    286		hx = tsk->thread.xstate->hardfpu.fp_regs[n];
    287		hy = tsk->thread.xstate->hardfpu.fp_regs[m];
    288		fpscr = tsk->thread.xstate->hardfpu.fpscr;
    289		prec = fpscr & FPSCR_DBL_PRECISION;
    290
    291		if ((fpscr & FPSCR_CAUSE_ERROR)
    292		    && (prec && ((hx & 0x7fffffff) < 0x00100000
    293				 || (hy & 0x7fffffff) < 0x00100000))) {
    294			long long llx, lly;
    295
    296			/* FPU error because of denormal (doubles) */
    297			llx = ((long long)hx << 32)
    298			    | tsk->thread.xstate->hardfpu.fp_regs[n + 1];
    299			lly = ((long long)hy << 32)
    300			    | tsk->thread.xstate->hardfpu.fp_regs[m + 1];
    301			if ((finsn & 0xf00f) == 0xf000)
    302				llx = float64_add(llx, lly);
    303			else
    304				llx = float64_sub(llx, lly);
    305			tsk->thread.xstate->hardfpu.fp_regs[n] = llx >> 32;
    306			tsk->thread.xstate->hardfpu.fp_regs[n + 1] = llx & 0xffffffff;
    307		} else if ((fpscr & FPSCR_CAUSE_ERROR)
    308			   && (!prec && ((hx & 0x7fffffff) < 0x00800000
    309					 || (hy & 0x7fffffff) < 0x00800000))) {
    310			/* FPU error because of denormal (floats) */
    311			if ((finsn & 0xf00f) == 0xf000)
    312				hx = float32_add(hx, hy);
    313			else
    314				hx = float32_sub(hx, hy);
    315			tsk->thread.xstate->hardfpu.fp_regs[n] = hx;
    316		} else
    317			return 0;
    318
    319		regs->pc = nextpc;
    320		return 1;
    321	} else if ((finsn & 0xf003) == 0xf003) {
    322		/* fdiv */
    323		struct task_struct *tsk = current;
    324		int fpscr;
    325		int n, m, prec;
    326		unsigned int hx, hy;
    327
    328		n = (finsn >> 8) & 0xf;
    329		m = (finsn >> 4) & 0xf;
    330		hx = tsk->thread.xstate->hardfpu.fp_regs[n];
    331		hy = tsk->thread.xstate->hardfpu.fp_regs[m];
    332		fpscr = tsk->thread.xstate->hardfpu.fpscr;
    333		prec = fpscr & FPSCR_DBL_PRECISION;
    334
    335		if ((fpscr & FPSCR_CAUSE_ERROR)
    336		    && (prec && ((hx & 0x7fffffff) < 0x00100000
    337				 || (hy & 0x7fffffff) < 0x00100000))) {
    338			long long llx, lly;
    339
    340			/* FPU error because of denormal (doubles) */
    341			llx = ((long long)hx << 32)
    342			    | tsk->thread.xstate->hardfpu.fp_regs[n + 1];
    343			lly = ((long long)hy << 32)
    344			    | tsk->thread.xstate->hardfpu.fp_regs[m + 1];
    345
    346			llx = float64_div(llx, lly);
    347
    348			tsk->thread.xstate->hardfpu.fp_regs[n] = llx >> 32;
    349			tsk->thread.xstate->hardfpu.fp_regs[n + 1] = llx & 0xffffffff;
    350		} else if ((fpscr & FPSCR_CAUSE_ERROR)
    351			   && (!prec && ((hx & 0x7fffffff) < 0x00800000
    352					 || (hy & 0x7fffffff) < 0x00800000))) {
    353			/* FPU error because of denormal (floats) */
    354			hx = float32_div(hx, hy);
    355			tsk->thread.xstate->hardfpu.fp_regs[n] = hx;
    356		} else
    357			return 0;
    358
    359		regs->pc = nextpc;
    360		return 1;
    361	} else if ((finsn & 0xf0bd) == 0xf0bd) {
    362		/* fcnvds - double to single precision convert */
    363		struct task_struct *tsk = current;
    364		int m;
    365		unsigned int hx;
    366
    367		m = (finsn >> 8) & 0x7;
    368		hx = tsk->thread.xstate->hardfpu.fp_regs[m];
    369
    370		if ((tsk->thread.xstate->hardfpu.fpscr & FPSCR_CAUSE_ERROR)
    371			&& ((hx & 0x7fffffff) < 0x00100000)) {
    372			/* subnormal double to float conversion */
    373			long long llx;
    374
    375			llx = ((long long)tsk->thread.xstate->hardfpu.fp_regs[m] << 32)
    376			    | tsk->thread.xstate->hardfpu.fp_regs[m + 1];
    377
    378			tsk->thread.xstate->hardfpu.fpul = float64_to_float32(llx);
    379		} else
    380			return 0;
    381
    382		regs->pc = nextpc;
    383		return 1;
    384	}
    385
    386	return 0;
    387}
    388
    389void float_raise(unsigned int flags)
    390{
    391	fpu_exception_flags |= flags;
    392}
    393
    394int float_rounding_mode(void)
    395{
    396	struct task_struct *tsk = current;
    397	int roundingMode = FPSCR_ROUNDING_MODE(tsk->thread.xstate->hardfpu.fpscr);
    398	return roundingMode;
    399}
    400
    401BUILD_TRAP_HANDLER(fpu_error)
    402{
    403	struct task_struct *tsk = current;
    404	TRAP_HANDLER_DECL;
    405
    406	__unlazy_fpu(tsk, regs);
    407	fpu_exception_flags = 0;
    408	if (ieee_fpe_handler(regs)) {
    409		tsk->thread.xstate->hardfpu.fpscr &=
    410		    ~(FPSCR_CAUSE_MASK | FPSCR_FLAG_MASK);
    411		tsk->thread.xstate->hardfpu.fpscr |= fpu_exception_flags;
    412		/* Set the FPSCR flag as well as cause bits - simply
    413		 * replicate the cause */
    414		tsk->thread.xstate->hardfpu.fpscr |= (fpu_exception_flags >> 10);
    415		grab_fpu(regs);
    416		restore_fpu(tsk);
    417		task_thread_info(tsk)->status |= TS_USEDFPU;
    418		if ((((tsk->thread.xstate->hardfpu.fpscr & FPSCR_ENABLE_MASK) >> 7) &
    419		     (fpu_exception_flags >> 2)) == 0) {
    420			return;
    421		}
    422	}
    423
    424	force_sig(SIGFPE);
    425}