diff options
author | Hui Li <lihui@loongson.cn> | 2024-06-21 10:18:40 +0800 |
---|---|---|
committer | Huacai Chen <chenhuacai@loongson.cn> | 2024-06-21 10:18:40 +0800 |
commit | c8e57ab0995c5b443d3c81c8a36b588776dcd0c3 (patch) | |
tree | 510c1640f58742bbb37aa89ec9b62b2ff039c852 /arch/loongarch/kernel/ptrace.c | |
parent | f63a47b34b140ed1ca39d7e4bd4f1cdc617fc316 (diff) | |
download | linux-stable-c8e57ab0995c5b443d3c81c8a36b588776dcd0c3.tar.gz linux-stable-c8e57ab0995c5b443d3c81c8a36b588776dcd0c3.tar.bz2 linux-stable-c8e57ab0995c5b443d3c81c8a36b588776dcd0c3.zip |
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: stable@vger.kernel.org
Signed-off-by: Hui Li <lihui@loongson.cn>
Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
Diffstat (limited to 'arch/loongarch/kernel/ptrace.c')
-rw-r--r-- | arch/loongarch/kernel/ptrace.c | 15 |
1 files changed, 12 insertions, 3 deletions
diff --git a/arch/loongarch/kernel/ptrace.c b/arch/loongarch/kernel/ptrace.c index 16b756c6049b..200109de1971 100644 --- a/arch/loongarch/kernel/ptrace.c +++ b/arch/loongarch/kernel/ptrace.c @@ -608,9 +608,14 @@ static int ptrace_hbp_set_ctrl(unsigned int note_type, return -EINVAL; } - err = ptrace_hbp_fill_attr_ctrl(note_type, ctrl, &attr); - if (err) - return err; + if (uctrl & CTRL_PLV_ENABLE) { + err = ptrace_hbp_fill_attr_ctrl(note_type, ctrl, &attr); + if (err) + return err; + attr.disabled = 0; + } else { + attr.disabled = 1; + } return modify_user_hw_breakpoint(bp, &attr); } @@ -641,6 +646,10 @@ static int ptrace_hbp_set_addr(unsigned int note_type, struct perf_event *bp; struct perf_event_attr attr; + /* Kernel-space address cannot be monitored by user-space */ + if ((unsigned long)addr >= XKPRANGE) + return -EINVAL; + bp = ptrace_hbp_get_initialised_bp(note_type, tsk, idx); if (IS_ERR(bp)) return PTR_ERR(bp); |