xref: /openbmc/linux/arch/arm/probes/uprobes/core.c (revision 818b26588994d9d95743fca0a427f08ec6c1c41d)
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