Skip to content

Commit c8e57ab

Browse files
lhloongsonchenhuacai
authored andcommitted
LoongArch: Trigger user-space watchpoints correctly
In the current code, gdb can set the watchpoint successfully through ptrace interface, but watchpoint will not be triggered. When debugging the following code using gdb. lihui@bogon:~$ cat test.c #include <stdio.h> int a = 0; int main() { a = 1; printf("a = %d\n", a); return 0; } lihui@bogon:~$ gcc -g test.c -o test lihui@bogon:~$ gdb test ... (gdb) watch a ... (gdb) r ... a = 1 [Inferior 1 (process 4650) exited normally] No watchpoints were triggered, the root causes are: 1. Kernel uses perf_event and hw_breakpoint framework to control watchpoint, but the perf_event corresponding to watchpoint is not enabled. So it needs to be enabled according to MWPnCFG3 or FWPnCFG3 PLV bit field in ptrace_hbp_set_ctrl(), and privilege is set according to the monitored addr in hw_breakpoint_control(). Furthermore, add a judgment in ptrace_hbp_set_addr() to ensure kernel-space addr cannot be monitored in user mode. 2. The global enable control for all watchpoints is the WE bit of CSR.CRMD, and hardware sets the value to 0 when an exception is triggered. When the ERTN instruction is executed to return, the hardware restores the value of the PWE field of CSR.PRMD here. So, before a thread containing watchpoints be scheduled, the PWE field of CSR.PRMD needs to be set to 1. Add this modification in hw_breakpoint_control(). All changes according to the LoongArch Reference Manual: https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html#control-and-status-registers-related-to-watchpoints https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html#basic-control-and-status-registers With this patch: lihui@bogon:~$ gdb test ... (gdb) watch a Hardware watchpoint 1: a (gdb) r ... Hardware watchpoint 1: a Old value = 0 New value = 1 main () at test.c:6 6 printf("a = %d\n", a); (gdb) c Continuing. a = 1 [Inferior 1 (process 775) exited normally] Cc: [email protected] Signed-off-by: Hui Li <[email protected]> Signed-off-by: Huacai Chen <[email protected]>
1 parent f63a47b commit c8e57ab

File tree

3 files changed

+31
-6
lines changed

3 files changed

+31
-6
lines changed

arch/loongarch/include/asm/hw_breakpoint.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ do { \
7575
#define CSR_MWPC_NUM 0x3f
7676

7777
#define CTRL_PLV_ENABLE 0x1e
78+
#define CTRL_PLV0_ENABLE 0x02
79+
#define CTRL_PLV3_ENABLE 0x10
7880

7981
#define MWPnCFG3_LoadEn 8
8082
#define MWPnCFG3_StoreEn 9

arch/loongarch/kernel/hw_breakpoint.c

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -174,11 +174,21 @@ void flush_ptrace_hw_breakpoint(struct task_struct *tsk)
174174
static int hw_breakpoint_control(struct perf_event *bp,
175175
enum hw_breakpoint_ops ops)
176176
{
177-
u32 ctrl;
177+
u32 ctrl, privilege;
178178
int i, max_slots, enable;
179+
struct pt_regs *regs;
179180
struct perf_event **slots;
180181
struct arch_hw_breakpoint *info = counter_arch_bp(bp);
181182

183+
if (arch_check_bp_in_kernelspace(info))
184+
privilege = CTRL_PLV0_ENABLE;
185+
else
186+
privilege = CTRL_PLV3_ENABLE;
187+
188+
/* Whether bp belongs to a task. */
189+
if (bp->hw.target)
190+
regs = task_pt_regs(bp->hw.target);
191+
182192
if (info->ctrl.type == LOONGARCH_BREAKPOINT_EXECUTE) {
183193
/* Breakpoint */
184194
slots = this_cpu_ptr(bp_on_reg);
@@ -204,13 +214,15 @@ static int hw_breakpoint_control(struct perf_event *bp,
204214
write_wb_reg(CSR_CFG_ASID, i, 0, 0);
205215
write_wb_reg(CSR_CFG_ASID, i, 1, 0);
206216
if (info->ctrl.type == LOONGARCH_BREAKPOINT_EXECUTE) {
207-
write_wb_reg(CSR_CFG_CTRL, i, 0, CTRL_PLV_ENABLE);
217+
write_wb_reg(CSR_CFG_CTRL, i, 0, privilege);
208218
} else {
209219
ctrl = encode_ctrl_reg(info->ctrl);
210-
write_wb_reg(CSR_CFG_CTRL, i, 1, ctrl | CTRL_PLV_ENABLE);
220+
write_wb_reg(CSR_CFG_CTRL, i, 1, ctrl | privilege);
211221
}
212222
enable = csr_read64(LOONGARCH_CSR_CRMD);
213223
csr_write64(CSR_CRMD_WE | enable, LOONGARCH_CSR_CRMD);
224+
if (bp->hw.target)
225+
regs->csr_prmd |= CSR_PRMD_PWE;
214226
break;
215227
case HW_BREAKPOINT_UNINSTALL:
216228
/* Reset the FWPnCFG/MWPnCFG 1~4 register. */
@@ -222,6 +234,8 @@ static int hw_breakpoint_control(struct perf_event *bp,
222234
write_wb_reg(CSR_CFG_CTRL, i, 1, 0);
223235
write_wb_reg(CSR_CFG_ASID, i, 0, 0);
224236
write_wb_reg(CSR_CFG_ASID, i, 1, 0);
237+
if (bp->hw.target)
238+
regs->csr_prmd &= ~CSR_PRMD_PWE;
225239
break;
226240
}
227241

arch/loongarch/kernel/ptrace.c

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -608,9 +608,14 @@ static int ptrace_hbp_set_ctrl(unsigned int note_type,
608608
return -EINVAL;
609609
}
610610

611-
err = ptrace_hbp_fill_attr_ctrl(note_type, ctrl, &attr);
612-
if (err)
613-
return err;
611+
if (uctrl & CTRL_PLV_ENABLE) {
612+
err = ptrace_hbp_fill_attr_ctrl(note_type, ctrl, &attr);
613+
if (err)
614+
return err;
615+
attr.disabled = 0;
616+
} else {
617+
attr.disabled = 1;
618+
}
614619

615620
return modify_user_hw_breakpoint(bp, &attr);
616621
}
@@ -641,6 +646,10 @@ static int ptrace_hbp_set_addr(unsigned int note_type,
641646
struct perf_event *bp;
642647
struct perf_event_attr attr;
643648

649+
/* Kernel-space address cannot be monitored by user-space */
650+
if ((unsigned long)addr >= XKPRANGE)
651+
return -EINVAL;
652+
644653
bp = ptrace_hbp_get_initialised_bp(note_type, tsk, idx);
645654
if (IS_ERR(bp))
646655
return PTR_ERR(bp);

0 commit comments

Comments
 (0)