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