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 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 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 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 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 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 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 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 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 108 int arch_uprobe_exception_notify(struct notifier_block *self, 109 unsigned long val, void *data) 110 { 111 return NOTIFY_DONE; 112 } 113 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 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 130 unsigned long uprobe_get_swbp_addr(struct pt_regs *regs) 131 { 132 return instruction_pointer(regs); 133 } 134 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