~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/arch/arm/probes/uprobes/core.c

Version: ~ [ linux-6.11-rc3 ] ~ [ linux-6.10.4 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.45 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.104 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.164 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.223 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.281 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.319 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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 
 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 
 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 
 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 
 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
 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 
 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 
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 
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 
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 
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 
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 
178 int arch_uprobe_exception_notify(struct notifier_block *self,
179                                  unsigned long val, void *data)
180 {
181         return NOTIFY_DONE;
182 }
183 
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 
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 
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 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php