1323a780cSChristophe Leroy // SPDX-License-Identifier: GPL-2.0-or-later
2323a780cSChristophe Leroy
3323a780cSChristophe Leroy #include <linux/regset.h>
4323a780cSChristophe Leroy #include <linux/hw_breakpoint.h>
5323a780cSChristophe Leroy
6323a780cSChristophe Leroy #include "ptrace-decl.h"
7323a780cSChristophe Leroy
user_enable_single_step(struct task_struct * task)8323a780cSChristophe Leroy void user_enable_single_step(struct task_struct *task)
9323a780cSChristophe Leroy {
10323a780cSChristophe Leroy struct pt_regs *regs = task->thread.regs;
11323a780cSChristophe Leroy
12323a780cSChristophe Leroy if (regs != NULL) {
13323a780cSChristophe Leroy task->thread.debug.dbcr0 &= ~DBCR0_BT;
14323a780cSChristophe Leroy task->thread.debug.dbcr0 |= DBCR0_IDM | DBCR0_IC;
15*59dc5bfcSNicholas Piggin regs_set_return_msr(regs, regs->msr | MSR_DE);
16323a780cSChristophe Leroy }
17323a780cSChristophe Leroy set_tsk_thread_flag(task, TIF_SINGLESTEP);
18323a780cSChristophe Leroy }
19323a780cSChristophe Leroy
user_enable_block_step(struct task_struct * task)20323a780cSChristophe Leroy void user_enable_block_step(struct task_struct *task)
21323a780cSChristophe Leroy {
22323a780cSChristophe Leroy struct pt_regs *regs = task->thread.regs;
23323a780cSChristophe Leroy
24323a780cSChristophe Leroy if (regs != NULL) {
25323a780cSChristophe Leroy task->thread.debug.dbcr0 &= ~DBCR0_IC;
26323a780cSChristophe Leroy task->thread.debug.dbcr0 = DBCR0_IDM | DBCR0_BT;
27*59dc5bfcSNicholas Piggin regs_set_return_msr(regs, regs->msr | MSR_DE);
28323a780cSChristophe Leroy }
29323a780cSChristophe Leroy set_tsk_thread_flag(task, TIF_SINGLESTEP);
30323a780cSChristophe Leroy }
31323a780cSChristophe Leroy
user_disable_single_step(struct task_struct * task)32323a780cSChristophe Leroy void user_disable_single_step(struct task_struct *task)
33323a780cSChristophe Leroy {
34323a780cSChristophe Leroy struct pt_regs *regs = task->thread.regs;
35323a780cSChristophe Leroy
36323a780cSChristophe Leroy if (regs != NULL) {
37323a780cSChristophe Leroy /*
38323a780cSChristophe Leroy * The logic to disable single stepping should be as
39323a780cSChristophe Leroy * simple as turning off the Instruction Complete flag.
40323a780cSChristophe Leroy * And, after doing so, if all debug flags are off, turn
41323a780cSChristophe Leroy * off DBCR0(IDM) and MSR(DE) .... Torez
42323a780cSChristophe Leroy */
43323a780cSChristophe Leroy task->thread.debug.dbcr0 &= ~(DBCR0_IC | DBCR0_BT);
44323a780cSChristophe Leroy /*
45323a780cSChristophe Leroy * Test to see if any of the DBCR_ACTIVE_EVENTS bits are set.
46323a780cSChristophe Leroy */
47323a780cSChristophe Leroy if (!DBCR_ACTIVE_EVENTS(task->thread.debug.dbcr0,
48323a780cSChristophe Leroy task->thread.debug.dbcr1)) {
49323a780cSChristophe Leroy /*
50323a780cSChristophe Leroy * All debug events were off.....
51323a780cSChristophe Leroy */
52323a780cSChristophe Leroy task->thread.debug.dbcr0 &= ~DBCR0_IDM;
53*59dc5bfcSNicholas Piggin regs_set_return_msr(regs, regs->msr & ~MSR_DE);
54323a780cSChristophe Leroy }
55323a780cSChristophe Leroy }
56323a780cSChristophe Leroy clear_tsk_thread_flag(task, TIF_SINGLESTEP);
57323a780cSChristophe Leroy }
58323a780cSChristophe Leroy
ppc_gethwdinfo(struct ppc_debug_info * dbginfo)59da529d47SChristophe Leroy void ppc_gethwdinfo(struct ppc_debug_info *dbginfo)
60da529d47SChristophe Leroy {
61da529d47SChristophe Leroy dbginfo->version = 1;
62da529d47SChristophe Leroy dbginfo->num_instruction_bps = CONFIG_PPC_ADV_DEBUG_IACS;
63da529d47SChristophe Leroy dbginfo->num_data_bps = CONFIG_PPC_ADV_DEBUG_DACS;
64da529d47SChristophe Leroy dbginfo->num_condition_regs = CONFIG_PPC_ADV_DEBUG_DVCS;
65da529d47SChristophe Leroy dbginfo->data_bp_alignment = 4;
66da529d47SChristophe Leroy dbginfo->sizeof_condition = 4;
67da529d47SChristophe Leroy dbginfo->features = PPC_DEBUG_FEATURE_INSN_BP_RANGE |
68da529d47SChristophe Leroy PPC_DEBUG_FEATURE_INSN_BP_MASK;
69da529d47SChristophe Leroy if (IS_ENABLED(CONFIG_PPC_ADV_DEBUG_DAC_RANGE))
70da529d47SChristophe Leroy dbginfo->features |= PPC_DEBUG_FEATURE_DATA_BP_RANGE |
71da529d47SChristophe Leroy PPC_DEBUG_FEATURE_DATA_BP_MASK;
72da529d47SChristophe Leroy }
73da529d47SChristophe Leroy
ptrace_get_debugreg(struct task_struct * child,unsigned long addr,unsigned long __user * datalp)74e08227d2SChristophe Leroy int ptrace_get_debugreg(struct task_struct *child, unsigned long addr,
75e08227d2SChristophe Leroy unsigned long __user *datalp)
76e08227d2SChristophe Leroy {
77e08227d2SChristophe Leroy /* We only support one DABR and no IABRS at the moment */
78e08227d2SChristophe Leroy if (addr > 0)
79e08227d2SChristophe Leroy return -EINVAL;
80e08227d2SChristophe Leroy return put_user(child->thread.debug.dac1, datalp);
81e08227d2SChristophe Leroy }
82e08227d2SChristophe Leroy
ptrace_set_debugreg(struct task_struct * task,unsigned long addr,unsigned long data)83323a780cSChristophe Leroy int ptrace_set_debugreg(struct task_struct *task, unsigned long addr, unsigned long data)
84323a780cSChristophe Leroy {
85*59dc5bfcSNicholas Piggin struct pt_regs *regs = task->thread.regs;
86323a780cSChristophe Leroy #ifdef CONFIG_HAVE_HW_BREAKPOINT
87323a780cSChristophe Leroy int ret;
88323a780cSChristophe Leroy struct thread_struct *thread = &task->thread;
89323a780cSChristophe Leroy struct perf_event *bp;
90323a780cSChristophe Leroy struct perf_event_attr attr;
91323a780cSChristophe Leroy #endif /* CONFIG_HAVE_HW_BREAKPOINT */
92323a780cSChristophe Leroy
93323a780cSChristophe Leroy /* For ppc64 we support one DABR and no IABR's at the moment (ppc64).
94323a780cSChristophe Leroy * For embedded processors we support one DAC and no IAC's at the
95323a780cSChristophe Leroy * moment.
96323a780cSChristophe Leroy */
97323a780cSChristophe Leroy if (addr > 0)
98323a780cSChristophe Leroy return -EINVAL;
99323a780cSChristophe Leroy
100323a780cSChristophe Leroy /* The bottom 3 bits in dabr are flags */
101323a780cSChristophe Leroy if ((data & ~0x7UL) >= TASK_SIZE)
102323a780cSChristophe Leroy return -EIO;
103323a780cSChristophe Leroy
104323a780cSChristophe Leroy /* As described above, it was assumed 3 bits were passed with the data
105323a780cSChristophe Leroy * address, but we will assume only the mode bits will be passed
106323a780cSChristophe Leroy * as to not cause alignment restrictions for DAC-based processors.
107323a780cSChristophe Leroy */
108323a780cSChristophe Leroy
109323a780cSChristophe Leroy /* DAC's hold the whole address without any mode flags */
110323a780cSChristophe Leroy task->thread.debug.dac1 = data & ~0x3UL;
111323a780cSChristophe Leroy
112323a780cSChristophe Leroy if (task->thread.debug.dac1 == 0) {
113323a780cSChristophe Leroy dbcr_dac(task) &= ~(DBCR_DAC1R | DBCR_DAC1W);
114323a780cSChristophe Leroy if (!DBCR_ACTIVE_EVENTS(task->thread.debug.dbcr0,
115323a780cSChristophe Leroy task->thread.debug.dbcr1)) {
116*59dc5bfcSNicholas Piggin regs_set_return_msr(regs, regs->msr & ~MSR_DE);
117323a780cSChristophe Leroy task->thread.debug.dbcr0 &= ~DBCR0_IDM;
118323a780cSChristophe Leroy }
119323a780cSChristophe Leroy return 0;
120323a780cSChristophe Leroy }
121323a780cSChristophe Leroy
122323a780cSChristophe Leroy /* Read or Write bits must be set */
123323a780cSChristophe Leroy
124323a780cSChristophe Leroy if (!(data & 0x3UL))
125323a780cSChristophe Leroy return -EINVAL;
126323a780cSChristophe Leroy
127323a780cSChristophe Leroy /* Set the Internal Debugging flag (IDM bit 1) for the DBCR0 register */
128323a780cSChristophe Leroy task->thread.debug.dbcr0 |= DBCR0_IDM;
129323a780cSChristophe Leroy
130323a780cSChristophe Leroy /* Check for write and read flags and set DBCR0 accordingly */
131323a780cSChristophe Leroy dbcr_dac(task) &= ~(DBCR_DAC1R | DBCR_DAC1W);
132323a780cSChristophe Leroy if (data & 0x1UL)
133323a780cSChristophe Leroy dbcr_dac(task) |= DBCR_DAC1R;
134323a780cSChristophe Leroy if (data & 0x2UL)
135323a780cSChristophe Leroy dbcr_dac(task) |= DBCR_DAC1W;
136*59dc5bfcSNicholas Piggin regs_set_return_msr(regs, regs->msr | MSR_DE);
137323a780cSChristophe Leroy return 0;
138323a780cSChristophe Leroy }
139323a780cSChristophe Leroy
set_instruction_bp(struct task_struct * child,struct ppc_hw_breakpoint * bp_info)140323a780cSChristophe Leroy static long set_instruction_bp(struct task_struct *child,
141323a780cSChristophe Leroy struct ppc_hw_breakpoint *bp_info)
142323a780cSChristophe Leroy {
143323a780cSChristophe Leroy int slot;
144323a780cSChristophe Leroy int slot1_in_use = ((child->thread.debug.dbcr0 & DBCR0_IAC1) != 0);
145323a780cSChristophe Leroy int slot2_in_use = ((child->thread.debug.dbcr0 & DBCR0_IAC2) != 0);
146323a780cSChristophe Leroy int slot3_in_use = ((child->thread.debug.dbcr0 & DBCR0_IAC3) != 0);
147323a780cSChristophe Leroy int slot4_in_use = ((child->thread.debug.dbcr0 & DBCR0_IAC4) != 0);
148323a780cSChristophe Leroy
149323a780cSChristophe Leroy if (dbcr_iac_range(child) & DBCR_IAC12MODE)
150323a780cSChristophe Leroy slot2_in_use = 1;
151323a780cSChristophe Leroy if (dbcr_iac_range(child) & DBCR_IAC34MODE)
152323a780cSChristophe Leroy slot4_in_use = 1;
153323a780cSChristophe Leroy
154323a780cSChristophe Leroy if (bp_info->addr >= TASK_SIZE)
155323a780cSChristophe Leroy return -EIO;
156323a780cSChristophe Leroy
157323a780cSChristophe Leroy if (bp_info->addr_mode != PPC_BREAKPOINT_MODE_EXACT) {
158323a780cSChristophe Leroy /* Make sure range is valid. */
159323a780cSChristophe Leroy if (bp_info->addr2 >= TASK_SIZE)
160323a780cSChristophe Leroy return -EIO;
161323a780cSChristophe Leroy
162323a780cSChristophe Leroy /* We need a pair of IAC regsisters */
163323a780cSChristophe Leroy if (!slot1_in_use && !slot2_in_use) {
164323a780cSChristophe Leroy slot = 1;
165323a780cSChristophe Leroy child->thread.debug.iac1 = bp_info->addr;
166323a780cSChristophe Leroy child->thread.debug.iac2 = bp_info->addr2;
167323a780cSChristophe Leroy child->thread.debug.dbcr0 |= DBCR0_IAC1;
168323a780cSChristophe Leroy if (bp_info->addr_mode ==
169323a780cSChristophe Leroy PPC_BREAKPOINT_MODE_RANGE_EXCLUSIVE)
170323a780cSChristophe Leroy dbcr_iac_range(child) |= DBCR_IAC12X;
171323a780cSChristophe Leroy else
172323a780cSChristophe Leroy dbcr_iac_range(child) |= DBCR_IAC12I;
173323a780cSChristophe Leroy #if CONFIG_PPC_ADV_DEBUG_IACS > 2
174323a780cSChristophe Leroy } else if ((!slot3_in_use) && (!slot4_in_use)) {
175323a780cSChristophe Leroy slot = 3;
176323a780cSChristophe Leroy child->thread.debug.iac3 = bp_info->addr;
177323a780cSChristophe Leroy child->thread.debug.iac4 = bp_info->addr2;
178323a780cSChristophe Leroy child->thread.debug.dbcr0 |= DBCR0_IAC3;
179323a780cSChristophe Leroy if (bp_info->addr_mode ==
180323a780cSChristophe Leroy PPC_BREAKPOINT_MODE_RANGE_EXCLUSIVE)
181323a780cSChristophe Leroy dbcr_iac_range(child) |= DBCR_IAC34X;
182323a780cSChristophe Leroy else
183323a780cSChristophe Leroy dbcr_iac_range(child) |= DBCR_IAC34I;
184323a780cSChristophe Leroy #endif
185323a780cSChristophe Leroy } else {
186323a780cSChristophe Leroy return -ENOSPC;
187323a780cSChristophe Leroy }
188323a780cSChristophe Leroy } else {
189323a780cSChristophe Leroy /* We only need one. If possible leave a pair free in
190323a780cSChristophe Leroy * case a range is needed later
191323a780cSChristophe Leroy */
192323a780cSChristophe Leroy if (!slot1_in_use) {
193323a780cSChristophe Leroy /*
194323a780cSChristophe Leroy * Don't use iac1 if iac1-iac2 are free and either
195323a780cSChristophe Leroy * iac3 or iac4 (but not both) are free
196323a780cSChristophe Leroy */
197323a780cSChristophe Leroy if (slot2_in_use || slot3_in_use == slot4_in_use) {
198323a780cSChristophe Leroy slot = 1;
199323a780cSChristophe Leroy child->thread.debug.iac1 = bp_info->addr;
200323a780cSChristophe Leroy child->thread.debug.dbcr0 |= DBCR0_IAC1;
201323a780cSChristophe Leroy goto out;
202323a780cSChristophe Leroy }
203323a780cSChristophe Leroy }
204323a780cSChristophe Leroy if (!slot2_in_use) {
205323a780cSChristophe Leroy slot = 2;
206323a780cSChristophe Leroy child->thread.debug.iac2 = bp_info->addr;
207323a780cSChristophe Leroy child->thread.debug.dbcr0 |= DBCR0_IAC2;
208323a780cSChristophe Leroy #if CONFIG_PPC_ADV_DEBUG_IACS > 2
209323a780cSChristophe Leroy } else if (!slot3_in_use) {
210323a780cSChristophe Leroy slot = 3;
211323a780cSChristophe Leroy child->thread.debug.iac3 = bp_info->addr;
212323a780cSChristophe Leroy child->thread.debug.dbcr0 |= DBCR0_IAC3;
213323a780cSChristophe Leroy } else if (!slot4_in_use) {
214323a780cSChristophe Leroy slot = 4;
215323a780cSChristophe Leroy child->thread.debug.iac4 = bp_info->addr;
216323a780cSChristophe Leroy child->thread.debug.dbcr0 |= DBCR0_IAC4;
217323a780cSChristophe Leroy #endif
218323a780cSChristophe Leroy } else {
219323a780cSChristophe Leroy return -ENOSPC;
220323a780cSChristophe Leroy }
221323a780cSChristophe Leroy }
222323a780cSChristophe Leroy out:
223323a780cSChristophe Leroy child->thread.debug.dbcr0 |= DBCR0_IDM;
224*59dc5bfcSNicholas Piggin regs_set_return_msr(child->thread.regs, child->thread.regs->msr | MSR_DE);
225323a780cSChristophe Leroy
226323a780cSChristophe Leroy return slot;
227323a780cSChristophe Leroy }
228323a780cSChristophe Leroy
del_instruction_bp(struct task_struct * child,int slot)229323a780cSChristophe Leroy static int del_instruction_bp(struct task_struct *child, int slot)
230323a780cSChristophe Leroy {
231323a780cSChristophe Leroy switch (slot) {
232323a780cSChristophe Leroy case 1:
233323a780cSChristophe Leroy if ((child->thread.debug.dbcr0 & DBCR0_IAC1) == 0)
234323a780cSChristophe Leroy return -ENOENT;
235323a780cSChristophe Leroy
236323a780cSChristophe Leroy if (dbcr_iac_range(child) & DBCR_IAC12MODE) {
237323a780cSChristophe Leroy /* address range - clear slots 1 & 2 */
238323a780cSChristophe Leroy child->thread.debug.iac2 = 0;
239323a780cSChristophe Leroy dbcr_iac_range(child) &= ~DBCR_IAC12MODE;
240323a780cSChristophe Leroy }
241323a780cSChristophe Leroy child->thread.debug.iac1 = 0;
242323a780cSChristophe Leroy child->thread.debug.dbcr0 &= ~DBCR0_IAC1;
243323a780cSChristophe Leroy break;
244323a780cSChristophe Leroy case 2:
245323a780cSChristophe Leroy if ((child->thread.debug.dbcr0 & DBCR0_IAC2) == 0)
246323a780cSChristophe Leroy return -ENOENT;
247323a780cSChristophe Leroy
248323a780cSChristophe Leroy if (dbcr_iac_range(child) & DBCR_IAC12MODE)
249323a780cSChristophe Leroy /* used in a range */
250323a780cSChristophe Leroy return -EINVAL;
251323a780cSChristophe Leroy child->thread.debug.iac2 = 0;
252323a780cSChristophe Leroy child->thread.debug.dbcr0 &= ~DBCR0_IAC2;
253323a780cSChristophe Leroy break;
254323a780cSChristophe Leroy #if CONFIG_PPC_ADV_DEBUG_IACS > 2
255323a780cSChristophe Leroy case 3:
256323a780cSChristophe Leroy if ((child->thread.debug.dbcr0 & DBCR0_IAC3) == 0)
257323a780cSChristophe Leroy return -ENOENT;
258323a780cSChristophe Leroy
259323a780cSChristophe Leroy if (dbcr_iac_range(child) & DBCR_IAC34MODE) {
260323a780cSChristophe Leroy /* address range - clear slots 3 & 4 */
261323a780cSChristophe Leroy child->thread.debug.iac4 = 0;
262323a780cSChristophe Leroy dbcr_iac_range(child) &= ~DBCR_IAC34MODE;
263323a780cSChristophe Leroy }
264323a780cSChristophe Leroy child->thread.debug.iac3 = 0;
265323a780cSChristophe Leroy child->thread.debug.dbcr0 &= ~DBCR0_IAC3;
266323a780cSChristophe Leroy break;
267323a780cSChristophe Leroy case 4:
268323a780cSChristophe Leroy if ((child->thread.debug.dbcr0 & DBCR0_IAC4) == 0)
269323a780cSChristophe Leroy return -ENOENT;
270323a780cSChristophe Leroy
271323a780cSChristophe Leroy if (dbcr_iac_range(child) & DBCR_IAC34MODE)
272323a780cSChristophe Leroy /* Used in a range */
273323a780cSChristophe Leroy return -EINVAL;
274323a780cSChristophe Leroy child->thread.debug.iac4 = 0;
275323a780cSChristophe Leroy child->thread.debug.dbcr0 &= ~DBCR0_IAC4;
276323a780cSChristophe Leroy break;
277323a780cSChristophe Leroy #endif
278323a780cSChristophe Leroy default:
279323a780cSChristophe Leroy return -EINVAL;
280323a780cSChristophe Leroy }
281323a780cSChristophe Leroy return 0;
282323a780cSChristophe Leroy }
283323a780cSChristophe Leroy
set_dac(struct task_struct * child,struct ppc_hw_breakpoint * bp_info)284323a780cSChristophe Leroy static int set_dac(struct task_struct *child, struct ppc_hw_breakpoint *bp_info)
285323a780cSChristophe Leroy {
286323a780cSChristophe Leroy int byte_enable =
287323a780cSChristophe Leroy (bp_info->condition_mode >> PPC_BREAKPOINT_CONDITION_BE_SHIFT)
288323a780cSChristophe Leroy & 0xf;
289323a780cSChristophe Leroy int condition_mode =
290323a780cSChristophe Leroy bp_info->condition_mode & PPC_BREAKPOINT_CONDITION_MODE;
291323a780cSChristophe Leroy int slot;
292323a780cSChristophe Leroy
293323a780cSChristophe Leroy if (byte_enable && condition_mode == 0)
294323a780cSChristophe Leroy return -EINVAL;
295323a780cSChristophe Leroy
296323a780cSChristophe Leroy if (bp_info->addr >= TASK_SIZE)
297323a780cSChristophe Leroy return -EIO;
298323a780cSChristophe Leroy
299323a780cSChristophe Leroy if ((dbcr_dac(child) & (DBCR_DAC1R | DBCR_DAC1W)) == 0) {
300323a780cSChristophe Leroy slot = 1;
301323a780cSChristophe Leroy if (bp_info->trigger_type & PPC_BREAKPOINT_TRIGGER_READ)
302323a780cSChristophe Leroy dbcr_dac(child) |= DBCR_DAC1R;
303323a780cSChristophe Leroy if (bp_info->trigger_type & PPC_BREAKPOINT_TRIGGER_WRITE)
304323a780cSChristophe Leroy dbcr_dac(child) |= DBCR_DAC1W;
305323a780cSChristophe Leroy child->thread.debug.dac1 = (unsigned long)bp_info->addr;
306323a780cSChristophe Leroy #if CONFIG_PPC_ADV_DEBUG_DVCS > 0
307323a780cSChristophe Leroy if (byte_enable) {
308323a780cSChristophe Leroy child->thread.debug.dvc1 =
309323a780cSChristophe Leroy (unsigned long)bp_info->condition_value;
310323a780cSChristophe Leroy child->thread.debug.dbcr2 |=
311323a780cSChristophe Leroy ((byte_enable << DBCR2_DVC1BE_SHIFT) |
312323a780cSChristophe Leroy (condition_mode << DBCR2_DVC1M_SHIFT));
313323a780cSChristophe Leroy }
314323a780cSChristophe Leroy #endif
315323a780cSChristophe Leroy #ifdef CONFIG_PPC_ADV_DEBUG_DAC_RANGE
316323a780cSChristophe Leroy } else if (child->thread.debug.dbcr2 & DBCR2_DAC12MODE) {
317323a780cSChristophe Leroy /* Both dac1 and dac2 are part of a range */
318323a780cSChristophe Leroy return -ENOSPC;
319323a780cSChristophe Leroy #endif
320323a780cSChristophe Leroy } else if ((dbcr_dac(child) & (DBCR_DAC2R | DBCR_DAC2W)) == 0) {
321323a780cSChristophe Leroy slot = 2;
322323a780cSChristophe Leroy if (bp_info->trigger_type & PPC_BREAKPOINT_TRIGGER_READ)
323323a780cSChristophe Leroy dbcr_dac(child) |= DBCR_DAC2R;
324323a780cSChristophe Leroy if (bp_info->trigger_type & PPC_BREAKPOINT_TRIGGER_WRITE)
325323a780cSChristophe Leroy dbcr_dac(child) |= DBCR_DAC2W;
326323a780cSChristophe Leroy child->thread.debug.dac2 = (unsigned long)bp_info->addr;
327323a780cSChristophe Leroy #if CONFIG_PPC_ADV_DEBUG_DVCS > 0
328323a780cSChristophe Leroy if (byte_enable) {
329323a780cSChristophe Leroy child->thread.debug.dvc2 =
330323a780cSChristophe Leroy (unsigned long)bp_info->condition_value;
331323a780cSChristophe Leroy child->thread.debug.dbcr2 |=
332323a780cSChristophe Leroy ((byte_enable << DBCR2_DVC2BE_SHIFT) |
333323a780cSChristophe Leroy (condition_mode << DBCR2_DVC2M_SHIFT));
334323a780cSChristophe Leroy }
335323a780cSChristophe Leroy #endif
336323a780cSChristophe Leroy } else {
337323a780cSChristophe Leroy return -ENOSPC;
338323a780cSChristophe Leroy }
339323a780cSChristophe Leroy child->thread.debug.dbcr0 |= DBCR0_IDM;
340*59dc5bfcSNicholas Piggin regs_set_return_msr(child->thread.regs, child->thread.regs->msr | MSR_DE);
341323a780cSChristophe Leroy
342323a780cSChristophe Leroy return slot + 4;
343323a780cSChristophe Leroy }
344323a780cSChristophe Leroy
del_dac(struct task_struct * child,int slot)345323a780cSChristophe Leroy static int del_dac(struct task_struct *child, int slot)
346323a780cSChristophe Leroy {
347323a780cSChristophe Leroy if (slot == 1) {
348323a780cSChristophe Leroy if ((dbcr_dac(child) & (DBCR_DAC1R | DBCR_DAC1W)) == 0)
349323a780cSChristophe Leroy return -ENOENT;
350323a780cSChristophe Leroy
351323a780cSChristophe Leroy child->thread.debug.dac1 = 0;
352323a780cSChristophe Leroy dbcr_dac(child) &= ~(DBCR_DAC1R | DBCR_DAC1W);
353323a780cSChristophe Leroy #ifdef CONFIG_PPC_ADV_DEBUG_DAC_RANGE
354323a780cSChristophe Leroy if (child->thread.debug.dbcr2 & DBCR2_DAC12MODE) {
355323a780cSChristophe Leroy child->thread.debug.dac2 = 0;
356323a780cSChristophe Leroy child->thread.debug.dbcr2 &= ~DBCR2_DAC12MODE;
357323a780cSChristophe Leroy }
358323a780cSChristophe Leroy child->thread.debug.dbcr2 &= ~(DBCR2_DVC1M | DBCR2_DVC1BE);
359323a780cSChristophe Leroy #endif
360323a780cSChristophe Leroy #if CONFIG_PPC_ADV_DEBUG_DVCS > 0
361323a780cSChristophe Leroy child->thread.debug.dvc1 = 0;
362323a780cSChristophe Leroy #endif
363323a780cSChristophe Leroy } else if (slot == 2) {
364323a780cSChristophe Leroy if ((dbcr_dac(child) & (DBCR_DAC2R | DBCR_DAC2W)) == 0)
365323a780cSChristophe Leroy return -ENOENT;
366323a780cSChristophe Leroy
367323a780cSChristophe Leroy #ifdef CONFIG_PPC_ADV_DEBUG_DAC_RANGE
368323a780cSChristophe Leroy if (child->thread.debug.dbcr2 & DBCR2_DAC12MODE)
369323a780cSChristophe Leroy /* Part of a range */
370323a780cSChristophe Leroy return -EINVAL;
371323a780cSChristophe Leroy child->thread.debug.dbcr2 &= ~(DBCR2_DVC2M | DBCR2_DVC2BE);
372323a780cSChristophe Leroy #endif
373323a780cSChristophe Leroy #if CONFIG_PPC_ADV_DEBUG_DVCS > 0
374323a780cSChristophe Leroy child->thread.debug.dvc2 = 0;
375323a780cSChristophe Leroy #endif
376323a780cSChristophe Leroy child->thread.debug.dac2 = 0;
377323a780cSChristophe Leroy dbcr_dac(child) &= ~(DBCR_DAC2R | DBCR_DAC2W);
378323a780cSChristophe Leroy } else {
379323a780cSChristophe Leroy return -EINVAL;
380323a780cSChristophe Leroy }
381323a780cSChristophe Leroy
382323a780cSChristophe Leroy return 0;
383323a780cSChristophe Leroy }
384323a780cSChristophe Leroy
385323a780cSChristophe Leroy #ifdef CONFIG_PPC_ADV_DEBUG_DAC_RANGE
set_dac_range(struct task_struct * child,struct ppc_hw_breakpoint * bp_info)386323a780cSChristophe Leroy static int set_dac_range(struct task_struct *child,
387323a780cSChristophe Leroy struct ppc_hw_breakpoint *bp_info)
388323a780cSChristophe Leroy {
389323a780cSChristophe Leroy int mode = bp_info->addr_mode & PPC_BREAKPOINT_MODE_MASK;
390323a780cSChristophe Leroy
391323a780cSChristophe Leroy /* We don't allow range watchpoints to be used with DVC */
392323a780cSChristophe Leroy if (bp_info->condition_mode)
393323a780cSChristophe Leroy return -EINVAL;
394323a780cSChristophe Leroy
395323a780cSChristophe Leroy /*
396323a780cSChristophe Leroy * Best effort to verify the address range. The user/supervisor bits
397323a780cSChristophe Leroy * prevent trapping in kernel space, but let's fail on an obvious bad
398323a780cSChristophe Leroy * range. The simple test on the mask is not fool-proof, and any
399323a780cSChristophe Leroy * exclusive range will spill over into kernel space.
400323a780cSChristophe Leroy */
401323a780cSChristophe Leroy if (bp_info->addr >= TASK_SIZE)
402323a780cSChristophe Leroy return -EIO;
403323a780cSChristophe Leroy if (mode == PPC_BREAKPOINT_MODE_MASK) {
404323a780cSChristophe Leroy /*
405323a780cSChristophe Leroy * dac2 is a bitmask. Don't allow a mask that makes a
406323a780cSChristophe Leroy * kernel space address from a valid dac1 value
407323a780cSChristophe Leroy */
408323a780cSChristophe Leroy if (~((unsigned long)bp_info->addr2) >= TASK_SIZE)
409323a780cSChristophe Leroy return -EIO;
410323a780cSChristophe Leroy } else {
411323a780cSChristophe Leroy /*
412323a780cSChristophe Leroy * For range breakpoints, addr2 must also be a valid address
413323a780cSChristophe Leroy */
414323a780cSChristophe Leroy if (bp_info->addr2 >= TASK_SIZE)
415323a780cSChristophe Leroy return -EIO;
416323a780cSChristophe Leroy }
417323a780cSChristophe Leroy
418323a780cSChristophe Leroy if (child->thread.debug.dbcr0 &
419323a780cSChristophe Leroy (DBCR0_DAC1R | DBCR0_DAC1W | DBCR0_DAC2R | DBCR0_DAC2W))
420323a780cSChristophe Leroy return -ENOSPC;
421323a780cSChristophe Leroy
422323a780cSChristophe Leroy if (bp_info->trigger_type & PPC_BREAKPOINT_TRIGGER_READ)
423323a780cSChristophe Leroy child->thread.debug.dbcr0 |= (DBCR0_DAC1R | DBCR0_IDM);
424323a780cSChristophe Leroy if (bp_info->trigger_type & PPC_BREAKPOINT_TRIGGER_WRITE)
425323a780cSChristophe Leroy child->thread.debug.dbcr0 |= (DBCR0_DAC1W | DBCR0_IDM);
426323a780cSChristophe Leroy child->thread.debug.dac1 = bp_info->addr;
427323a780cSChristophe Leroy child->thread.debug.dac2 = bp_info->addr2;
428323a780cSChristophe Leroy if (mode == PPC_BREAKPOINT_MODE_RANGE_INCLUSIVE)
429323a780cSChristophe Leroy child->thread.debug.dbcr2 |= DBCR2_DAC12M;
430323a780cSChristophe Leroy else if (mode == PPC_BREAKPOINT_MODE_RANGE_EXCLUSIVE)
431323a780cSChristophe Leroy child->thread.debug.dbcr2 |= DBCR2_DAC12MX;
432323a780cSChristophe Leroy else /* PPC_BREAKPOINT_MODE_MASK */
433323a780cSChristophe Leroy child->thread.debug.dbcr2 |= DBCR2_DAC12MM;
434*59dc5bfcSNicholas Piggin regs_set_return_msr(child->thread.regs, child->thread.regs->msr | MSR_DE);
435323a780cSChristophe Leroy
436323a780cSChristophe Leroy return 5;
437323a780cSChristophe Leroy }
438323a780cSChristophe Leroy #endif /* CONFIG_PPC_ADV_DEBUG_DAC_RANGE */
439323a780cSChristophe Leroy
ppc_set_hwdebug(struct task_struct * child,struct ppc_hw_breakpoint * bp_info)440323a780cSChristophe Leroy long ppc_set_hwdebug(struct task_struct *child, struct ppc_hw_breakpoint *bp_info)
441323a780cSChristophe Leroy {
442323a780cSChristophe Leroy if (bp_info->version != 1)
443323a780cSChristophe Leroy return -ENOTSUPP;
444323a780cSChristophe Leroy /*
445323a780cSChristophe Leroy * Check for invalid flags and combinations
446323a780cSChristophe Leroy */
447323a780cSChristophe Leroy if (bp_info->trigger_type == 0 ||
448323a780cSChristophe Leroy (bp_info->trigger_type & ~(PPC_BREAKPOINT_TRIGGER_EXECUTE |
449323a780cSChristophe Leroy PPC_BREAKPOINT_TRIGGER_RW)) ||
450323a780cSChristophe Leroy (bp_info->addr_mode & ~PPC_BREAKPOINT_MODE_MASK) ||
451323a780cSChristophe Leroy (bp_info->condition_mode &
452323a780cSChristophe Leroy ~(PPC_BREAKPOINT_CONDITION_MODE |
453323a780cSChristophe Leroy PPC_BREAKPOINT_CONDITION_BE_ALL)))
454323a780cSChristophe Leroy return -EINVAL;
455323a780cSChristophe Leroy #if CONFIG_PPC_ADV_DEBUG_DVCS == 0
456323a780cSChristophe Leroy if (bp_info->condition_mode != PPC_BREAKPOINT_CONDITION_NONE)
457323a780cSChristophe Leroy return -EINVAL;
458323a780cSChristophe Leroy #endif
459323a780cSChristophe Leroy
460323a780cSChristophe Leroy if (bp_info->trigger_type & PPC_BREAKPOINT_TRIGGER_EXECUTE) {
461323a780cSChristophe Leroy if (bp_info->trigger_type != PPC_BREAKPOINT_TRIGGER_EXECUTE ||
462323a780cSChristophe Leroy bp_info->condition_mode != PPC_BREAKPOINT_CONDITION_NONE)
463323a780cSChristophe Leroy return -EINVAL;
464323a780cSChristophe Leroy return set_instruction_bp(child, bp_info);
465323a780cSChristophe Leroy }
466323a780cSChristophe Leroy if (bp_info->addr_mode == PPC_BREAKPOINT_MODE_EXACT)
467323a780cSChristophe Leroy return set_dac(child, bp_info);
468323a780cSChristophe Leroy
469323a780cSChristophe Leroy #ifdef CONFIG_PPC_ADV_DEBUG_DAC_RANGE
470323a780cSChristophe Leroy return set_dac_range(child, bp_info);
471323a780cSChristophe Leroy #else
472323a780cSChristophe Leroy return -EINVAL;
473323a780cSChristophe Leroy #endif
474323a780cSChristophe Leroy }
475323a780cSChristophe Leroy
ppc_del_hwdebug(struct task_struct * child,long data)476323a780cSChristophe Leroy long ppc_del_hwdebug(struct task_struct *child, long data)
477323a780cSChristophe Leroy {
478323a780cSChristophe Leroy int rc;
479323a780cSChristophe Leroy
480323a780cSChristophe Leroy if (data <= 4)
481323a780cSChristophe Leroy rc = del_instruction_bp(child, (int)data);
482323a780cSChristophe Leroy else
483323a780cSChristophe Leroy rc = del_dac(child, (int)data - 4);
484323a780cSChristophe Leroy
485323a780cSChristophe Leroy if (!rc) {
486323a780cSChristophe Leroy if (!DBCR_ACTIVE_EVENTS(child->thread.debug.dbcr0,
487323a780cSChristophe Leroy child->thread.debug.dbcr1)) {
488323a780cSChristophe Leroy child->thread.debug.dbcr0 &= ~DBCR0_IDM;
489*59dc5bfcSNicholas Piggin regs_set_return_msr(child->thread.regs,
490*59dc5bfcSNicholas Piggin child->thread.regs->msr & ~MSR_DE);
491323a780cSChristophe Leroy }
492323a780cSChristophe Leroy }
493323a780cSChristophe Leroy return rc;
494323a780cSChristophe Leroy }
495