File Index
Files and directories
Sign in
FILE

core.c

File preview with syntax highlighting.

linux-6.19.14/arch/arm/probes/uprobes/core.c C
  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 vm_area_struct *vma,
 30	     unsigned long vaddr)
 31{
 32	return uprobe_write_opcode(auprobe, vma, vaddr,
 33		   __opcode_to_mem_arm(auprobe->bpinsn), true);
 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);
~/ Read-only file preview.