Linux Audio

Check our new training course

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/*
  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);