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); 228