2 * Copyright (C) 2012 Rabin Vincent <rabin at rab.in>
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.
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>
17 #include <asm/opcodes.h>
18 #include <asm/traps.h>
20 #include "../decode.h"
21 #include "../decode-arm.h"
24 #define UPROBE_TRAP_NR UINT_MAX
26 bool is_swbp_insn(uprobe_opcode_t
*insn
)
28 return (__mem_to_opcode_arm(*insn
) & 0x0fffffff) ==
29 (UPROBE_SWBP_ARM_INSN
& 0x0fffffff);
32 int set_swbp(struct arch_uprobe
*auprobe
, struct mm_struct
*mm
,
35 return uprobe_write_opcode(mm
, vaddr
,
36 __opcode_to_mem_arm(auprobe
->bpinsn
));
39 bool arch_uprobe_ignore(struct arch_uprobe
*auprobe
, struct pt_regs
*regs
)
41 if (!auprobe
->asi
.insn_check_cc(regs
->ARM_cpsr
)) {
49 bool arch_uprobe_skip_sstep(struct arch_uprobe
*auprobe
, struct pt_regs
*regs
)
51 probes_opcode_t opcode
;
53 if (!auprobe
->simulate
)
56 opcode
= __mem_to_opcode_arm(*(unsigned int *) auprobe
->insn
);
58 auprobe
->asi
.insn_singlestep(opcode
, &auprobe
->asi
, regs
);
64 arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr
,
67 unsigned long orig_ret_vaddr
;
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
;
75 int arch_uprobe_analyze_insn(struct arch_uprobe
*auprobe
, struct mm_struct
*mm
,
82 /* Thumb not yet support */
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
);
90 ret
= arm_probes_decode_insn(insn
, &auprobe
->asi
, false,
91 uprobes_probes_actions
, NULL
);
96 case INSN_GOOD_NO_SLOT
:
97 auprobe
->simulate
= true;
105 bpinsn
= UPROBE_SWBP_ARM_INSN
& 0x0fffffff;
106 if (insn
>= 0xe0000000)
107 bpinsn
|= 0xe0000000; /* Unconditional instruction */
109 bpinsn
|= insn
& 0xf0000000; /* Copy condition from insn */
111 auprobe
->bpinsn
= bpinsn
;
116 void arch_uprobe_copy_ixol(struct page
*page
, unsigned long vaddr
,
117 void *src
, unsigned long len
)
119 void *xol_page_kaddr
= kmap_atomic(page
);
120 void *dst
= xol_page_kaddr
+ (vaddr
& ~PAGE_MASK
);
124 /* Initialize the slot */
125 memcpy(dst
, src
, len
);
127 /* flush caches (dcache/icache) */
128 flush_uprobe_xol_access(page
, vaddr
, dst
, len
);
132 kunmap_atomic(xol_page_kaddr
);
136 int arch_uprobe_pre_xol(struct arch_uprobe
*auprobe
, struct pt_regs
*regs
)
138 struct uprobe_task
*utask
= current
->utask
;
140 if (auprobe
->prehandler
)
141 auprobe
->prehandler(auprobe
, &utask
->autask
, regs
);
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
;
150 int arch_uprobe_post_xol(struct arch_uprobe
*auprobe
, struct pt_regs
*regs
)
152 struct uprobe_task
*utask
= current
->utask
;
154 WARN_ON_ONCE(current
->thread
.trap_no
!= UPROBE_TRAP_NR
);
156 current
->thread
.trap_no
= utask
->autask
.saved_trap_no
;
157 regs
->ARM_pc
= utask
->vaddr
+ 4;
159 if (auprobe
->posthandler
)
160 auprobe
->posthandler(auprobe
, &utask
->autask
, regs
);
165 bool arch_uprobe_xol_was_trapped(struct task_struct
*t
)
167 if (t
->thread
.trap_no
!= UPROBE_TRAP_NR
)
173 void arch_uprobe_abort_xol(struct arch_uprobe
*auprobe
, struct pt_regs
*regs
)
175 struct uprobe_task
*utask
= current
->utask
;
177 current
->thread
.trap_no
= utask
->autask
.saved_trap_no
;
178 instruction_pointer_set(regs
, utask
->vaddr
);
181 int arch_uprobe_exception_notify(struct notifier_block
*self
,
182 unsigned long val
, void *data
)
187 static int uprobe_trap_handler(struct pt_regs
*regs
, unsigned int instr
)
191 local_irq_save(flags
);
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
);
202 unsigned long uprobe_get_swbp_addr(struct pt_regs
*regs
)
204 return instruction_pointer(regs
);
207 static 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
,
215 static 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
,
223 static int arch_uprobes_init(void)
225 register_undef_hook(&uprobes_arm_break_hook
);
226 register_undef_hook(&uprobes_arm_ss_hook
);
230 device_initcall(arch_uprobes_init
);