root/arch/arm/probes/uprobes/core.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. is_swbp_insn
  2. set_swbp
  3. arch_uprobe_ignore
  4. arch_uprobe_skip_sstep
  5. arch_uretprobe_hijack_return_addr
  6. arch_uprobe_analyze_insn
  7. arch_uprobe_copy_ixol
  8. arch_uprobe_pre_xol
  9. arch_uprobe_post_xol
  10. arch_uprobe_xol_was_trapped
  11. arch_uprobe_abort_xol
  12. arch_uprobe_exception_notify
  13. uprobe_trap_handler
  14. uprobe_get_swbp_addr
  15. arch_uprobes_init

   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 
  23 bool is_swbp_insn(uprobe_opcode_t *insn)
  24 {
  25         return (__mem_to_opcode_arm(*insn) & 0x0fffffff) ==
  26                 (UPROBE_SWBP_ARM_INSN & 0x0fffffff);
  27 }
  28 
  29 int 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 
  36 bool 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 
  46 bool 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 
  60 unsigned long
  61 arch_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 
  72 int 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 
 113 void 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 
 133 int 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 
 147 int 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 
 162 bool 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 
 170 void 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 
 178 int arch_uprobe_exception_notify(struct notifier_block *self,
 179                                  unsigned long val, void *data)
 180 {
 181         return NOTIFY_DONE;
 182 }
 183 
 184 static 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 
 199 unsigned long uprobe_get_swbp_addr(struct pt_regs *regs)
 200 {
 201         return instruction_pointer(regs);
 202 }
 203 
 204 static struct undef_hook uprobes_arm_break_hook = {
 205         .instr_mask     = 0x0fffffff,
 206         .instr_val      = (UPROBE_SWBP_ARM_INSN & 0x0fffffff),
 207         .cpsr_mask      = MODE_MASK,
 208         .cpsr_val       = USR_MODE,
 209         .fn             = uprobe_trap_handler,
 210 };
 211 
 212 static struct undef_hook uprobes_arm_ss_hook = {
 213         .instr_mask     = 0x0fffffff,
 214         .instr_val      = (UPROBE_SS_ARM_INSN & 0x0fffffff),
 215         .cpsr_mask      = MODE_MASK,
 216         .cpsr_val       = USR_MODE,
 217         .fn             = uprobe_trap_handler,
 218 };
 219 
 220 static 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 }
 227 device_initcall(arch_uprobes_init);

/* [<][>][^][v][top][bottom][index][help] */