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