1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) 2014-2016 Pratyush Anand <panand@redhat.com> 4 */ 5 #include <linux/highmem.h> 6 #include <linux/ptrace.h> 7 #include <linux/uprobes.h> 8 #include <asm/cacheflush.h> 9 10 #include "decode-insn.h" 11 12 #define UPROBE_TRAP_NR UINT_MAX 13 14 bool is_swbp_insn(uprobe_opcode_t *insn) 15 { 16 return (*insn & 0xffff) == UPROBE_SWBP_INSN; 17 } 18 19 unsigned long uprobe_get_swbp_addr(struct pt_regs *regs) 20 { 21 return instruction_pointer(regs); 22 } 23 24 int arch_uprobe_analyze_insn(struct arch_uprobe *auprobe, struct mm_struct *mm, 25 unsigned long addr) 26 { 27 probe_opcode_t insn; 28 29 insn = *(probe_opcode_t *)(&auprobe->insn[0]); 30 31 auprobe->insn_size = is_insn32(insn) ? 4 : 2; 32 33 switch (csky_probe_decode_insn(&insn, &auprobe->api)) { 34 case INSN_REJECTED: 35 return -EINVAL; 36 37 case INSN_GOOD_NO_SLOT: 38 auprobe->simulate = true; 39 break; 40 41 default: 42 break; 43 } 44 45 return 0; 46 } 47 48 int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) 49 { 50 struct uprobe_task *utask = current->utask; 51 52 utask->autask.saved_trap_no = current->thread.trap_no; 53 current->thread.trap_no = UPROBE_TRAP_NR; 54 55 instruction_pointer_set(regs, utask->xol_vaddr); 56 57 user_enable_single_step(current); 58 59 return 0; 60 } 61 62 int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) 63 { 64 struct uprobe_task *utask = current->utask; 65 66 WARN_ON_ONCE(current->thread.trap_no != UPROBE_TRAP_NR); 67 current->thread.trap_no = utask->autask.saved_trap_no; 68 69 instruction_pointer_set(regs, utask->vaddr + auprobe->insn_size); 70 71 user_disable_single_step(current); 72 73 return 0; 74 } 75 76 bool arch_uprobe_xol_was_trapped(struct task_struct *t) 77 { 78 if (t->thread.trap_no != 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 probe_opcode_t insn; 87 unsigned long addr; 88 89 if (!auprobe->simulate) 90 return false; 91 92 insn = *(probe_opcode_t *)(&auprobe->insn[0]); 93 addr = instruction_pointer(regs); 94 95 if (auprobe->api.handler) 96 auprobe->api.handler(insn, addr, regs); 97 98 return true; 99 } 100 101 void arch_uprobe_abort_xol(struct arch_uprobe *auprobe, struct pt_regs *regs) 102 { 103 struct uprobe_task *utask = current->utask; 104 105 current->thread.trap_no = utask->autask.saved_trap_no; 106 107 /* 108 * Task has received a fatal signal, so reset back to probed 109 * address. 110 */ 111 instruction_pointer_set(regs, utask->vaddr); 112 113 user_disable_single_step(current); 114 } 115 116 bool arch_uretprobe_is_alive(struct return_instance *ret, enum rp_check ctx, 117 struct pt_regs *regs) 118 { 119 if (ctx == RP_CHECK_CHAIN_CALL) 120 return regs->usp <= ret->stack; 121 else 122 return regs->usp < ret->stack; 123 } 124 125 unsigned long 126 arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr, 127 struct pt_regs *regs) 128 { 129 unsigned long ra; 130 131 ra = regs->lr; 132 133 regs->lr = trampoline_vaddr; 134 135 return ra; 136 } 137 138 int arch_uprobe_exception_notify(struct notifier_block *self, 139 unsigned long val, void *data) 140 { 141 return NOTIFY_DONE; 142 } 143 144 int uprobe_breakpoint_handler(struct pt_regs *regs) 145 { 146 if (uprobe_pre_sstep_notifier(regs)) 147 return 1; 148 149 return 0; 150 } 151 152 int uprobe_single_step_handler(struct pt_regs *regs) 153 { 154 if (uprobe_post_sstep_notifier(regs)) 155 return 1; 156 157 return 0; 158 } 159
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.