linux/arch/arm/probes/uprobes/core.c
<<
>>
Prefs
   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);
 228