Skip to content

Commit 3eb2a8b

Browse files
lhloongsonchenhuacai
authored andcommitted
LoongArch: Fix multiple hardware watchpoint issues
In the current code, if multiple hardware breakpoints/watchpoints in a user-space thread, some of them will not be triggered. When debugging the following code using gdb. lihui@bogon:~$ cat test.c #include <stdio.h> int a = 0; int main() { printf("start test\n"); a = 1; printf("a = %d\n", a); printf("end test\n"); return 0; } lihui@bogon:~$ gcc -g test.c -o test lihui@bogon:~$ gdb test ... (gdb) start ... Temporary breakpoint 1, main () at test.c:5 5 printf("start test\n"); (gdb) watch a Hardware watchpoint 2: a (gdb) hbreak 8 Hardware assisted breakpoint 3 at 0x1200006ec: file test.c, line 8. (gdb) c Continuing. start test a = 1 Breakpoint 3, main () at test.c:8 8 printf("end test\n"); ... The first hardware watchpoint is not triggered, the root causes are: 1. In hw_breakpoint_control(), The FWPnCFG1.2.4/MWPnCFG1.2.4 register settings are not distinguished. They should be set based on hardware watchpoint functions (fetch or load/store operations). 2. In breakpoint_handler() and watchpoint_handler(), it doesn't identify which watchpoint is triggered. So, all watchpoint-related perf_event callbacks are called and siginfo is sent to the user space. This will cause user-space unable to determine which watchpoint is triggered. The kernel need to identity which watchpoint is triggered via MWPS/ FWPS registers, and then call the corresponding perf event callbacks to report siginfo to the user-space. Modify the relevant code to solve above issues. 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 With this patch: lihui@bogon:~$ gdb test ... (gdb) start ... Temporary breakpoint 1, main () at test.c:5 5 printf("start test\n"); (gdb) watch a Hardware watchpoint 2: a (gdb) hbreak 8 Hardware assisted breakpoint 3 at 0x1200006ec: file test.c, line 8. (gdb) c Continuing. start test Hardware watchpoint 2: a Old value = 0 New value = 1 main () at test.c:7 7 printf("a = %d\n", a); (gdb) c Continuing. a = 1 Breakpoint 3, main () at test.c:8 8 printf("end test\n"); (gdb) c Continuing. end test [Inferior 1 (process 778) exited normally] Cc: stable@vger.kernel.org Signed-off-by: Hui Li <lihui@loongson.cn> Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
1 parent c8e57ab commit 3eb2a8b

File tree

1 file changed

+33
-24
lines changed

1 file changed

+33
-24
lines changed

arch/loongarch/kernel/hw_breakpoint.c

Lines changed: 33 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -207,15 +207,15 @@ static int hw_breakpoint_control(struct perf_event *bp,
207207
switch (ops) {
208208
case HW_BREAKPOINT_INSTALL:
209209
/* Set the FWPnCFG/MWPnCFG 1~4 register. */
210-
write_wb_reg(CSR_CFG_ADDR, i, 0, info->address);
211-
write_wb_reg(CSR_CFG_ADDR, i, 1, info->address);
212-
write_wb_reg(CSR_CFG_MASK, i, 0, info->mask);
213-
write_wb_reg(CSR_CFG_MASK, i, 1, info->mask);
214-
write_wb_reg(CSR_CFG_ASID, i, 0, 0);
215-
write_wb_reg(CSR_CFG_ASID, i, 1, 0);
216210
if (info->ctrl.type == LOONGARCH_BREAKPOINT_EXECUTE) {
211+
write_wb_reg(CSR_CFG_ADDR, i, 0, info->address);
212+
write_wb_reg(CSR_CFG_MASK, i, 0, info->mask);
213+
write_wb_reg(CSR_CFG_ASID, i, 0, 0);
217214
write_wb_reg(CSR_CFG_CTRL, i, 0, privilege);
218215
} else {
216+
write_wb_reg(CSR_CFG_ADDR, i, 1, info->address);
217+
write_wb_reg(CSR_CFG_MASK, i, 1, info->mask);
218+
write_wb_reg(CSR_CFG_ASID, i, 1, 0);
219219
ctrl = encode_ctrl_reg(info->ctrl);
220220
write_wb_reg(CSR_CFG_CTRL, i, 1, ctrl | privilege);
221221
}
@@ -226,14 +226,17 @@ static int hw_breakpoint_control(struct perf_event *bp,
226226
break;
227227
case HW_BREAKPOINT_UNINSTALL:
228228
/* Reset the FWPnCFG/MWPnCFG 1~4 register. */
229-
write_wb_reg(CSR_CFG_ADDR, i, 0, 0);
230-
write_wb_reg(CSR_CFG_ADDR, i, 1, 0);
231-
write_wb_reg(CSR_CFG_MASK, i, 0, 0);
232-
write_wb_reg(CSR_CFG_MASK, i, 1, 0);
233-
write_wb_reg(CSR_CFG_CTRL, i, 0, 0);
234-
write_wb_reg(CSR_CFG_CTRL, i, 1, 0);
235-
write_wb_reg(CSR_CFG_ASID, i, 0, 0);
236-
write_wb_reg(CSR_CFG_ASID, i, 1, 0);
229+
if (info->ctrl.type == LOONGARCH_BREAKPOINT_EXECUTE) {
230+
write_wb_reg(CSR_CFG_ADDR, i, 0, 0);
231+
write_wb_reg(CSR_CFG_MASK, i, 0, 0);
232+
write_wb_reg(CSR_CFG_CTRL, i, 0, 0);
233+
write_wb_reg(CSR_CFG_ASID, i, 0, 0);
234+
} else {
235+
write_wb_reg(CSR_CFG_ADDR, i, 1, 0);
236+
write_wb_reg(CSR_CFG_MASK, i, 1, 0);
237+
write_wb_reg(CSR_CFG_CTRL, i, 1, 0);
238+
write_wb_reg(CSR_CFG_ASID, i, 1, 0);
239+
}
237240
if (bp->hw.target)
238241
regs->csr_prmd &= ~CSR_PRMD_PWE;
239242
break;
@@ -476,12 +479,15 @@ void breakpoint_handler(struct pt_regs *regs)
476479
slots = this_cpu_ptr(bp_on_reg);
477480

478481
for (i = 0; i < boot_cpu_data.watch_ireg_count; ++i) {
479-
bp = slots[i];
480-
if (bp == NULL)
481-
continue;
482-
perf_bp_event(bp, regs);
482+
if ((csr_read32(LOONGARCH_CSR_FWPS) & (0x1 << i))) {
483+
bp = slots[i];
484+
if (bp == NULL)
485+
continue;
486+
perf_bp_event(bp, regs);
487+
csr_write32(0x1 << i, LOONGARCH_CSR_FWPS);
488+
update_bp_registers(regs, 0, 0);
489+
}
483490
}
484-
update_bp_registers(regs, 0, 0);
485491
}
486492
NOKPROBE_SYMBOL(breakpoint_handler);
487493

@@ -493,12 +499,15 @@ void watchpoint_handler(struct pt_regs *regs)
493499
slots = this_cpu_ptr(wp_on_reg);
494500

495501
for (i = 0; i < boot_cpu_data.watch_dreg_count; ++i) {
496-
wp = slots[i];
497-
if (wp == NULL)
498-
continue;
499-
perf_bp_event(wp, regs);
502+
if ((csr_read32(LOONGARCH_CSR_MWPS) & (0x1 << i))) {
503+
wp = slots[i];
504+
if (wp == NULL)
505+
continue;
506+
perf_bp_event(wp, regs);
507+
csr_write32(0x1 << i, LOONGARCH_CSR_MWPS);
508+
update_bp_registers(regs, 0, 1);
509+
}
500510
}
501-
update_bp_registers(regs, 0, 1);
502511
}
503512
NOKPROBE_SYMBOL(watchpoint_handler);
504513

0 commit comments

Comments
 (0)