1 // SPDX-License-Identifier: GPL-2.0-only
2 #include <linux/highmem.h>
3 #include <linux/ptrace.h>
4 #include <linux/sched.h>
5 #include <linux/uprobes.h>
6 #include <asm/cacheflush.h>
7
8 #define UPROBE_TRAP_NR UINT_MAX
9
arch_uprobe_analyze_insn(struct arch_uprobe * auprobe,struct mm_struct * mm,unsigned long addr)10 int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe,
11 struct mm_struct *mm, unsigned long addr)
12 {
13 int idx;
14 union loongarch_instruction insn;
15
16 if (addr & 0x3)
17 return -EILSEQ;
18
19 for (idx = ARRAY_SIZE(auprobe->insn) - 1; idx >= 0; idx--) {
20 insn.word = auprobe->insn[idx];
21 if (insns_not_supported(insn))
22 return -EINVAL;
23 }
24
25 if (insns_need_simulation(insn)) {
26 auprobe->ixol[0] = larch_insn_gen_nop();
27 auprobe->simulate = true;
28 } else {
29 auprobe->ixol[0] = auprobe->insn[0];
30 auprobe->simulate = false;
31 }
32
33 auprobe->ixol[1] = UPROBE_XOLBP_INSN;
34
35 return 0;
36 }
37
arch_uprobe_pre_xol(struct arch_uprobe * auprobe,struct pt_regs * regs)38 int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
39 {
40 struct uprobe_task *utask = current->utask;
41
42 utask->autask.saved_trap_nr = current->thread.trap_nr;
43 current->thread.trap_nr = UPROBE_TRAP_NR;
44 instruction_pointer_set(regs, utask->xol_vaddr);
45
46 return 0;
47 }
48
arch_uprobe_post_xol(struct arch_uprobe * auprobe,struct pt_regs * regs)49 int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
50 {
51 struct uprobe_task *utask = current->utask;
52
53 WARN_ON_ONCE(current->thread.trap_nr != UPROBE_TRAP_NR);
54 current->thread.trap_nr = utask->autask.saved_trap_nr;
55 instruction_pointer_set(regs, utask->vaddr + LOONGARCH_INSN_SIZE);
56
57 return 0;
58 }
59
arch_uprobe_abort_xol(struct arch_uprobe * auprobe,struct pt_regs * regs)60 void arch_uprobe_abort_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
61 {
62 struct uprobe_task *utask = current->utask;
63
64 current->thread.trap_nr = utask->autask.saved_trap_nr;
65 instruction_pointer_set(regs, utask->vaddr);
66 }
67
arch_uprobe_xol_was_trapped(struct task_struct * t)68 bool arch_uprobe_xol_was_trapped(struct task_struct *t)
69 {
70 if (t->thread.trap_nr != UPROBE_TRAP_NR)
71 return true;
72
73 return false;
74 }
75
arch_uprobe_skip_sstep(struct arch_uprobe * auprobe,struct pt_regs * regs)76 bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs)
77 {
78 union loongarch_instruction insn;
79
80 if (!auprobe->simulate)
81 return false;
82
83 insn.word = auprobe->insn[0];
84 arch_simulate_insn(insn, regs);
85
86 return true;
87 }
88
arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr,struct pt_regs * regs)89 unsigned long arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr,
90 struct pt_regs *regs)
91 {
92 unsigned long ra = regs->regs[1];
93
94 regs->regs[1] = trampoline_vaddr;
95
96 return ra;
97 }
98
arch_uretprobe_is_alive(struct return_instance * ret,enum rp_check ctx,struct pt_regs * regs)99 bool arch_uretprobe_is_alive(struct return_instance *ret,
100 enum rp_check ctx, struct pt_regs *regs)
101 {
102 if (ctx == RP_CHECK_CHAIN_CALL)
103 return regs->regs[3] <= ret->stack;
104 else
105 return regs->regs[3] < ret->stack;
106 }
107
arch_uprobe_exception_notify(struct notifier_block * self,unsigned long val,void * data)108 int arch_uprobe_exception_notify(struct notifier_block *self,
109 unsigned long val, void *data)
110 {
111 return NOTIFY_DONE;
112 }
113
uprobe_breakpoint_handler(struct pt_regs * regs)114 bool uprobe_breakpoint_handler(struct pt_regs *regs)
115 {
116 if (uprobe_pre_sstep_notifier(regs))
117 return true;
118
119 return false;
120 }
121
uprobe_singlestep_handler(struct pt_regs * regs)122 bool uprobe_singlestep_handler(struct pt_regs *regs)
123 {
124 if (uprobe_post_sstep_notifier(regs))
125 return true;
126
127 return false;
128 }
129
uprobe_get_swbp_addr(struct pt_regs * regs)130 unsigned long uprobe_get_swbp_addr(struct pt_regs *regs)
131 {
132 return instruction_pointer(regs);
133 }
134
arch_uprobe_copy_ixol(struct page * page,unsigned long vaddr,void * src,unsigned long len)135 void arch_uprobe_copy_ixol(struct page *page, unsigned long vaddr,
136 void *src, unsigned long len)
137 {
138 void *kaddr = kmap_local_page(page);
139 void *dst = kaddr + (vaddr & ~PAGE_MASK);
140
141 memcpy(dst, src, len);
142 flush_icache_range((unsigned long)dst, (unsigned long)dst + len);
143 kunmap_local(kaddr);
144 }
145