xref: /openbmc/linux/arch/arm/probes/uprobes/core.c (revision d2f7eca6)
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 
is_swbp_insn(uprobe_opcode_t * insn)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 
set_swbp(struct arch_uprobe * auprobe,struct mm_struct * mm,unsigned long vaddr)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 
arch_uprobe_ignore(struct arch_uprobe * auprobe,struct pt_regs * regs)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 
arch_uprobe_skip_sstep(struct arch_uprobe * auprobe,struct pt_regs * regs)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
arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr,struct pt_regs * regs)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 
arch_uprobe_analyze_insn(struct arch_uprobe * auprobe,struct mm_struct * mm,unsigned long addr)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 
arch_uprobe_copy_ixol(struct page * page,unsigned long vaddr,void * src,unsigned long len)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 
arch_uprobe_pre_xol(struct arch_uprobe * auprobe,struct pt_regs * regs)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 
arch_uprobe_post_xol(struct arch_uprobe * auprobe,struct pt_regs * regs)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 
arch_uprobe_xol_was_trapped(struct task_struct * t)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 
arch_uprobe_abort_xol(struct arch_uprobe * auprobe,struct pt_regs * regs)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 
arch_uprobe_exception_notify(struct notifier_block * self,unsigned long val,void * data)178 int arch_uprobe_exception_notify(struct notifier_block *self,
179 				 unsigned long val, void *data)
180 {
181 	return NOTIFY_DONE;
182 }
183 
uprobe_trap_handler(struct pt_regs * regs,unsigned int instr)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 
uprobe_get_swbp_addr(struct pt_regs * regs)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	= (PSR_T_BIT | 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	= (PSR_T_BIT | MODE_MASK),
216 	.cpsr_val	= USR_MODE,
217 	.fn		= uprobe_trap_handler,
218 };
219 
arch_uprobes_init(void)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