1 // SPDX-License-Identifier: GPL-2.0-or-later 2 3 #include <linux/regset.h> 4 #include <linux/hw_breakpoint.h> 5 6 #include <asm/debug.h> 7 8 #include "ptrace-decl.h" 9 10 void user_enable_single_step(struct task_struct *task) 11 { 12 struct pt_regs *regs = task->thread.regs; 13 14 if (regs != NULL) 15 regs_set_return_msr(regs, (regs->msr & ~MSR_BE) | MSR_SE); 16 set_tsk_thread_flag(task, TIF_SINGLESTEP); 17 } 18 19 void user_enable_block_step(struct task_struct *task) 20 { 21 struct pt_regs *regs = task->thread.regs; 22 23 if (regs != NULL) 24 regs_set_return_msr(regs, (regs->msr & ~MSR_SE) | MSR_BE); 25 set_tsk_thread_flag(task, TIF_SINGLESTEP); 26 } 27 28 void user_disable_single_step(struct task_struct *task) 29 { 30 struct pt_regs *regs = task->thread.regs; 31 32 if (regs != NULL) 33 regs_set_return_msr(regs, regs->msr & ~(MSR_SE | MSR_BE)); 34 35 clear_tsk_thread_flag(task, TIF_SINGLESTEP); 36 } 37 38 void ppc_gethwdinfo(struct ppc_debug_info *dbginfo) 39 { 40 dbginfo->version = 1; 41 dbginfo->num_instruction_bps = 0; 42 if (ppc_breakpoint_available()) 43 dbginfo->num_data_bps = nr_wp_slots(); 44 else 45 dbginfo->num_data_bps = 0; 46 dbginfo->num_condition_regs = 0; 47 dbginfo->data_bp_alignment = sizeof(long); 48 dbginfo->sizeof_condition = 0; 49 if (IS_ENABLED(CONFIG_HAVE_HW_BREAKPOINT)) { 50 dbginfo->features = PPC_DEBUG_FEATURE_DATA_BP_RANGE; 51 if (dawr_enabled()) 52 dbginfo->features |= PPC_DEBUG_FEATURE_DATA_BP_DAWR; 53 } else { 54 dbginfo->features = 0; 55 } 56 if (cpu_has_feature(CPU_FTR_ARCH_31)) 57 dbginfo->features |= PPC_DEBUG_FEATURE_DATA_BP_ARCH_31; 58 } 59 60 int ptrace_get_debugreg(struct task_struct *child, unsigned long addr, 61 unsigned long __user *datalp) 62 { 63 unsigned long dabr_fake; 64 65 /* We only support one DABR and no IABRS at the moment */ 66 if (addr > 0) 67 return -EINVAL; 68 dabr_fake = ((child->thread.hw_brk[0].address & (~HW_BRK_TYPE_DABR)) | 69 (child->thread.hw_brk[0].type & HW_BRK_TYPE_DABR)); 70 return put_user(dabr_fake, datalp); 71 } 72 73 /* 74 * ptrace_set_debugreg() fakes DABR and DABR is only one. So even if 75 * internal hw supports more than one watchpoint, we support only one 76 * watchpoint with this interface. 77 */ 78 int ptrace_set_debugreg(struct task_struct *task, unsigned long addr, unsigned long data) 79 { 80 #ifdef CONFIG_HAVE_HW_BREAKPOINT 81 int ret; 82 struct thread_struct *thread = &task->thread; 83 struct perf_event *bp; 84 struct perf_event_attr attr; 85 #endif /* CONFIG_HAVE_HW_BREAKPOINT */ 86 bool set_bp = true; 87 struct arch_hw_breakpoint hw_brk; 88 89 /* For ppc64 we support one DABR and no IABR's at the moment (ppc64). 90 * For embedded processors we support one DAC and no IAC's at the 91 * moment. 92 */ 93 if (addr > 0) 94 return -EINVAL; 95 96 /* The bottom 3 bits in dabr are flags */ 97 if ((data & ~0x7UL) >= TASK_SIZE) 98 return -EIO; 99 100 /* For processors using DABR (i.e. 970), the bottom 3 bits are flags. 101 * It was assumed, on previous implementations, that 3 bits were 102 * passed together with the data address, fitting the design of the 103 * DABR register, as follows: 104 * 105 * bit 0: Read flag 106 * bit 1: Write flag 107 * bit 2: Breakpoint translation 108 * 109 * Thus, we use them here as so. 110 */ 111 112 /* Ensure breakpoint translation bit is set */ 113 if (data && !(data & HW_BRK_TYPE_TRANSLATE)) 114 return -EIO; 115 hw_brk.address = data & (~HW_BRK_TYPE_DABR); 116 hw_brk.type = (data & HW_BRK_TYPE_DABR) | HW_BRK_TYPE_PRIV_ALL; 117 hw_brk.len = DABR_MAX_LEN; 118 hw_brk.hw_len = DABR_MAX_LEN; 119 set_bp = (data) && (hw_brk.type & HW_BRK_TYPE_RDWR); 120 #ifdef CONFIG_HAVE_HW_BREAKPOINT 121 bp = thread->ptrace_bps[0]; 122 if (!set_bp) { 123 if (bp) { 124 unregister_hw_breakpoint(bp); 125 thread->ptrace_bps[0] = NULL; 126 } 127 return 0; 128 } 129 if (bp) { 130 attr = bp->attr; 131 attr.bp_addr = hw_brk.address; 132 attr.bp_len = DABR_MAX_LEN; 133 arch_bp_generic_fields(hw_brk.type, &attr.bp_type); 134 135 /* Enable breakpoint */ 136 attr.disabled = false; 137 138 ret = modify_user_hw_breakpoint(bp, &attr); 139 if (ret) 140 return ret; 141 142 thread->ptrace_bps[0] = bp; 143 thread->hw_brk[0] = hw_brk; 144 return 0; 145 } 146 147 /* Create a new breakpoint request if one doesn't exist already */ 148 hw_breakpoint_init(&attr); 149 attr.bp_addr = hw_brk.address; 150 attr.bp_len = DABR_MAX_LEN; 151 arch_bp_generic_fields(hw_brk.type, 152 &attr.bp_type); 153 154 thread->ptrace_bps[0] = bp = register_user_hw_breakpoint(&attr, 155 ptrace_triggered, NULL, task); 156 if (IS_ERR(bp)) { 157 thread->ptrace_bps[0] = NULL; 158 return PTR_ERR(bp); 159 } 160 161 #else /* !CONFIG_HAVE_HW_BREAKPOINT */ 162 if (set_bp && (!ppc_breakpoint_available())) 163 return -ENODEV; 164 #endif /* CONFIG_HAVE_HW_BREAKPOINT */ 165 task->thread.hw_brk[0] = hw_brk; 166 return 0; 167 } 168 169 #ifdef CONFIG_HAVE_HW_BREAKPOINT 170 static int find_empty_ptrace_bp(struct thread_struct *thread) 171 { 172 int i; 173 174 for (i = 0; i < nr_wp_slots(); i++) { 175 if (!thread->ptrace_bps[i]) 176 return i; 177 } 178 return -1; 179 } 180 #endif 181 182 static int find_empty_hw_brk(struct thread_struct *thread) 183 { 184 int i; 185 186 for (i = 0; i < nr_wp_slots(); i++) { 187 if (!thread->hw_brk[i].address) 188 return i; 189 } 190 return -1; 191 } 192 193 long ppc_set_hwdebug(struct task_struct *child, struct ppc_hw_breakpoint *bp_info) 194 { 195 int i; 196 #ifdef CONFIG_HAVE_HW_BREAKPOINT 197 int len = 0; 198 struct thread_struct *thread = &child->thread; 199 struct perf_event *bp; 200 struct perf_event_attr attr; 201 #endif /* CONFIG_HAVE_HW_BREAKPOINT */ 202 struct arch_hw_breakpoint brk; 203 204 if (bp_info->version != 1) 205 return -ENOTSUPP; 206 /* 207 * We only support one data breakpoint 208 */ 209 if ((bp_info->trigger_type & PPC_BREAKPOINT_TRIGGER_RW) == 0 || 210 (bp_info->trigger_type & ~PPC_BREAKPOINT_TRIGGER_RW) != 0 || 211 bp_info->condition_mode != PPC_BREAKPOINT_CONDITION_NONE) 212 return -EINVAL; 213 214 if ((unsigned long)bp_info->addr >= TASK_SIZE) 215 return -EIO; 216 217 brk.address = ALIGN_DOWN(bp_info->addr, HW_BREAKPOINT_SIZE); 218 brk.type = HW_BRK_TYPE_TRANSLATE | HW_BRK_TYPE_PRIV_ALL; 219 brk.len = DABR_MAX_LEN; 220 brk.hw_len = DABR_MAX_LEN; 221 if (bp_info->trigger_type & PPC_BREAKPOINT_TRIGGER_READ) 222 brk.type |= HW_BRK_TYPE_READ; 223 if (bp_info->trigger_type & PPC_BREAKPOINT_TRIGGER_WRITE) 224 brk.type |= HW_BRK_TYPE_WRITE; 225 #ifdef CONFIG_HAVE_HW_BREAKPOINT 226 if (bp_info->addr_mode == PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE) 227 len = bp_info->addr2 - bp_info->addr; 228 else if (bp_info->addr_mode == PPC_BREAKPOINT_MODE_EXACT) 229 len = 1; 230 else 231 return -EINVAL; 232 233 i = find_empty_ptrace_bp(thread); 234 if (i < 0) 235 return -ENOSPC; 236 237 /* Create a new breakpoint request if one doesn't exist already */ 238 hw_breakpoint_init(&attr); 239 attr.bp_addr = (unsigned long)bp_info->addr; 240 attr.bp_len = len; 241 arch_bp_generic_fields(brk.type, &attr.bp_type); 242 243 bp = register_user_hw_breakpoint(&attr, ptrace_triggered, NULL, child); 244 thread->ptrace_bps[i] = bp; 245 if (IS_ERR(bp)) { 246 thread->ptrace_bps[i] = NULL; 247 return PTR_ERR(bp); 248 } 249 250 return i + 1; 251 #endif /* CONFIG_HAVE_HW_BREAKPOINT */ 252 253 if (bp_info->addr_mode != PPC_BREAKPOINT_MODE_EXACT) 254 return -EINVAL; 255 256 i = find_empty_hw_brk(&child->thread); 257 if (i < 0) 258 return -ENOSPC; 259 260 if (!ppc_breakpoint_available()) 261 return -ENODEV; 262 263 child->thread.hw_brk[i] = brk; 264 265 return i + 1; 266 } 267 268 long ppc_del_hwdebug(struct task_struct *child, long data) 269 { 270 #ifdef CONFIG_HAVE_HW_BREAKPOINT 271 int ret = 0; 272 struct thread_struct *thread = &child->thread; 273 struct perf_event *bp; 274 #endif /* CONFIG_HAVE_HW_BREAKPOINT */ 275 if (data < 1 || data > nr_wp_slots()) 276 return -EINVAL; 277 278 #ifdef CONFIG_HAVE_HW_BREAKPOINT 279 bp = thread->ptrace_bps[data - 1]; 280 if (bp) { 281 unregister_hw_breakpoint(bp); 282 thread->ptrace_bps[data - 1] = NULL; 283 } else { 284 ret = -ENOENT; 285 } 286 return ret; 287 #else /* CONFIG_HAVE_HW_BREAKPOINT */ 288 if (!(child->thread.hw_brk[data - 1].flags & HW_BRK_FLAG_DISABLED) && 289 child->thread.hw_brk[data - 1].address == 0) 290 return -ENOENT; 291 292 child->thread.hw_brk[data - 1].address = 0; 293 child->thread.hw_brk[data - 1].type = 0; 294 child->thread.hw_brk[data - 1].flags = 0; 295 #endif /* CONFIG_HAVE_HW_BREAKPOINT */ 296 297 return 0; 298 } 299
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.