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 10int 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 38int 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 user_enable_single_step(current); 46 47 return 0; 48} 49 50int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) 51{ 52 struct uprobe_task *utask = current->utask; 53 54 WARN_ON_ONCE(current->thread.trap_nr != UPROBE_TRAP_NR); 55 current->thread.trap_nr = utask->autask.saved_trap_nr; 56 57 if (auprobe->simulate) 58 instruction_pointer_set(regs, auprobe->resume_era); 59 else 60 instruction_pointer_set(regs, utask->vaddr + LOONGARCH_INSN_SIZE); 61 62 user_disable_single_step(current); 63 64 return 0; 65} 66 67void arch_uprobe_abort_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) 68{ 69 struct uprobe_task *utask = current->utask; 70 71 current->thread.trap_nr = utask->autask.saved_trap_nr; 72 instruction_pointer_set(regs, utask->vaddr); 73 user_disable_single_step(current); 74} 75 76bool arch_uprobe_xol_was_trapped(struct task_struct *t) 77{ 78 if (t->thread.trap_nr != UPROBE_TRAP_NR) 79 return true; 80 81 return false; 82} 83 84bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs) 85{ 86 union loongarch_instruction insn; 87 88 if (!auprobe->simulate) 89 return false; 90 91 insn.word = auprobe->insn[0]; 92 arch_simulate_insn(insn, regs); 93 auprobe->resume_era = regs->csr_era; 94 95 return true; 96} 97 98unsigned long arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr, 99 struct pt_regs *regs) 100{ 101 unsigned long ra = regs->regs[1]; 102 103 regs->regs[1] = trampoline_vaddr; 104 105 return ra; 106} 107 108bool arch_uretprobe_is_alive(struct return_instance *ret, 109 enum rp_check ctx, struct pt_regs *regs) 110{ 111 if (ctx == RP_CHECK_CHAIN_CALL) 112 return regs->regs[3] <= ret->stack; 113 else 114 return regs->regs[3] < ret->stack; 115} 116 117int arch_uprobe_exception_notify(struct notifier_block *self, 118 unsigned long val, void *data) 119{ 120 return NOTIFY_DONE; 121} 122 123bool uprobe_breakpoint_handler(struct pt_regs *regs) 124{ 125 if (uprobe_pre_sstep_notifier(regs)) 126 return true; 127 128 return false; 129} 130 131bool uprobe_singlestep_handler(struct pt_regs *regs) 132{ 133 if (uprobe_post_sstep_notifier(regs)) 134 return true; 135 136 return false; 137} 138 139unsigned long uprobe_get_swbp_addr(struct pt_regs *regs) 140{ 141 return instruction_pointer(regs); 142} 143 144void arch_uprobe_copy_ixol(struct page *page, unsigned long vaddr, 145 void *src, unsigned long len) 146{ 147 void *kaddr = kmap_local_page(page); 148 void *dst = kaddr + (vaddr & ~PAGE_MASK); 149 150 memcpy(dst, src, len); 151 flush_icache_range((unsigned long)dst, (unsigned long)dst + len); 152 kunmap_local(kaddr); 153} 154