~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/arch/powerpc/kernel/ptrace/ptrace-noadv.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php