]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
LoongArch: Extend the maximum number of watchpoints
authorTiezhu Yang <yangtiezhu@loongson.cn>
Sun, 26 Jan 2025 13:49:59 +0000 (21:49 +0800)
committerHuacai Chen <chenhuacai@loongson.cn>
Sun, 26 Jan 2025 13:49:59 +0000 (21:49 +0800)
The maximum number of load/store watchpoints and fetch instruction
watchpoints is 14 each according to LoongArch Reference Manual, so
extend the maximum number of watchpoints from 8 to 14 for ptrace.

By the way, just simply change 8 to 14 for the definition in struct
user_watch_state at the beginning, but it may corrupt uapi, then add
a new struct user_watch_state_v2 directly.

As far as I can tell, the only users for this struct in the userspace
are GDB and LLDB, there are no any problems of software compatibility
between the application and kernel according to the analysis.

The compatibility problem has been considered while developing and
testing. When the applications in the userspace get watchpoint state,
the length will be specified which is no bigger than the sizeof struct
user_watch_state or user_watch_state_v2, the actual length is assigned
as the minimal value of the application and kernel in the generic code
of ptrace:

kernel/ptrace.c: ptrace_regset():

kiov->iov_len = min(kiov->iov_len,
   (__kernel_size_t) (regset->n * regset->size));

if (req == PTRACE_GETREGSET)
return copy_regset_to_user(task, view, regset_no, 0,
  kiov->iov_len, kiov->iov_base);
else
return copy_regset_from_user(task, view, regset_no, 0,
  kiov->iov_len, kiov->iov_base);

For example, there are four kind of combinations, all of them work well.

(1) "older kernel + older gdb", the actual length is 8+(8+8+4+4)*8=200;
(2) "newer kernel + newer gdb", the actual length is 8+(8+8+4+4)*14=344;
(3) "older kernel + newer gdb", the actual length is 8+(8+8+4+4)*8=200;
(4) "newer kernel + older gdb", the actual length is 8+(8+8+4+4)*8=200.

Link: https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html#control-and-status-registers-related-to-watchpoints
Cc: stable@vger.kernel.org
Fixes: 1a69f7a161a7 ("LoongArch: ptrace: Expose hardware breakpoints to debuggers")
Reviewed-by: WANG Xuerui <git@xen0n.name>
Reviewed-by: Xi Ruoyao <xry111@xry111.site>
Signed-off-by: Tiezhu Yang <yangtiezhu@loongson.cn>
Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
arch/loongarch/include/uapi/asm/ptrace.h
arch/loongarch/kernel/ptrace.c

index ac915f84165053964105383889e4d8f48c72f741..aafb3cd9e943e5a32150040f647b721365b3a60e 100644 (file)
@@ -72,6 +72,16 @@ struct user_watch_state {
        } dbg_regs[8];
 };
 
+struct user_watch_state_v2 {
+       uint64_t dbg_info;
+       struct {
+               uint64_t    addr;
+               uint64_t    mask;
+               uint32_t    ctrl;
+               uint32_t    pad;
+       } dbg_regs[14];
+};
+
 #define PTRACE_SYSEMU                  0x1f
 #define PTRACE_SYSEMU_SINGLESTEP       0x20
 
index 19dc6eff45ccc84a1f3ab198aa22a3541f36a923..5e2402cfcab0a1370b2f21901bd46f02489ad477 100644 (file)
@@ -720,7 +720,7 @@ static int hw_break_set(struct task_struct *target,
        unsigned int note_type = regset->core_note_type;
 
        /* Resource info */
-       offset = offsetof(struct user_watch_state, dbg_regs);
+       offset = offsetof(struct user_watch_state_v2, dbg_regs);
        user_regset_copyin_ignore(&pos, &count, &kbuf, &ubuf, 0, offset);
 
        /* (address, mask, ctrl) registers */
@@ -920,7 +920,7 @@ static const struct user_regset loongarch64_regsets[] = {
 #ifdef CONFIG_HAVE_HW_BREAKPOINT
        [REGSET_HW_BREAK] = {
                .core_note_type = NT_LOONGARCH_HW_BREAK,
-               .n = sizeof(struct user_watch_state) / sizeof(u32),
+               .n = sizeof(struct user_watch_state_v2) / sizeof(u32),
                .size = sizeof(u32),
                .align = sizeof(u32),
                .regset_get = hw_break_get,
@@ -928,7 +928,7 @@ static const struct user_regset loongarch64_regsets[] = {
        },
        [REGSET_HW_WATCH] = {
                .core_note_type = NT_LOONGARCH_HW_WATCH,
-               .n = sizeof(struct user_watch_state) / sizeof(u32),
+               .n = sizeof(struct user_watch_state_v2) / sizeof(u32),
                .size = sizeof(u32),
                .align = sizeof(u32),
                .regset_get = hw_break_get,