1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (C) 2012 Rabin Vincent <rabin at rab.in>
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>
14 #include <asm/opcodes.h>
15 #include <asm/traps.h>
17 #include "../decode.h"
18 #include "../decode-arm.h"
21 #define UPROBE_TRAP_NR UINT_MAX
23 bool is_swbp_insn(uprobe_opcode_t *insn)
25 return (__mem_to_opcode_arm(*insn) & 0x0fffffff) ==
26 (UPROBE_SWBP_ARM_INSN & 0x0fffffff);
29 int set_swbp(struct arch_uprobe *auprobe, struct mm_struct *mm,
32 return uprobe_write_opcode(auprobe, mm, vaddr,
33 __opcode_to_mem_arm(auprobe->bpinsn));
36 bool arch_uprobe_ignore(struct arch_uprobe *auprobe, struct pt_regs *regs)
38 if (!auprobe->asi.insn_check_cc(regs->ARM_cpsr)) {
46 bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs)
48 probes_opcode_t opcode;
50 if (!auprobe->simulate)
53 opcode = __mem_to_opcode_arm(*(unsigned int *) auprobe->insn);
55 auprobe->asi.insn_singlestep(opcode, &auprobe->asi, regs);
61 arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr,
64 unsigned long orig_ret_vaddr;
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;
72 int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm,
79 /* Thumb not yet support */
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);
87 ret = arm_probes_decode_insn(insn, &auprobe->asi, false,
88 uprobes_probes_actions, NULL);
93 case INSN_GOOD_NO_SLOT:
94 auprobe->simulate = true;
102 bpinsn = UPROBE_SWBP_ARM_INSN & 0x0fffffff;
103 if (insn >= 0xe0000000)
104 bpinsn |= 0xe0000000; /* Unconditional instruction */
106 bpinsn |= insn & 0xf0000000; /* Copy condition from insn */
108 auprobe->bpinsn = bpinsn;
113 void arch_uprobe_copy_ixol(struct page *page, unsigned long vaddr,
114 void *src, unsigned long len)
116 void *xol_page_kaddr = kmap_atomic(page);
117 void *dst = xol_page_kaddr + (vaddr & ~PAGE_MASK);
121 /* Initialize the slot */
122 memcpy(dst, src, len);
124 /* flush caches (dcache/icache) */
125 flush_uprobe_xol_access(page, vaddr, dst, len);
129 kunmap_atomic(xol_page_kaddr);
133 int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
135 struct uprobe_task *utask = current->utask;
137 if (auprobe->prehandler)
138 auprobe->prehandler(auprobe, &utask->autask, regs);
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;
147 int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
149 struct uprobe_task *utask = current->utask;
151 WARN_ON_ONCE(current->thread.trap_no != UPROBE_TRAP_NR);
153 current->thread.trap_no = utask->autask.saved_trap_no;
154 regs->ARM_pc = utask->vaddr + 4;
156 if (auprobe->posthandler)
157 auprobe->posthandler(auprobe, &utask->autask, regs);
162 bool arch_uprobe_xol_was_trapped(struct task_struct *t)
164 if (t->thread.trap_no != UPROBE_TRAP_NR)
170 void arch_uprobe_abort_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
172 struct uprobe_task *utask = current->utask;
174 current->thread.trap_no = utask->autask.saved_trap_no;
175 instruction_pointer_set(regs, utask->vaddr);
178 int arch_uprobe_exception_notify(struct notifier_block *self,
179 unsigned long val, void *data)
184 static int uprobe_trap_handler(struct pt_regs *regs, unsigned int instr)
188 local_irq_save(flags);
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);
199 unsigned long uprobe_get_swbp_addr(struct pt_regs *regs)
201 return instruction_pointer(regs);
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,
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,
220 static int arch_uprobes_init(void)
222 register_undef_hook(&uprobes_arm_break_hook);
223 register_undef_hook(&uprobes_arm_ss_hook);
227 device_initcall(arch_uprobes_init);