• Hui Li's avatar
    LoongArch: Trigger user-space watchpoints correctly · c8e57ab0
    Hui Li authored
    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: default avatarHui Li <lihui@loongson.cn>
    Signed-off-by: default avatarHuacai Chen <chenhuacai@loongson.cn>
    c8e57ab0
ptrace.c 25.6 KB