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

swp_emulate.c (6761B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 *  linux/arch/arm/kernel/swp_emulate.c
      4 *
      5 *  Copyright (C) 2009 ARM Limited
      6 *  __user_* functions adapted from include/asm/uaccess.h
      7 *
      8 *  Implements emulation of the SWP/SWPB instructions using load-exclusive and
      9 *  store-exclusive for processors that have them disabled (or future ones that
     10 *  might not implement them).
     11 *
     12 *  Syntax of SWP{B} instruction: SWP{B}<c> <Rt>, <Rt2>, [<Rn>]
     13 *  Where: Rt  = destination
     14 *	   Rt2 = source
     15 *	   Rn  = address
     16 */
     17
     18#include <linux/init.h>
     19#include <linux/kernel.h>
     20#include <linux/proc_fs.h>
     21#include <linux/seq_file.h>
     22#include <linux/sched.h>
     23#include <linux/sched/mm.h>
     24#include <linux/syscalls.h>
     25#include <linux/perf_event.h>
     26
     27#include <asm/opcodes.h>
     28#include <asm/system_info.h>
     29#include <asm/traps.h>
     30#include <linux/uaccess.h>
     31
     32/*
     33 * Error-checking SWP macros implemented using ldrex{b}/strex{b}
     34 */
     35#define __user_swpX_asm(data, addr, res, temp, B)		\
     36	__asm__ __volatile__(					\
     37	"0:	ldrex"B"	%2, [%3]\n"			\
     38	"1:	strex"B"	%0, %1, [%3]\n"			\
     39	"	cmp		%0, #0\n"			\
     40	"	moveq		%1, %2\n"			\
     41	"	movne		%0, %4\n"			\
     42	"2:\n"							\
     43	"	.section	 .text.fixup,\"ax\"\n"		\
     44	"	.align		2\n"				\
     45	"3:	mov		%0, %5\n"			\
     46	"	b		2b\n"				\
     47	"	.previous\n"					\
     48	"	.section	 __ex_table,\"a\"\n"		\
     49	"	.align		3\n"				\
     50	"	.long		0b, 3b\n"			\
     51	"	.long		1b, 3b\n"			\
     52	"	.previous"					\
     53	: "=&r" (res), "+r" (data), "=&r" (temp)		\
     54	: "r" (addr), "i" (-EAGAIN), "i" (-EFAULT)		\
     55	: "cc", "memory")
     56
     57#define __user_swp_asm(data, addr, res, temp) \
     58	__user_swpX_asm(data, addr, res, temp, "")
     59#define __user_swpb_asm(data, addr, res, temp) \
     60	__user_swpX_asm(data, addr, res, temp, "b")
     61
     62/*
     63 * Macros/defines for extracting register numbers from instruction.
     64 */
     65#define EXTRACT_REG_NUM(instruction, offset) \
     66	(((instruction) & (0xf << (offset))) >> (offset))
     67#define RN_OFFSET  16
     68#define RT_OFFSET  12
     69#define RT2_OFFSET  0
     70/*
     71 * Bit 22 of the instruction encoding distinguishes between
     72 * the SWP and SWPB variants (bit set means SWPB).
     73 */
     74#define TYPE_SWPB (1 << 22)
     75
     76static unsigned long swpcounter;
     77static unsigned long swpbcounter;
     78static unsigned long abtcounter;
     79static pid_t         previous_pid;
     80
     81#ifdef CONFIG_PROC_FS
     82static int proc_status_show(struct seq_file *m, void *v)
     83{
     84	seq_printf(m, "Emulated SWP:\t\t%lu\n", swpcounter);
     85	seq_printf(m, "Emulated SWPB:\t\t%lu\n", swpbcounter);
     86	seq_printf(m, "Aborted SWP{B}:\t\t%lu\n", abtcounter);
     87	if (previous_pid != 0)
     88		seq_printf(m, "Last process:\t\t%d\n", previous_pid);
     89	return 0;
     90}
     91#endif
     92
     93/*
     94 * Set up process info to signal segmentation fault - called on access error.
     95 */
     96static void set_segfault(struct pt_regs *regs, unsigned long addr)
     97{
     98	int si_code;
     99
    100	mmap_read_lock(current->mm);
    101	if (find_vma(current->mm, addr) == NULL)
    102		si_code = SEGV_MAPERR;
    103	else
    104		si_code = SEGV_ACCERR;
    105	mmap_read_unlock(current->mm);
    106
    107	pr_debug("SWP{B} emulation: access caused memory abort!\n");
    108	arm_notify_die("Illegal memory access", regs,
    109		       SIGSEGV, si_code,
    110		       (void __user *)instruction_pointer(regs),
    111		       0, 0);
    112
    113	abtcounter++;
    114}
    115
    116static int emulate_swpX(unsigned int address, unsigned int *data,
    117			unsigned int type)
    118{
    119	unsigned int res = 0;
    120
    121	if ((type != TYPE_SWPB) && (address & 0x3)) {
    122		/* SWP to unaligned address not permitted */
    123		pr_debug("SWP instruction on unaligned pointer!\n");
    124		return -EFAULT;
    125	}
    126
    127	while (1) {
    128		unsigned long temp;
    129		unsigned int __ua_flags;
    130
    131		__ua_flags = uaccess_save_and_enable();
    132		if (type == TYPE_SWPB)
    133			__user_swpb_asm(*data, address, res, temp);
    134		else
    135			__user_swp_asm(*data, address, res, temp);
    136		uaccess_restore(__ua_flags);
    137
    138		if (likely(res != -EAGAIN) || signal_pending(current))
    139			break;
    140
    141		cond_resched();
    142	}
    143
    144	if (res == 0) {
    145		if (type == TYPE_SWPB)
    146			swpbcounter++;
    147		else
    148			swpcounter++;
    149	}
    150
    151	return res;
    152}
    153
    154/*
    155 * swp_handler logs the id of calling process, dissects the instruction, sanity
    156 * checks the memory location, calls emulate_swpX for the actual operation and
    157 * deals with fixup/error handling before returning
    158 */
    159static int swp_handler(struct pt_regs *regs, unsigned int instr)
    160{
    161	unsigned int address, destreg, data, type;
    162	unsigned int res = 0;
    163
    164	perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, regs->ARM_pc);
    165
    166	res = arm_check_condition(instr, regs->ARM_cpsr);
    167	switch (res) {
    168	case ARM_OPCODE_CONDTEST_PASS:
    169		break;
    170	case ARM_OPCODE_CONDTEST_FAIL:
    171		/* Condition failed - return to next instruction */
    172		regs->ARM_pc += 4;
    173		return 0;
    174	case ARM_OPCODE_CONDTEST_UNCOND:
    175		/* If unconditional encoding - not a SWP, undef */
    176		return -EFAULT;
    177	default:
    178		return -EINVAL;
    179	}
    180
    181	if (current->pid != previous_pid) {
    182		pr_debug("\"%s\" (%ld) uses deprecated SWP{B} instruction\n",
    183			 current->comm, (unsigned long)current->pid);
    184		previous_pid = current->pid;
    185	}
    186
    187	address = regs->uregs[EXTRACT_REG_NUM(instr, RN_OFFSET)];
    188	data	= regs->uregs[EXTRACT_REG_NUM(instr, RT2_OFFSET)];
    189	destreg = EXTRACT_REG_NUM(instr, RT_OFFSET);
    190
    191	type = instr & TYPE_SWPB;
    192
    193	pr_debug("addr in r%d->0x%08x, dest is r%d, source in r%d->0x%08x)\n",
    194		 EXTRACT_REG_NUM(instr, RN_OFFSET), address,
    195		 destreg, EXTRACT_REG_NUM(instr, RT2_OFFSET), data);
    196
    197	/* Check access in reasonable access range for both SWP and SWPB */
    198	if (!access_ok((void __user *)(address & ~3), 4)) {
    199		pr_debug("SWP{B} emulation: access to %p not allowed!\n",
    200			 (void *)address);
    201		res = -EFAULT;
    202	} else {
    203		res = emulate_swpX(address, &data, type);
    204	}
    205
    206	if (res == 0) {
    207		/*
    208		 * On successful emulation, revert the adjustment to the PC
    209		 * made in kernel/traps.c in order to resume execution at the
    210		 * instruction following the SWP{B}.
    211		 */
    212		regs->ARM_pc += 4;
    213		regs->uregs[destreg] = data;
    214	} else if (res == -EFAULT) {
    215		/*
    216		 * Memory errors do not mean emulation failed.
    217		 * Set up signal info to return SEGV, then return OK
    218		 */
    219		set_segfault(regs, address);
    220	}
    221
    222	return 0;
    223}
    224
    225/*
    226 * Only emulate SWP/SWPB executed in ARM state/User mode.
    227 * The kernel must be SWP free and SWP{B} does not exist in Thumb/ThumbEE.
    228 */
    229static struct undef_hook swp_hook = {
    230	.instr_mask = 0x0fb00ff0,
    231	.instr_val  = 0x01000090,
    232	.cpsr_mask  = MODE_MASK | PSR_T_BIT | PSR_J_BIT,
    233	.cpsr_val   = USR_MODE,
    234	.fn	    = swp_handler
    235};
    236
    237/*
    238 * Register handler and create status file in /proc/cpu
    239 * Invoked as late_initcall, since not needed before init spawned.
    240 */
    241static int __init swp_emulation_init(void)
    242{
    243	if (cpu_architecture() < CPU_ARCH_ARMv7)
    244		return 0;
    245
    246#ifdef CONFIG_PROC_FS
    247	if (!proc_create_single("cpu/swp_emulation", S_IRUGO, NULL,
    248			proc_status_show))
    249		return -ENOMEM;
    250#endif /* CONFIG_PROC_FS */
    251
    252	pr_notice("Registering SWP/SWPB emulation handler\n");
    253	register_undef_hook(&swp_hook);
    254
    255	return 0;
    256}
    257
    258late_initcall(swp_emulation_init);