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

cop2-ex.c (8057B)


      1/*
      2 * This file is subject to the terms and conditions of the GNU General Public
      3 * License.  See the file "COPYING" in the main directory of this archive
      4 * for more details.
      5 *
      6 * Copyright (C) 2014 Lemote Corporation.
      7 *   written by Huacai Chen <chenhc@lemote.com>
      8 *
      9 * based on arch/mips/cavium-octeon/cpu.c
     10 * Copyright (C) 2009 Wind River Systems,
     11 *   written by Ralf Baechle <ralf@linux-mips.org>
     12 */
     13#include <linux/init.h>
     14#include <linux/sched.h>
     15#include <linux/notifier.h>
     16#include <linux/ptrace.h>
     17#include <linux/uaccess.h>
     18#include <linux/sched/signal.h>
     19
     20#include <asm/fpu.h>
     21#include <asm/cop2.h>
     22#include <asm/inst.h>
     23#include <asm/branch.h>
     24#include <asm/current.h>
     25#include <asm/mipsregs.h>
     26#include <asm/unaligned-emul.h>
     27
     28static int loongson_cu2_call(struct notifier_block *nfb, unsigned long action,
     29	void *data)
     30{
     31	unsigned int res, fpu_owned;
     32	unsigned long ra, value, value_next;
     33	union mips_instruction insn;
     34	int fr = !test_thread_flag(TIF_32BIT_FPREGS);
     35	struct pt_regs *regs = (struct pt_regs *)data;
     36	void __user *addr = (void __user *)regs->cp0_badvaddr;
     37	unsigned int __user *pc = (unsigned int __user *)exception_epc(regs);
     38
     39	ra = regs->regs[31];
     40	__get_user(insn.word, pc);
     41
     42	switch (action) {
     43	case CU2_EXCEPTION:
     44		preempt_disable();
     45		fpu_owned = __is_fpu_owner();
     46		if (!fr)
     47			set_c0_status(ST0_CU1 | ST0_CU2);
     48		else
     49			set_c0_status(ST0_CU1 | ST0_CU2 | ST0_FR);
     50		enable_fpu_hazard();
     51		KSTK_STATUS(current) |= (ST0_CU1 | ST0_CU2);
     52		if (fr)
     53			KSTK_STATUS(current) |= ST0_FR;
     54		else
     55			KSTK_STATUS(current) &= ~ST0_FR;
     56		/* If FPU is owned, we needn't init or restore fp */
     57		if (!fpu_owned) {
     58			set_thread_flag(TIF_USEDFPU);
     59			init_fp_ctx(current);
     60			_restore_fp(current);
     61		}
     62		preempt_enable();
     63
     64		return NOTIFY_STOP;	/* Don't call default notifier */
     65
     66	case CU2_LWC2_OP:
     67		if (insn.loongson3_lswc2_format.ls == 0)
     68			goto sigbus;
     69
     70		if (insn.loongson3_lswc2_format.fr == 0) {	/* gslq */
     71			if (!access_ok(addr, 16))
     72				goto sigbus;
     73
     74			LoadDW(addr, value, res);
     75			if (res)
     76				goto fault;
     77
     78			LoadDW(addr + 8, value_next, res);
     79			if (res)
     80				goto fault;
     81
     82			regs->regs[insn.loongson3_lswc2_format.rt] = value;
     83			regs->regs[insn.loongson3_lswc2_format.rq] = value_next;
     84			compute_return_epc(regs);
     85		} else {					/* gslqc1 */
     86			if (!access_ok(addr, 16))
     87				goto sigbus;
     88
     89			lose_fpu(1);
     90			LoadDW(addr, value, res);
     91			if (res)
     92				goto fault;
     93
     94			LoadDW(addr + 8, value_next, res);
     95			if (res)
     96				goto fault;
     97
     98			set_fpr64(&current->thread.fpu.fpr[insn.loongson3_lswc2_format.rt], 0, value);
     99			set_fpr64(&current->thread.fpu.fpr[insn.loongson3_lswc2_format.rq], 0, value_next);
    100			compute_return_epc(regs);
    101			own_fpu(1);
    102		}
    103		return NOTIFY_STOP;	/* Don't call default notifier */
    104
    105	case CU2_SWC2_OP:
    106		if (insn.loongson3_lswc2_format.ls == 0)
    107			goto sigbus;
    108
    109		if (insn.loongson3_lswc2_format.fr == 0) {	/* gssq */
    110			if (!access_ok(addr, 16))
    111				goto sigbus;
    112
    113			/* write upper 8 bytes first */
    114			value_next = regs->regs[insn.loongson3_lswc2_format.rq];
    115
    116			StoreDW(addr + 8, value_next, res);
    117			if (res)
    118				goto fault;
    119			value = regs->regs[insn.loongson3_lswc2_format.rt];
    120
    121			StoreDW(addr, value, res);
    122			if (res)
    123				goto fault;
    124
    125			compute_return_epc(regs);
    126		} else {					/* gssqc1 */
    127			if (!access_ok(addr, 16))
    128				goto sigbus;
    129
    130			lose_fpu(1);
    131			value_next = get_fpr64(&current->thread.fpu.fpr[insn.loongson3_lswc2_format.rq], 0);
    132
    133			StoreDW(addr + 8, value_next, res);
    134			if (res)
    135				goto fault;
    136
    137			value = get_fpr64(&current->thread.fpu.fpr[insn.loongson3_lswc2_format.rt], 0);
    138
    139			StoreDW(addr, value, res);
    140			if (res)
    141				goto fault;
    142
    143			compute_return_epc(regs);
    144			own_fpu(1);
    145		}
    146		return NOTIFY_STOP;	/* Don't call default notifier */
    147
    148	case CU2_LDC2_OP:
    149		switch (insn.loongson3_lsdc2_format.opcode1) {
    150		/*
    151		 * Loongson-3 overridden ldc2 instructions.
    152		 * opcode1              instruction
    153		 *   0x1          gslhx: load 2 bytes to GPR
    154		 *   0x2          gslwx: load 4 bytes to GPR
    155		 *   0x3          gsldx: load 8 bytes to GPR
    156		 *   0x6	  gslwxc1: load 4 bytes to FPR
    157		 *   0x7	  gsldxc1: load 8 bytes to FPR
    158		 */
    159		case 0x1:
    160			if (!access_ok(addr, 2))
    161				goto sigbus;
    162
    163			LoadHW(addr, value, res);
    164			if (res)
    165				goto fault;
    166
    167			compute_return_epc(regs);
    168			regs->regs[insn.loongson3_lsdc2_format.rt] = value;
    169			break;
    170		case 0x2:
    171			if (!access_ok(addr, 4))
    172				goto sigbus;
    173
    174			LoadW(addr, value, res);
    175			if (res)
    176				goto fault;
    177
    178			compute_return_epc(regs);
    179			regs->regs[insn.loongson3_lsdc2_format.rt] = value;
    180			break;
    181		case 0x3:
    182			if (!access_ok(addr, 8))
    183				goto sigbus;
    184
    185			LoadDW(addr, value, res);
    186			if (res)
    187				goto fault;
    188
    189			compute_return_epc(regs);
    190			regs->regs[insn.loongson3_lsdc2_format.rt] = value;
    191			break;
    192		case 0x6:
    193			die_if_kernel("Unaligned FP access in kernel code", regs);
    194			BUG_ON(!used_math());
    195			if (!access_ok(addr, 4))
    196				goto sigbus;
    197
    198			lose_fpu(1);
    199			LoadW(addr, value, res);
    200			if (res)
    201				goto fault;
    202
    203			set_fpr64(&current->thread.fpu.fpr[insn.loongson3_lsdc2_format.rt], 0, value);
    204			compute_return_epc(regs);
    205			own_fpu(1);
    206
    207			break;
    208		case 0x7:
    209			die_if_kernel("Unaligned FP access in kernel code", regs);
    210			BUG_ON(!used_math());
    211			if (!access_ok(addr, 8))
    212				goto sigbus;
    213
    214			lose_fpu(1);
    215			LoadDW(addr, value, res);
    216			if (res)
    217				goto fault;
    218
    219			set_fpr64(&current->thread.fpu.fpr[insn.loongson3_lsdc2_format.rt], 0, value);
    220			compute_return_epc(regs);
    221			own_fpu(1);
    222			break;
    223
    224		}
    225		return NOTIFY_STOP;	/* Don't call default notifier */
    226
    227	case CU2_SDC2_OP:
    228		switch (insn.loongson3_lsdc2_format.opcode1) {
    229		/*
    230		 * Loongson-3 overridden sdc2 instructions.
    231		 * opcode1              instruction
    232		 *   0x1          gsshx: store 2 bytes from GPR
    233		 *   0x2          gsswx: store 4 bytes from GPR
    234		 *   0x3          gssdx: store 8 bytes from GPR
    235		 *   0x6          gsswxc1: store 4 bytes from FPR
    236		 *   0x7          gssdxc1: store 8 bytes from FPR
    237		 */
    238		case 0x1:
    239			if (!access_ok(addr, 2))
    240				goto sigbus;
    241
    242			compute_return_epc(regs);
    243			value = regs->regs[insn.loongson3_lsdc2_format.rt];
    244
    245			StoreHW(addr, value, res);
    246			if (res)
    247				goto fault;
    248
    249			break;
    250		case 0x2:
    251			if (!access_ok(addr, 4))
    252				goto sigbus;
    253
    254			compute_return_epc(regs);
    255			value = regs->regs[insn.loongson3_lsdc2_format.rt];
    256
    257			StoreW(addr, value, res);
    258			if (res)
    259				goto fault;
    260
    261			break;
    262		case 0x3:
    263			if (!access_ok(addr, 8))
    264				goto sigbus;
    265
    266			compute_return_epc(regs);
    267			value = regs->regs[insn.loongson3_lsdc2_format.rt];
    268
    269			StoreDW(addr, value, res);
    270			if (res)
    271				goto fault;
    272
    273			break;
    274
    275		case 0x6:
    276			die_if_kernel("Unaligned FP access in kernel code", regs);
    277			BUG_ON(!used_math());
    278
    279			if (!access_ok(addr, 4))
    280				goto sigbus;
    281
    282			lose_fpu(1);
    283			value = get_fpr64(&current->thread.fpu.fpr[insn.loongson3_lsdc2_format.rt], 0);
    284
    285			StoreW(addr, value, res);
    286			if (res)
    287				goto fault;
    288
    289			compute_return_epc(regs);
    290			own_fpu(1);
    291
    292			break;
    293		case 0x7:
    294			die_if_kernel("Unaligned FP access in kernel code", regs);
    295			BUG_ON(!used_math());
    296
    297			if (!access_ok(addr, 8))
    298				goto sigbus;
    299
    300			lose_fpu(1);
    301			value = get_fpr64(&current->thread.fpu.fpr[insn.loongson3_lsdc2_format.rt], 0);
    302
    303			StoreDW(addr, value, res);
    304			if (res)
    305				goto fault;
    306
    307			compute_return_epc(regs);
    308			own_fpu(1);
    309
    310			break;
    311		}
    312		return NOTIFY_STOP;	/* Don't call default notifier */
    313	}
    314
    315	return NOTIFY_OK;		/* Let default notifier send signals */
    316
    317fault:
    318	/* roll back jump/branch */
    319	regs->regs[31] = ra;
    320	regs->cp0_epc = (unsigned long)pc;
    321	/* Did we have an exception handler installed? */
    322	if (fixup_exception(regs))
    323		return NOTIFY_STOP;	/* Don't call default notifier */
    324
    325	die_if_kernel("Unhandled kernel unaligned access", regs);
    326	force_sig(SIGSEGV);
    327
    328	return NOTIFY_STOP;	/* Don't call default notifier */
    329
    330sigbus:
    331	die_if_kernel("Unhandled kernel unaligned access", regs);
    332	force_sig(SIGBUS);
    333
    334	return NOTIFY_STOP;	/* Don't call default notifier */
    335}
    336
    337static int __init loongson_cu2_setup(void)
    338{
    339	return cu2_notifier(loongson_cu2_call, 0);
    340}
    341early_initcall(loongson_cu2_setup);