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 user_enable_single_step(current); 46 47 return 0; 48 } 49 50 int 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 67 void 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 76 bool 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 84 bool 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 98 unsigned 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 108 bool 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 117 int arch_uprobe_exception_notify(struct notifier_block *self, 118 unsigned long val, void *data) 119 { 120 return NOTIFY_DONE; 121 } 122 123 bool uprobe_breakpoint_handler(struct pt_regs *regs) 124 { 125 if (uprobe_pre_sstep_notifier(regs)) 126 return true; 127 128 return false; 129 } 130 131 bool uprobe_singlestep_handler(struct pt_regs *regs) 132 { 133 if (uprobe_post_sstep_notifier(regs)) 134 return true; 135 136 return false; 137 } 138 139 unsigned long uprobe_get_swbp_addr(struct pt_regs *regs) 140 { 141 return instruction_pointer(regs); 142 } 143 144 void 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
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.