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

core.c (5148B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Copyright (C) 2012 Rabin Vincent <rabin at rab.in>
      4 */
      5
      6#include <linux/kernel.h>
      7#include <linux/stddef.h>
      8#include <linux/errno.h>
      9#include <linux/highmem.h>
     10#include <linux/sched.h>
     11#include <linux/uprobes.h>
     12#include <linux/notifier.h>
     13
     14#include <asm/opcodes.h>
     15#include <asm/traps.h>
     16
     17#include "../decode.h"
     18#include "../decode-arm.h"
     19#include "core.h"
     20
     21#define UPROBE_TRAP_NR	UINT_MAX
     22
     23bool is_swbp_insn(uprobe_opcode_t *insn)
     24{
     25	return (__mem_to_opcode_arm(*insn) & 0x0fffffff) ==
     26		(UPROBE_SWBP_ARM_INSN & 0x0fffffff);
     27}
     28
     29int set_swbp(struct arch_uprobe *auprobe, struct mm_struct *mm,
     30	     unsigned long vaddr)
     31{
     32	return uprobe_write_opcode(auprobe, mm, vaddr,
     33		   __opcode_to_mem_arm(auprobe->bpinsn));
     34}
     35
     36bool arch_uprobe_ignore(struct arch_uprobe *auprobe, struct pt_regs *regs)
     37{
     38	if (!auprobe->asi.insn_check_cc(regs->ARM_cpsr)) {
     39		regs->ARM_pc += 4;
     40		return true;
     41	}
     42
     43	return false;
     44}
     45
     46bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs)
     47{
     48	probes_opcode_t opcode;
     49
     50	if (!auprobe->simulate)
     51		return false;
     52
     53	opcode = __mem_to_opcode_arm(*(unsigned int *) auprobe->insn);
     54
     55	auprobe->asi.insn_singlestep(opcode, &auprobe->asi, regs);
     56
     57	return true;
     58}
     59
     60unsigned long
     61arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr,
     62				  struct pt_regs *regs)
     63{
     64	unsigned long orig_ret_vaddr;
     65
     66	orig_ret_vaddr = regs->ARM_lr;
     67	/* Replace the return addr with trampoline addr */
     68	regs->ARM_lr = trampoline_vaddr;
     69	return orig_ret_vaddr;
     70}
     71
     72int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm,
     73			     unsigned long addr)
     74{
     75	unsigned int insn;
     76	unsigned int bpinsn;
     77	enum probes_insn ret;
     78
     79	/* Thumb not yet support */
     80	if (addr & 0x3)
     81		return -EINVAL;
     82
     83	insn = __mem_to_opcode_arm(*(unsigned int *)auprobe->insn);
     84	auprobe->ixol[0] = __opcode_to_mem_arm(insn);
     85	auprobe->ixol[1] = __opcode_to_mem_arm(UPROBE_SS_ARM_INSN);
     86
     87	ret = arm_probes_decode_insn(insn, &auprobe->asi, false,
     88				     uprobes_probes_actions, NULL);
     89	switch (ret) {
     90	case INSN_REJECTED:
     91		return -EINVAL;
     92
     93	case INSN_GOOD_NO_SLOT:
     94		auprobe->simulate = true;
     95		break;
     96
     97	case INSN_GOOD:
     98	default:
     99		break;
    100	}
    101
    102	bpinsn = UPROBE_SWBP_ARM_INSN & 0x0fffffff;
    103	if (insn >= 0xe0000000)
    104		bpinsn |= 0xe0000000;  /* Unconditional instruction */
    105	else
    106		bpinsn |= insn & 0xf0000000;  /* Copy condition from insn */
    107
    108	auprobe->bpinsn = bpinsn;
    109
    110	return 0;
    111}
    112
    113void arch_uprobe_copy_ixol(struct page *page, unsigned long vaddr,
    114			   void *src, unsigned long len)
    115{
    116	void *xol_page_kaddr = kmap_atomic(page);
    117	void *dst = xol_page_kaddr + (vaddr & ~PAGE_MASK);
    118
    119	preempt_disable();
    120
    121	/* Initialize the slot */
    122	memcpy(dst, src, len);
    123
    124	/* flush caches (dcache/icache) */
    125	flush_uprobe_xol_access(page, vaddr, dst, len);
    126
    127	preempt_enable();
    128
    129	kunmap_atomic(xol_page_kaddr);
    130}
    131
    132
    133int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
    134{
    135	struct uprobe_task *utask = current->utask;
    136
    137	if (auprobe->prehandler)
    138		auprobe->prehandler(auprobe, &utask->autask, regs);
    139
    140	utask->autask.saved_trap_no = current->thread.trap_no;
    141	current->thread.trap_no = UPROBE_TRAP_NR;
    142	regs->ARM_pc = utask->xol_vaddr;
    143
    144	return 0;
    145}
    146
    147int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
    148{
    149	struct uprobe_task *utask = current->utask;
    150
    151	WARN_ON_ONCE(current->thread.trap_no != UPROBE_TRAP_NR);
    152
    153	current->thread.trap_no = utask->autask.saved_trap_no;
    154	regs->ARM_pc = utask->vaddr + 4;
    155
    156	if (auprobe->posthandler)
    157		auprobe->posthandler(auprobe, &utask->autask, regs);
    158
    159	return 0;
    160}
    161
    162bool arch_uprobe_xol_was_trapped(struct task_struct *t)
    163{
    164	if (t->thread.trap_no != UPROBE_TRAP_NR)
    165		return true;
    166
    167	return false;
    168}
    169
    170void arch_uprobe_abort_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
    171{
    172	struct uprobe_task *utask = current->utask;
    173
    174	current->thread.trap_no = utask->autask.saved_trap_no;
    175	instruction_pointer_set(regs, utask->vaddr);
    176}
    177
    178int arch_uprobe_exception_notify(struct notifier_block *self,
    179				 unsigned long val, void *data)
    180{
    181	return NOTIFY_DONE;
    182}
    183
    184static int uprobe_trap_handler(struct pt_regs *regs, unsigned int instr)
    185{
    186	unsigned long flags;
    187
    188	local_irq_save(flags);
    189	instr &= 0x0fffffff;
    190	if (instr == (UPROBE_SWBP_ARM_INSN & 0x0fffffff))
    191		uprobe_pre_sstep_notifier(regs);
    192	else if (instr == (UPROBE_SS_ARM_INSN & 0x0fffffff))
    193		uprobe_post_sstep_notifier(regs);
    194	local_irq_restore(flags);
    195
    196	return 0;
    197}
    198
    199unsigned long uprobe_get_swbp_addr(struct pt_regs *regs)
    200{
    201	return instruction_pointer(regs);
    202}
    203
    204static struct undef_hook uprobes_arm_break_hook = {
    205	.instr_mask	= 0x0fffffff,
    206	.instr_val	= (UPROBE_SWBP_ARM_INSN & 0x0fffffff),
    207	.cpsr_mask	= (PSR_T_BIT | MODE_MASK),
    208	.cpsr_val	= USR_MODE,
    209	.fn		= uprobe_trap_handler,
    210};
    211
    212static struct undef_hook uprobes_arm_ss_hook = {
    213	.instr_mask	= 0x0fffffff,
    214	.instr_val	= (UPROBE_SS_ARM_INSN & 0x0fffffff),
    215	.cpsr_mask	= (PSR_T_BIT | MODE_MASK),
    216	.cpsr_val	= USR_MODE,
    217	.fn		= uprobe_trap_handler,
    218};
    219
    220static int arch_uprobes_init(void)
    221{
    222	register_undef_hook(&uprobes_arm_break_hook);
    223	register_undef_hook(&uprobes_arm_ss_hook);
    224
    225	return 0;
    226}
    227device_initcall(arch_uprobes_init);