public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH v2 0/2] gdb: LoongArch: Add support for hardware watchpoint & breakpoint
@ 2024-06-11 11:21 Hui Li
  2024-06-11 11:21 ` [PATCH v2 1/2] gdb: LoongArch: Add support for hardware watchpoint Hui Li
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Hui Li @ 2024-06-11 11:21 UTC (permalink / raw)
  To: gdb-patches; +Cc: Tiezhu Yang

LoongArch defines hardware watchpoint functions for fetch and load/store
operations. After the software configures the watchpoints for fetch and
load/store, the processor hardware will monitor the access addresses of
the fetch and load/store operations and trigger a watchpoint exception
when the watchpoint setting conditions are met.

After this series, watch/rwatch/awatch/hbreak command are supported.

Refer to the following document for hardware watchpoint and breakpoint:
https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html#control-and-status-registers-related-to-watchpoints

Changes v1 -> v2: 
- Fixed some code format.
- Use PLV3 privilege level in ctrl register to enable hardware breakpoint/watchpoint. 

Hui Li (2):
  gdb: LoongArch: Add support for hardware watchpoint
  gdb: LoongArch: Add support for hardware breakpoint

 gdb/Makefile.in                    |   3 +
 gdb/configure.nat                  |   4 +-
 gdb/loongarch-linux-nat.c          | 351 +++++++++++++++++++++++++++++
 gdb/loongarch-tdep.c               |   1 +
 gdb/nat/loongarch-hw-point.c       | 320 ++++++++++++++++++++++++++
 gdb/nat/loongarch-hw-point.h       | 103 +++++++++
 gdb/nat/loongarch-linux-hw-point.c | 252 +++++++++++++++++++++
 gdb/nat/loongarch-linux-hw-point.h | 126 +++++++++++
 gdb/nat/loongarch-linux.c          | 100 ++++++++
 gdb/nat/loongarch-linux.h          |  42 ++++
 include/elf/common.h               |   4 +
 11 files changed, 1305 insertions(+), 1 deletion(-)
 create mode 100644 gdb/nat/loongarch-hw-point.c
 create mode 100644 gdb/nat/loongarch-hw-point.h
 create mode 100644 gdb/nat/loongarch-linux-hw-point.c
 create mode 100644 gdb/nat/loongarch-linux-hw-point.h
 create mode 100644 gdb/nat/loongarch-linux.c
 create mode 100644 gdb/nat/loongarch-linux.h

-- 
2.38.1


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH v2 1/2] gdb: LoongArch: Add support for hardware watchpoint
  2024-06-11 11:21 [PATCH v2 0/2] gdb: LoongArch: Add support for hardware watchpoint & breakpoint Hui Li
@ 2024-06-11 11:21 ` Hui Li
  2024-06-11 11:21 ` [PATCH v2 2/2] gdb: LoongArch: Add support for hardware breakpoint Hui Li
  2024-06-24 21:55 ` [PATCH v2 0/2] gdb: LoongArch: Add support for hardware watchpoint & breakpoint Tiezhu Yang
  2 siblings, 0 replies; 4+ messages in thread
From: Hui Li @ 2024-06-11 11:21 UTC (permalink / raw)
  To: gdb-patches; +Cc: Tiezhu Yang

LoongArch defines hardware watchpoint functions for load/store
operations. After the software configures the watchpoints for
load/store, the processor hardware will monitor the access
addresses of the load/store operations and trigger watchpoint
exception when the watchpoint setting conditions are met.

After this patch, watch/rwatch/awatch command are supported. Refer to the
following document for hardware watchpoint.
https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html#control-and-status-registers-related-to-watchpoints

A simple test is as follows:

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

without this patch:

lihui@bogon:~$ gdb test
...
(gdb) start
...
Temporary breakpoint 1, main () at test.c:5
5               printf("start test\n");
(gdb) awatch a
Target does not support this type of hardware watchpoint.
...

with this patch:

lihui@bogon:~$ gdb test
...
(gdb) start
...
Temporary breakpoint 1, main () at test.c:5
5               printf("start test\n");
(gdb) awatch a
Hardware access (read/write) watchpoint 2: a
(gdb) c
Continuing.
start test

Hardware access (read/write) watchpoint 2: a

Old value = 0
New value = 1
main () at test.c:7
7               printf("a = %d\n", a);
(gdb) c
Continuing.

Hardware access (read/write) watchpoint 2: a

Value = 1
0x00000001200006e0 in main () at test.c:7
7               printf("a = %d\n", a);
(gdb) c
Continuing.
a = 1
end test
[Inferior 1 (process 22250) exited normally]

Signed-off-by: Hui Li <lihui@loongson.cn>
---
 gdb/Makefile.in                    |   3 +
 gdb/configure.nat                  |   4 +-
 gdb/loongarch-linux-nat.c          | 279 +++++++++++++++++++++++++++
 gdb/loongarch-tdep.c               |   1 +
 gdb/nat/loongarch-hw-point.c       | 293 +++++++++++++++++++++++++++++
 gdb/nat/loongarch-hw-point.h       |  92 +++++++++
 gdb/nat/loongarch-linux-hw-point.c | 227 ++++++++++++++++++++++
 gdb/nat/loongarch-linux-hw-point.h | 125 ++++++++++++
 gdb/nat/loongarch-linux.c          |  87 +++++++++
 gdb/nat/loongarch-linux.h          |  42 +++++
 include/elf/common.h               |   2 +
 11 files changed, 1154 insertions(+), 1 deletion(-)
 create mode 100644 gdb/nat/loongarch-hw-point.c
 create mode 100644 gdb/nat/loongarch-hw-point.h
 create mode 100644 gdb/nat/loongarch-linux-hw-point.c
 create mode 100644 gdb/nat/loongarch-linux-hw-point.h
 create mode 100644 gdb/nat/loongarch-linux.c
 create mode 100644 gdb/nat/loongarch-linux.h

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 3752bdff79b..a7a66b121b2 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -1601,6 +1601,9 @@ HFILES_NO_SRCDIR = \
 	nat/linux-personality.h \
 	nat/linux-ptrace.h \
 	nat/linux-waitpid.h \
+	nat/loongarch-hw-point.h \
+	nat/loongarch-linux.h \
+	nat/loongarch-linux-hw-point.h \
 	nat/mips-linux-watch.h \
 	nat/ppc-linux.h \
 	nat/x86-cpuid.h \
diff --git a/gdb/configure.nat b/gdb/configure.nat
index 8b98511cef7..1bc070ef8b4 100644
--- a/gdb/configure.nat
+++ b/gdb/configure.nat
@@ -264,7 +264,9 @@ case ${gdb_host} in
 		;;
 	    loongarch)
 		# Host: LoongArch, running GNU/Linux.
-		NATDEPFILES="${NATDEPFILES} loongarch-linux-nat.o linux-nat-trad.o"
+		NATDEPFILES="${NATDEPFILES} loongarch-linux-nat.o linux-nat-trad.o \
+		nat/loongarch-hw-point.o nat/loongarch-linux.o \
+		nat/loongarch-linux-hw-point.o"
 		;;
 	    m32r)
 		# Host: M32R based machine running GNU/Linux
diff --git a/gdb/loongarch-linux-nat.c b/gdb/loongarch-linux-nat.c
index 15fca6ad781..9f5760066b3 100644
--- a/gdb/loongarch-linux-nat.c
+++ b/gdb/loongarch-linux-nat.c
@@ -24,6 +24,9 @@
 #include "linux-nat-trad.h"
 #include "loongarch-tdep.h"
 #include "nat/gdb_ptrace.h"
+#include "nat/loongarch-hw-point.h"
+#include "nat/loongarch-linux.h"
+#include "nat/loongarch-linux-hw-point.h"
 #include "target-descriptions.h"
 
 #include <asm/ptrace.h>
@@ -37,6 +40,37 @@ class loongarch_linux_nat_target final : public linux_nat_trad_target
   void fetch_registers (struct regcache *, int) override;
   void store_registers (struct regcache *, int) override;
 
+  int can_use_hw_breakpoint (enum bptype type, int cnt, int othertype) override;
+  int region_ok_for_hw_watchpoint (CORE_ADDR addr, int len) override;
+
+  int insert_watchpoint (CORE_ADDR addr, int len, enum target_hw_bp_type type,
+			 struct expression *cond) override;
+  int remove_watchpoint (CORE_ADDR addr, int len, enum target_hw_bp_type type,
+			 struct expression *cond) override;
+  bool watchpoint_addr_within_range (CORE_ADDR addr, CORE_ADDR start,
+				     int length) override;
+
+  /* Add our hardware breakpoint and watchpoint implementation.  */
+  bool stopped_by_watchpoint () override;
+  bool stopped_data_address (CORE_ADDR *) override;
+
+  /* Override the GNU/Linux inferior startup hook.  */
+  void post_startup_inferior (ptid_t) override;
+
+  /* Override the GNU/Linux post attach hook.  */
+  void post_attach (int pid) override;
+
+  /* These three defer to common nat/ code.  */
+  void low_new_thread (struct lwp_info *lp) override
+  { loongarch_linux_new_thread (lp); }
+  void low_delete_thread (struct arch_lwp_info *lp) override
+  { loongarch_linux_delete_thread (lp); }
+  void low_prepare_to_resume (struct lwp_info *lp) override
+  { loongarch_linux_prepare_to_resume (lp); }
+
+  void low_new_fork (struct lwp_info *parent, pid_t child_pid) override;
+  void low_forget_process (pid_t pid) override;
+
 protected:
   /* Override linux_nat_trad_target methods.  */
   CORE_ADDR register_u_offset (struct gdbarch *gdbarch, int regnum,
@@ -406,6 +440,251 @@ fill_fpregset (const struct regcache *regcache, gdb_fpregset_t *fpregset,
 				     sizeof (gdb_fpregset_t));
 }
 
+/* Helper for the "stopped_data_address" target method.  Returns TRUE
+   if a hardware watchpoint trap at ADDR_TRAP matches a set watchpoint.
+   The address of the matched watchpoint is returned in *ADDR_P.  */
+
+static bool
+loongarch_stopped_data_address (const struct loongarch_debug_reg_state *state,
+			      CORE_ADDR addr_trap, CORE_ADDR *addr_p)
+{
+
+  int i;
+
+  for (i = loongarch_num_wp_regs - 1; i >= 0; --i)
+    {
+      const CORE_ADDR addr_watch = state->dr_addr_wp[i];
+
+      if (state->dr_ref_count_wp[i]
+	  && DR_CONTROL_ENABLED (state->dr_ctrl_wp[i])
+	  && addr_trap == addr_watch)
+	{
+	  *addr_p = addr_watch;
+	  return true;
+	}
+    }
+  return false;
+}
+
+
+/* Returns the number of hardware watchpoints of type TYPE that we can
+   set.  Value is positive if we can set CNT watchpoints, zero if
+   setting watchpoints of type TYPE is not supported, and negative if
+   CNT is more than the maximum number of watchpoints of type TYPE
+   that we can support.  TYPE is one of bp_hardware_watchpoint,
+   bp_read_watchpoint, bp_write_watchpoint, or bp_hardware_breakpoint.
+   CNT is the number of such watchpoints used so far (including this
+   one).  OTHERTYPE is non-zero if other types of watchpoints are
+   currently enabled.  */
+
+int
+loongarch_linux_nat_target::can_use_hw_breakpoint (enum bptype type, int cnt,
+						   int othertype)
+{
+  if (type == bp_hardware_watchpoint || type == bp_read_watchpoint
+      || type == bp_access_watchpoint || type == bp_watchpoint)
+    {
+      if (loongarch_num_wp_regs == 0)
+	return 0;
+    }
+  else if (type == bp_hardware_breakpoint)
+    {
+      return 0;
+    }
+  else
+    gdb_assert_not_reached ("unexpected breakpoint type");
+
+  /* We always return 1 here because we don't have enough information
+     about possible overlap of addresses that they want to watch.  As an
+     extreme example, consider the case where all the watchpoints watch
+     the same address and the same region length: then we can handle a
+     virtually unlimited number of watchpoints, due to debug register
+     sharing implemented via reference counts.  */
+  return 1;
+
+}
+
+int
+loongarch_linux_nat_target::region_ok_for_hw_watchpoint (CORE_ADDR addr,
+							int len)
+{
+  return loongarch_region_ok_for_watchpoint (addr, len);
+}
+
+/* Insert a watchpoint to watch a memory region which starts at
+   address ADDR and whose length is LEN bytes.  Watch memory accesses
+   of the type TYPE.  Return 0 on success, -1 on failure.  */
+
+int
+loongarch_linux_nat_target::insert_watchpoint (CORE_ADDR addr, int len,
+					      enum target_hw_bp_type type,
+					      struct expression *cond)
+{
+  int ret;
+  struct loongarch_debug_reg_state *state
+	= loongarch_get_debug_reg_state (inferior_ptid.pid ());
+
+  if (show_debug_regs)
+    gdb_printf (gdb_stdlog,
+		"insert_watchpoint on entry (addr=0x%08lx, len=%d)\n",
+		(unsigned long) addr, len);
+
+  gdb_assert (type != hw_execute);
+
+  ret = loongarch_handle_watchpoint (type, addr, len, 1 /* is_insert */,
+				     inferior_ptid, state);
+
+  if (show_debug_regs)
+    {
+      loongarch_show_debug_reg_state (state,
+				      "insert_watchpoint", addr, len, type);
+    }
+
+  return ret;
+
+}
+
+/* Remove a watchpoint that watched the memory region which starts at
+   address ADDR, whose length is LEN bytes, and for accesses of the
+   type TYPE.  Return 0 on success, -1 on failure.  */
+
+int
+loongarch_linux_nat_target::remove_watchpoint (CORE_ADDR addr, int len,
+					      enum target_hw_bp_type type,
+					      struct expression *cond)
+{
+  int ret;
+  struct loongarch_debug_reg_state *state
+	= loongarch_get_debug_reg_state (inferior_ptid.pid ());
+
+  if (show_debug_regs)
+    gdb_printf (gdb_stdlog,
+		"remove_watchpoint on entry (addr=0x%08lx, len=%d)\n",
+		(unsigned long) addr, len);
+
+  gdb_assert (type != hw_execute);
+
+  ret = loongarch_handle_watchpoint (type, addr, len, 0 /* is_insert */,
+				     inferior_ptid, state);
+
+  if (show_debug_regs)
+    {
+      loongarch_show_debug_reg_state (state,
+				      "remove_watchpoint", addr, len, type);
+    }
+
+  return ret;
+
+}
+
+bool
+loongarch_linux_nat_target::watchpoint_addr_within_range (CORE_ADDR addr,
+							  CORE_ADDR start,
+							  int length)
+{
+  return start <= addr && start + length - 1 >= addr;
+}
+
+
+/* Implement the "stopped_data_address" target_ops method.  */
+
+bool
+loongarch_linux_nat_target::stopped_data_address (CORE_ADDR *addr_p)
+{
+  siginfo_t siginfo;
+  struct loongarch_debug_reg_state *state;
+
+  if (!linux_nat_get_siginfo (inferior_ptid, &siginfo))
+    return false;
+
+  /* This must be a hardware breakpoint.  */
+  if (siginfo.si_signo != SIGTRAP || (siginfo.si_code & 0xffff) != TRAP_HWBKPT)
+    return false;
+
+  /* Make sure to ignore the top byte, otherwise we may not recognize a
+     hardware watchpoint hit.  The stopped data addresses coming from the
+     kernel can potentially be tagged addresses.  */
+  struct gdbarch *gdbarch = thread_architecture (inferior_ptid);
+  const CORE_ADDR addr_trap
+    = gdbarch_remove_non_address_bits (gdbarch, (CORE_ADDR) siginfo.si_addr);
+
+  /* Check if the address matches any watched address.  */
+  state = loongarch_get_debug_reg_state (inferior_ptid.pid ());
+
+  return loongarch_stopped_data_address (state, addr_trap, addr_p);
+}
+
+/* Implement the "stopped_by_watchpoint" target_ops method.  */
+
+bool
+loongarch_linux_nat_target::stopped_by_watchpoint ()
+{
+  CORE_ADDR addr;
+
+  return stopped_data_address (&addr);
+}
+
+/* Implement the virtual inf_ptrace_target::post_startup_inferior method.  */
+
+void
+loongarch_linux_nat_target::post_startup_inferior (ptid_t ptid)
+{
+  low_forget_process (ptid.pid ());
+  loongarch_linux_get_debug_reg_capacity (ptid.pid ());
+  linux_nat_target::post_startup_inferior (ptid);
+}
+
+/* Implement the "post_attach" target_ops method.  */
+
+void
+loongarch_linux_nat_target::post_attach (int pid)
+{
+  low_forget_process (pid);
+  /* Get the hardware debug register capacity. If
+     loongarch_linux_get_debug_reg_capacity is not called
+     (as it is in loongarch_linux_child_post_startup_inferior) then
+     software watchpoints will be used instead of hardware
+     watchpoints when attaching to a target.  */
+  loongarch_linux_get_debug_reg_capacity (pid);
+  linux_nat_target::post_attach (pid);
+}
+
+/* linux_nat_new_fork hook.   */
+
+void
+loongarch_linux_nat_target::low_new_fork (struct lwp_info *parent,
+					  pid_t child_pid)
+{
+  pid_t parent_pid;
+  struct loongarch_debug_reg_state *parent_state;
+  struct loongarch_debug_reg_state *child_state;
+
+  /* NULL means no watchpoint has ever been set in the parent.  In
+     that case, there's nothing to do.  */
+  if (parent->arch_private == NULL)
+    return;
+
+  /* GDB core assumes the child inherits the watchpoints/hw
+     breakpoints of the parent, and will remove them all from the
+     forked off process.  Copy the debug registers mirrors into the
+     new process so that all breakpoints and watchpoints can be
+     removed together.  */
+
+  parent_pid = parent->ptid.pid ();
+  parent_state = loongarch_get_debug_reg_state (parent_pid);
+  child_state = loongarch_get_debug_reg_state (child_pid);
+  *child_state = *parent_state;
+}
+
+/* Called whenever GDB is no longer debugging process PID.  It deletes
+   data structures that keep track of debug register state.  */
+
+void
+loongarch_linux_nat_target::low_forget_process (pid_t pid)
+{
+  loongarch_remove_debug_reg_state (pid);
+}
+
 /* Initialize LoongArch Linux native support.  */
 
 void _initialize_loongarch_linux_nat ();
diff --git a/gdb/loongarch-tdep.c b/gdb/loongarch-tdep.c
index af0d6896143..757f4acc489 100644
--- a/gdb/loongarch-tdep.c
+++ b/gdb/loongarch-tdep.c
@@ -1869,6 +1869,7 @@ loongarch_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
   set_gdbarch_software_single_step (gdbarch, loongarch_software_single_step);
   set_gdbarch_breakpoint_kind_from_pc (gdbarch, loongarch_breakpoint::kind_from_pc);
   set_gdbarch_sw_breakpoint_from_kind (gdbarch, loongarch_breakpoint::bp_from_kind);
+  set_gdbarch_have_nonsteppable_watchpoint (gdbarch, 1);
 
   /* Frame unwinders. Use DWARF debug info if available, otherwise use our own unwinder.  */
   set_gdbarch_dwarf2_reg_to_regnum (gdbarch, loongarch_dwarf2_reg_to_regnum);
diff --git a/gdb/nat/loongarch-hw-point.c b/gdb/nat/loongarch-hw-point.c
new file mode 100644
index 00000000000..44af61abd00
--- /dev/null
+++ b/gdb/nat/loongarch-hw-point.c
@@ -0,0 +1,293 @@
+/* Native-dependent code for GNU/Linux on LoongArch processors.
+
+   Copyright (C) 2024 Free Software Foundation, Inc.
+   Contributed by Loongson Ltd.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "gdbsupport/common-defs.h"
+#include "gdbsupport/break-common.h"
+#include "gdbsupport/common-regcache.h"
+#include "loongarch-hw-point.h"
+#include "loongarch-linux-hw-point.h"
+
+/* Number of hardware breakpoints/watchpoints the target supports.
+   They are initialized with values obtained via ptrace.  */
+
+int loongarch_num_wp_regs;
+
+/* Given the hardware breakpoint or watchpoint type TYPE and its
+   length LEN, return the expected encoding for a hardware
+   breakpoint/watchpoint control register.  */
+
+static unsigned int
+loongarch_point_encode_ctrl_reg (enum target_hw_bp_type type, int len)
+{
+  unsigned int ctrl, ttype, llen;
+
+  gdb_assert (len <= LOONGARCH_HWP_MAX_LEN_PER_REG);
+
+  /* type */
+  switch (type)
+    {
+    case hw_write:
+      ttype = 2;
+      break;
+    case hw_read:
+      ttype = 1;
+      break;
+    case hw_access:
+      ttype = 3;
+      break;
+    case hw_execute:
+      ttype = 0;
+      break;
+    default:
+      perror_with_name (_("Unrecognized watchpoint type"));
+    }
+
+  /* len */
+  switch (len)
+    {
+    case 1:
+      llen = 0b11;
+      break;
+    case 2:
+      llen = 0b10;
+      break;
+    case 4:
+      llen = 0b01;
+      break;
+    case 8:
+      llen = 0b00;
+      break;
+    default:
+      perror_with_name (_("Unrecognized watchpoint length"));
+    }
+  ctrl = 0;
+  if (type != hw_execute) {
+    /*  type and length bitmask */
+    ctrl |= llen << 10;
+    ctrl |= ttype << 8;
+  }
+  ctrl |= CTRL_PLV3_ENABLE;
+  return ctrl;
+}
+
+
+/* Record the insertion of one breakpoint/watchpoint, as represented
+   by ADDR and CTRL, in the process' arch-specific data area *STATE.  */
+
+static int
+loongarch_dr_state_insert_one_point (ptid_t ptid,
+				   struct loongarch_debug_reg_state *state,
+				   enum target_hw_bp_type type, CORE_ADDR addr,
+				   int len, CORE_ADDR addr_orig)
+{
+  int i, idx, num_regs, is_watchpoint;
+  unsigned int ctrl, *dr_ctrl_p, *dr_ref_count;
+  CORE_ADDR *dr_addr_p;
+
+  /* Set up state pointers.  */
+  is_watchpoint = (type != hw_execute);
+  if (is_watchpoint)
+    {
+      num_regs = loongarch_num_wp_regs;
+      dr_addr_p = state->dr_addr_wp;
+      dr_ctrl_p = state->dr_ctrl_wp;
+      dr_ref_count = state->dr_ref_count_wp;
+    }
+  else
+    {
+      return -1;
+    }
+
+  ctrl = loongarch_point_encode_ctrl_reg (type, len);
+
+  /* Find an existing or free register in our cache.  */
+  idx = -1;
+  for (i = 0; i < num_regs; ++i)
+    {
+      if ((dr_ctrl_p[i] & CTRL_PLV3_ENABLE) == 0) // PLV3 disable
+	{
+	  gdb_assert (dr_ref_count[i] == 0);
+	  idx = i;
+	  /* no break; continue hunting for an exising one.  */
+	}
+      else if (dr_addr_p[i] == addr && dr_ctrl_p[i] == ctrl)
+	{
+	  idx = i;
+	  gdb_assert (dr_ref_count[i] != 0);
+	  break;
+	}
+
+    }
+
+  /* No space.  */
+  if (idx == -1)
+    return -1;
+
+  /* Update our cache.  */
+  if ((dr_ctrl_p[idx] & CTRL_PLV3_ENABLE) == 0)
+    {
+      /* new entry */
+      dr_addr_p[idx] = addr;
+      dr_ctrl_p[idx] = ctrl;
+      dr_ref_count[idx] = 1;
+
+      /* Notify the change.  */
+      loongarch_notify_debug_reg_change (ptid, is_watchpoint, idx);
+    }
+  else
+    {
+      /* existing entry */
+      dr_ref_count[idx]++;
+    }
+
+  return 0;
+}
+
+/* Record the removal of one breakpoint/watchpoint, as represented by
+   ADDR and CTRL, in the process' arch-specific data area *STATE.  */
+
+static int
+loongarch_dr_state_remove_one_point (ptid_t ptid,
+				   struct loongarch_debug_reg_state *state,
+				   enum target_hw_bp_type type, CORE_ADDR addr,
+				   int len, CORE_ADDR addr_orig)
+{
+  int i, num_regs, is_watchpoint;
+  unsigned int ctrl, *dr_ctrl_p, *dr_ref_count;
+  CORE_ADDR *dr_addr_p;
+
+  /* Set up state pointers.  */
+  is_watchpoint = (type != hw_execute);
+  if (is_watchpoint)
+    {
+      num_regs = loongarch_num_wp_regs;
+      dr_addr_p = state->dr_addr_wp;
+      dr_ctrl_p = state->dr_ctrl_wp;
+      dr_ref_count = state->dr_ref_count_wp;
+    }
+  else
+    {
+      return -1;
+    }
+
+  ctrl = loongarch_point_encode_ctrl_reg (type, len);
+
+  /* Find the entry that matches the ADDR and CTRL.  */
+  for (i = 0; i < num_regs; ++i)
+    if (dr_addr_p[i] == addr && dr_ctrl_p[i] == ctrl)
+      {
+	gdb_assert (dr_ref_count[i] != 0);
+	break;
+      }
+
+  /* Not found.  */
+  if (i == num_regs)
+    return -1;
+
+  /* Clear our cache.  */
+  if (--dr_ref_count[i] == 0)
+    {
+      dr_addr_p[i] = 0;
+      dr_ctrl_p[i] = 0;
+
+      /* Notify the change.  */
+      loongarch_notify_debug_reg_change (ptid, is_watchpoint, i);
+    }
+
+  return 0;
+}
+
+int
+loongarch_handle_watchpoint (enum target_hw_bp_type type, CORE_ADDR addr,
+			     int len, int is_insert, ptid_t ptid,
+			     struct loongarch_debug_reg_state *state)
+{
+  if (is_insert)
+    return loongarch_dr_state_insert_one_point (ptid, state, type, addr,
+						len, addr);
+  else
+    return loongarch_dr_state_remove_one_point (ptid, state, type, addr,
+						len, addr);
+}
+
+
+/* See nat/loongarch-hw-point.h.  */
+
+bool
+loongarch_any_set_debug_regs_state (loongarch_debug_reg_state *state,
+				    bool watchpoint)
+{
+  int count = watchpoint ? loongarch_num_wp_regs : 0;
+  if (count == 0)
+    return false;
+
+  const CORE_ADDR *addr = watchpoint ? state->dr_addr_wp : 0;
+  const unsigned int *ctrl = watchpoint ? state->dr_ctrl_wp : 0;
+
+  for (int i = 0; i < count; i++)
+    if (addr[i] != 0 || ctrl[i] != 0)
+      return true;
+
+  return false;
+}
+
+/* Print the values of the cached breakpoint/watchpoint registers.  */
+
+void
+loongarch_show_debug_reg_state (struct loongarch_debug_reg_state *state,
+				const char *func, CORE_ADDR addr,
+				int len, enum target_hw_bp_type type)
+{
+  int i;
+
+  debug_printf ("%s", func);
+  if (addr || len)
+    debug_printf (" (addr=0x%08lx, len=%d, type=%s)",
+		  (unsigned long) addr, len,
+		  type == hw_write ? "hw-write-watchpoint"
+		  : (type == hw_read ? "hw-read-watchpoint"
+		     : (type == hw_access ? "hw-access-watchpoint"
+			: (type == hw_execute ? "hw-breakpoint"
+			   : "??unknown??"))));
+  debug_printf (":\n");
+
+  debug_printf ("\tWATCHPOINTs:\n");
+  for (i = 0; i < loongarch_num_wp_regs; i++)
+    debug_printf ("\tWP%d: addr=%s, ctrl=0x%08x, ref.count=%d\n",
+		  i, core_addr_to_string_nz (state->dr_addr_wp[i]),
+		  state->dr_ctrl_wp[i], state->dr_ref_count_wp[i]);
+}
+
+/* Return true if we can watch a memory region that starts address
+   ADDR and whose length is LEN in bytes.  */
+
+int
+loongarch_region_ok_for_watchpoint (CORE_ADDR addr, int len)
+{
+  /* Can not set watchpoints for zero or negative lengths.  */
+  if (len <= 0)
+    return 0;
+
+  /* Must have hardware watchpoint debug register(s).  */
+  if (loongarch_num_wp_regs == 0)
+    return 0;
+
+  return 1;
+}
diff --git a/gdb/nat/loongarch-hw-point.h b/gdb/nat/loongarch-hw-point.h
new file mode 100644
index 00000000000..86e5aa3f452
--- /dev/null
+++ b/gdb/nat/loongarch-hw-point.h
@@ -0,0 +1,92 @@
+/* Native-dependent code for GNU/Linux on LoongArch processors.
+
+   Copyright (C) 2024 Free Software Foundation, Inc.
+   Contributed by Loongson Ltd.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef NAT_LOONGARCH_HW_POINT_H
+#define NAT_LOONGARCH_HW_POINT_H
+
+/* Macro definitions, data structures, and code for the hardware
+   breakpoint and hardware watchpoint support follow.  We use the
+   following abbreviations throughout the code:
+
+   hw - hardware
+   bp - breakpoint
+   wp - watchpoint  */
+
+/* Maximum number of hardware breakpoint and watchpoint registers.
+   Neither of these values may exceed the width of dr_changed_t
+   measured in bits.  */
+
+#define LOONGARCH_HWP_MAX_NUM 8
+
+
+/* The maximum length of a memory region that can be watched by one
+   hardware watchpoint register.  */
+
+#define LOONGARCH_HWP_MAX_LEN_PER_REG 8
+#define CTRL_PLV3_ENABLE		0x10
+
+#define DR_CONTROL_ENABLED(ctrl)  ((ctrl & CTRL_PLV3_ENABLE) == CTRL_PLV3_ENABLE)
+
+/* Structure for managing the hardware breakpoint/watchpoint resources.
+   DR_ADDR_* stores the address, DR_CTRL_* stores the control register
+   content, and DR_REF_COUNT_* counts the numbers of references to the
+   corresponding bp/wp, by which way the limited hardware resources
+   are not wasted on duplicated bp/wp settings (though so far gdb has
+   done a good job by not sending duplicated bp/wp requests).  */
+
+struct loongarch_debug_reg_state
+{
+  /* hardware watchpoint */
+  CORE_ADDR dr_addr_wp[LOONGARCH_HWP_MAX_NUM];
+  unsigned int dr_ctrl_wp[LOONGARCH_HWP_MAX_NUM];
+  unsigned int dr_ref_count_wp[LOONGARCH_HWP_MAX_NUM];
+};
+
+extern int loongarch_num_wp_regs;
+
+/* Invoked when IDXth breakpoint/watchpoint register pair needs to be
+   updated.  */
+
+void loongarch_notify_debug_reg_change (ptid_t ptid, int is_watchpoint,
+				      unsigned int idx);
+
+
+int loongarch_handle_watchpoint (enum target_hw_bp_type type, CORE_ADDR addr,
+				 int len, int is_insert, ptid_t ptid,
+				 struct loongarch_debug_reg_state *state);
+
+/* Return TRUE if there are any hardware breakpoints.  If WATCHPOINT is TRUE,
+   check hardware watchpoints instead.  */
+
+bool loongarch_any_set_debug_regs_state (loongarch_debug_reg_state *state,
+					 bool watchpoint);
+
+/* Print the values of the cached breakpoint/watchpoint registers.  */
+
+void loongarch_show_debug_reg_state (struct loongarch_debug_reg_state *state,
+				     const char *func, CORE_ADDR addr,
+				     int len, enum target_hw_bp_type type);
+
+/* Return true if we can watch a memory region that starts address
+   ADDR and whose length is LEN in bytes.  */
+
+int loongarch_region_ok_for_watchpoint (CORE_ADDR addr, int len);
+
+#endif /* NAT_LOONGARCH_HW_POINT_H */
diff --git a/gdb/nat/loongarch-linux-hw-point.c b/gdb/nat/loongarch-linux-hw-point.c
new file mode 100644
index 00000000000..cced5a388ab
--- /dev/null
+++ b/gdb/nat/loongarch-linux-hw-point.c
@@ -0,0 +1,227 @@
+/* Native-dependent code for GNU/Linux on LoongArch processors.
+
+   Copyright (C) 2024 Free Software Foundation, Inc.
+   Contributed by Loongson Ltd.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#include "gdbsupport/common-defs.h"
+#include "gdbsupport/break-common.h"
+#include "gdbsupport/common-regcache.h"
+#include "nat/linux-nat.h"
+#include "loongarch-linux-hw-point.h"
+
+#include <sys/uio.h>
+
+/* The order in which <sys/ptrace.h> and <asm/ptrace.h> are included
+   can be important.  <sys/ptrace.h> often declares various PTRACE_*
+   enums.  <asm/ptrace.h> often defines preprocessor constants for
+   these very same symbols.  When that's the case, build errors will
+   result when <asm/ptrace.h> is included before <sys/ptrace.h>.  */
+
+#include <sys/ptrace.h>
+#include <asm/ptrace.h>
+
+#include "elf/common.h"
+
+/* Hash table storing per-process data.  We don't bind this to a
+   per-inferior registry because of targets like x86 GNU/Linux that
+   need to keep track of processes that aren't bound to any inferior
+   (e.g., fork children, checkpoints).  */
+
+static std::unordered_map<pid_t, loongarch_debug_reg_state>
+loongarch_debug_process_state;
+
+/* See loongarch-linux-hw-point.h  */
+
+/* Helper for loongarch_notify_debug_reg_change.  Records the
+   information about the change of one hardware breakpoint/watchpoint
+   setting for the thread LWP.
+   N.B.  The actual updating of hardware debug registers is not
+   carried out until the moment the thread is resumed.  */
+
+static int
+loongarch_dr_change_callback (struct lwp_info *lwp, int is_watchpoint,
+			      unsigned int idx)
+{
+  int tid = ptid_of_lwp (lwp).lwp ();
+  struct arch_lwp_info *info = lwp_arch_private_info (lwp);
+  dr_changed_t *dr_changed_ptr;
+  dr_changed_t dr_changed;
+
+  if (!is_watchpoint)
+    return -1;
+
+  if (info == NULL)
+    {
+      info = XCNEW (struct arch_lwp_info);
+      lwp_set_arch_private_info (lwp, info);
+    }
+
+  if (show_debug_regs)
+    {
+      debug_printf ("loongarch_dr_change_callback: \n\tOn entry:\n");
+      debug_printf ("\ttid%d, dr_changed_wp=0x%s\n",
+		    tid, phex (info->dr_changed_wp, 8));
+    }
+
+  dr_changed_ptr = &info->dr_changed_wp;
+  dr_changed = *dr_changed_ptr;
+
+  gdb_assert (idx >= 0 && idx <= loongarch_num_wp_regs);
+
+  /* The actual update is done later just before resuming the lwp,
+     we just mark that one register pair needs updating.  */
+  DR_MARK_N_CHANGED (dr_changed, idx);
+  *dr_changed_ptr = dr_changed;
+
+  /* If the lwp isn't stopped, force it to momentarily pause, so
+     we can update its debug registers.  */
+  if (!lwp_is_stopped (lwp))
+    linux_stop_lwp (lwp);
+
+  if (show_debug_regs)
+    {
+      debug_printf ("\tOn exit:\n\ttid%d, dr_changed_wp=0x%s\n",
+		    tid, phex (info->dr_changed_wp, 8));
+    }
+
+  return 0;
+}
+
+/* Notify each thread that their IDXth breakpoint/watchpoint register
+   pair needs to be updated.  The message will be recorded in each
+   thread's arch-specific data area, the actual updating will be done
+   when the thread is resumed.  */
+
+void
+loongarch_notify_debug_reg_change (ptid_t ptid,
+				 int is_watchpoint, unsigned int idx)
+{
+  ptid_t pid_ptid = ptid_t (ptid.pid ());
+
+  iterate_over_lwps (pid_ptid, [=] (struct lwp_info *info)
+			       {
+				 return loongarch_dr_change_callback (info,
+								      is_watchpoint,
+								      idx);
+			       });
+}
+
+/* Call ptrace to set the thread TID's hardware breakpoint/watchpoint
+   registers with data from *STATE.  */
+
+void
+loongarch_linux_set_debug_regs (struct loongarch_debug_reg_state *state,
+			      int tid, int watchpoint)
+{
+  int i, count;
+  struct iovec iov;
+  struct loongarch_user_watch_state regs;
+  const CORE_ADDR *addr;
+  const unsigned int *ctrl;
+
+  memset (&regs, 0, sizeof (regs));
+  iov.iov_base = &regs;
+  count = watchpoint ? loongarch_num_wp_regs : 0;
+  addr = watchpoint ? state->dr_addr_wp : 0;
+  ctrl = watchpoint ? state->dr_ctrl_wp : 0;
+
+  if (count == 0)
+    return;
+
+  iov.iov_len = (offsetof (struct loongarch_user_watch_state, dbg_regs)
+		 + count * sizeof (regs.dbg_regs[0]));
+  for (i = 0; i < count; i++)
+    {
+      regs.dbg_regs[i].addr = addr[i];
+      regs.dbg_regs[i].ctrl = ctrl[i];
+    }
+
+  if (ptrace(PTRACE_SETREGSET, tid, NT_LOONGARCH_HW_WATCH, (void *) &iov))
+    {
+      if (errno == EINVAL)
+	error (_("Invalid argument setting hardware debug registers"));
+      else
+	error (_("Unexpected error setting hardware debug registers"));
+    }
+
+}
+
+/* Get the hardware debug register capacity information from the
+   process represented by TID.  */
+
+void
+loongarch_linux_get_debug_reg_capacity (int tid)
+{
+  struct iovec iov;
+  struct loongarch_user_watch_state dreg_state;
+  int result;
+  iov.iov_base = &dreg_state;
+  iov.iov_len = sizeof (dreg_state);
+
+  /* Get hardware watchpoint register info.  */
+  result = ptrace (PTRACE_GETREGSET, tid, NT_LOONGARCH_HW_WATCH, &iov);
+
+  if (result == 0)
+    {
+      loongarch_num_wp_regs = LOONGARCH_DEBUG_NUM_SLOTS (dreg_state.dbg_info);
+      if (loongarch_num_wp_regs > LOONGARCH_HWP_MAX_NUM)
+	{
+	  warning (_("Unexpected number of hardware watchpoint registers"
+		     " reported by ptrace, got %d, expected %d."),
+		   loongarch_num_wp_regs, LOONGARCH_HWP_MAX_NUM);
+	  loongarch_num_wp_regs = LOONGARCH_HWP_MAX_NUM;
+	}
+    }
+  else
+    {
+      warning (_("Unable to determine the number of hardware watchpoints"
+		 " available."));
+      loongarch_num_wp_regs = 0;
+    }
+
+}
+
+/* Return the debug register state for process PID.  If no existing
+   state is found for this process, return nullptr.  */
+
+struct loongarch_debug_reg_state *
+loongarch_lookup_debug_reg_state (pid_t pid)
+{
+  auto it = loongarch_debug_process_state.find (pid);
+  if (it != loongarch_debug_process_state.end ())
+    return &it->second;
+
+  return nullptr;
+}
+
+/* Return the debug register state for process PID.  If no existing
+   state is found for this process, create new state.  */
+
+struct loongarch_debug_reg_state *
+loongarch_get_debug_reg_state (pid_t pid)
+{
+  return &loongarch_debug_process_state[pid];
+}
+
+/* Remove any existing per-process debug state for process PID.  */
+
+void
+loongarch_remove_debug_reg_state (pid_t pid)
+{
+  loongarch_debug_process_state.erase (pid);
+}
diff --git a/gdb/nat/loongarch-linux-hw-point.h b/gdb/nat/loongarch-linux-hw-point.h
new file mode 100644
index 00000000000..4086907e3c0
--- /dev/null
+++ b/gdb/nat/loongarch-linux-hw-point.h
@@ -0,0 +1,125 @@
+/* Native-dependent code for GNU/Linux on LoongArch processors.
+
+   Copyright (C) 2024 Free Software Foundation, Inc.
+   Contributed by Loongson Ltd.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef NAT_LOONGARCH_LINUX_HW_POINT_H
+#define NAT_LOONGARCH_LINUX_HW_POINT_H
+
+#include "gdbsupport/break-common.h" /* For enum target_hw_bp_type.  */
+
+#include "nat/loongarch-hw-point.h"
+
+struct loongarch_user_watch_state {
+	uint64_t dbg_info;
+	struct {
+		uint64_t    addr;
+		uint64_t    mask;
+		uint32_t    ctrl;
+		uint32_t    pad;
+	} dbg_regs[8];
+};
+
+
+/* Macros to extract fields from the hardware debug information word.  */
+#define LOONGARCH_DEBUG_NUM_SLOTS(x) ((x) & 0xffff)
+
+/* Each bit of a variable of this type is used to indicate whether a
+   hardware breakpoint or watchpoint setting has been changed since
+   the last update.
+
+   Bit N corresponds to the Nth hardware breakpoint or watchpoint
+   setting which is managed in loongarch_debug_reg_state, where N is
+   valid between 0 and the total number of the hardware breakpoint or
+   watchpoint debug registers minus 1.
+
+   When bit N is 1, the corresponding breakpoint or watchpoint setting
+   has changed, and therefore the corresponding hardware debug
+   register needs to be updated via the ptrace interface.
+
+   In the per-thread arch-specific data area, we define two such
+   variables for per-thread hardware breakpoint and watchpoint
+   settings respectively.
+
+   This type is part of the mechanism which helps reduce the number of
+   ptrace calls to the kernel, i.e. avoid asking the kernel to write
+   to the debug registers with unchanged values.  */
+
+typedef ULONGEST dr_changed_t;
+
+/* Set each of the lower M bits of X to 1; assert X is wide enough.  */
+
+#define DR_MARK_ALL_CHANGED(x, m)					\
+  do									\
+    {									\
+      gdb_assert (sizeof ((x)) * 8 >= (m));				\
+      (x) = (((dr_changed_t)1 << (m)) - 1);				\
+    } while (0)
+
+#define DR_MARK_N_CHANGED(x, n)						\
+  do									\
+    {									\
+      (x) |= ((dr_changed_t)1 << (n));					\
+    } while (0)
+
+#define DR_CLEAR_CHANGED(x)						\
+  do									\
+    {									\
+      (x) = 0;								\
+    } while (0)
+
+#define DR_HAS_CHANGED(x) ((x) != 0)
+#define DR_N_HAS_CHANGED(x, n) ((x) & ((dr_changed_t)1 << (n)))
+
+/* Per-thread arch-specific data we want to keep.  */
+
+struct arch_lwp_info
+{
+  /* When bit N is 1, it indicates the Nth hardware breakpoint or
+     watchpoint register pair needs to be updated when the thread is
+     resumed; see loongarch_linux_prepare_to_resume.  */
+  dr_changed_t dr_changed_wp;
+};
+
+/* Call ptrace to set the thread TID's hardware breakpoint/watchpoint
+   registers with data from *STATE.  */
+
+void loongarch_linux_set_debug_regs (struct loongarch_debug_reg_state *state,
+				     int tid, int watchpoint);
+
+/* Get the hardware debug register capacity information from the
+   process represented by TID.  */
+
+void loongarch_linux_get_debug_reg_capacity (int tid);
+
+/* Return the debug register state for process PID.  If no existing
+   state is found for this process, return nullptr.  */
+
+struct loongarch_debug_reg_state *loongarch_lookup_debug_reg_state (pid_t pid);
+
+/* Return the debug register state for process PID.  If no existing
+   state is found for this process, create new state.  */
+
+struct loongarch_debug_reg_state *loongarch_get_debug_reg_state (pid_t pid);
+
+/* Remove any existing per-process debug state for process PID.  */
+
+void loongarch_remove_debug_reg_state (pid_t pid);
+
+
+#endif /* NAT_LOONGARCH_LINUX_HW_POINT_H */
diff --git a/gdb/nat/loongarch-linux.c b/gdb/nat/loongarch-linux.c
new file mode 100644
index 00000000000..088d0fcbf34
--- /dev/null
+++ b/gdb/nat/loongarch-linux.c
@@ -0,0 +1,87 @@
+/* Native-dependent code for GNU/Linux on LoongArch processors.
+
+   Copyright (C) 2024 Free Software Foundation, Inc.
+   Contributed by Loongson Ltd.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "gdbsupport/common-defs.h"
+#include "gdbsupport/break-common.h"
+#include "nat/linux-nat.h"
+#include "nat/loongarch-linux-hw-point.h"
+#include "nat/loongarch-linux.h"
+
+#include "elf/common.h"
+#include "nat/gdb_ptrace.h"
+#include <asm/ptrace.h>
+#include <sys/uio.h>
+
+/* Called when resuming a thread LWP.
+   The hardware debug registers are updated when there is any change.  */
+
+void
+loongarch_linux_prepare_to_resume (struct lwp_info *lwp)
+{
+  struct arch_lwp_info *info = lwp_arch_private_info (lwp);
+
+  /* NULL means this is the main thread still going through the shell,
+     or, no watchpoint has been set yet.  In that case, there's
+     nothing to do.  */
+  if (info == NULL)
+    return;
+
+  if (DR_HAS_CHANGED (info->dr_changed_wp))
+    {
+      ptid_t ptid = ptid_of_lwp (lwp);
+      int tid = ptid.lwp ();
+      struct loongarch_debug_reg_state *state
+	= loongarch_get_debug_reg_state (ptid.pid ());
+
+      if (show_debug_regs)
+	debug_printf ("prepare_to_resume thread %d\n", tid);
+
+      loongarch_linux_set_debug_regs (state, tid, 1);
+      DR_CLEAR_CHANGED (info->dr_changed_wp);
+
+    }
+}
+
+/* Function to call when a new thread is detected.  */
+
+void
+loongarch_linux_new_thread (struct lwp_info *lwp)
+{
+  ptid_t ptid = ptid_of_lwp (lwp);
+  struct loongarch_debug_reg_state *state
+	= loongarch_get_debug_reg_state (ptid.pid ());
+  struct arch_lwp_info *info = XCNEW (struct arch_lwp_info);
+
+  /* If there are hardware breakpoints/watchpoints in the process then mark that
+     all the hardware breakpoint/watchpoint register pairs for this thread need
+     to be initialized (with data from arch_process_info.debug_reg_state).  */
+  if (loongarch_any_set_debug_regs_state (state, true))
+    DR_MARK_ALL_CHANGED (info->dr_changed_wp, loongarch_num_wp_regs);
+
+  lwp_set_arch_private_info (lwp, info);
+}
+
+/* See nat/loongarch-linux.h.  */
+
+void
+loongarch_linux_delete_thread (struct arch_lwp_info *arch_lwp)
+{
+  xfree (arch_lwp);
+}
diff --git a/gdb/nat/loongarch-linux.h b/gdb/nat/loongarch-linux.h
new file mode 100644
index 00000000000..f4bb75af88c
--- /dev/null
+++ b/gdb/nat/loongarch-linux.h
@@ -0,0 +1,42 @@
+/* Native-dependent code for GNU/Linux on LoongArch processors.
+
+   Copyright (C) 2024 Free Software Foundation, Inc.
+   Contributed by Loongson Ltd.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef NAT_LOONGARCH_LINUX_H
+#define NAT_LOONGARCH_LINUX_H
+
+#include <signal.h>
+
+/* Defines ps_err_e, struct ps_prochandle.  */
+#include "gdb_proc_service.h"
+
+/* Called when resuming a thread LWP.
+   The hardware debug registers are updated when there is any change.  */
+
+void loongarch_linux_prepare_to_resume (struct lwp_info *lwp);
+
+/* Function to call when a new thread is detected.  */
+
+void loongarch_linux_new_thread (struct lwp_info *lwp);
+
+/* Function to call when a thread is being deleted.  */
+
+void loongarch_linux_delete_thread (struct arch_lwp_info *arch_lwp);
+
+#endif /* LOONGARCH_LINUX_H */
diff --git a/include/elf/common.h b/include/elf/common.h
index 78c85bd514b..2d183342b49 100644
--- a/include/elf/common.h
+++ b/include/elf/common.h
@@ -751,6 +751,8 @@
 					/*   note name must be "LINUX".  */
 #define NT_LARCH_LBT    0xa04		/* LoongArch Binary Translation registers */
 					/*   note name must be "CORE".  */
+#define NT_LOONGARCH_HW_WATCH   0xa06	/* LoongArch hardware watchpoint registers */
+					/*   note name must be "LINUX".  */
 #define NT_RISCV_CSR    0x900		/* RISC-V Control and Status Registers */
 					/*   note name must be "LINUX".  */
 #define NT_SIGINFO	0x53494749	/* Fields of siginfo_t.  */
-- 
2.38.1


^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH v2 2/2] gdb: LoongArch: Add support for hardware breakpoint
  2024-06-11 11:21 [PATCH v2 0/2] gdb: LoongArch: Add support for hardware watchpoint & breakpoint Hui Li
  2024-06-11 11:21 ` [PATCH v2 1/2] gdb: LoongArch: Add support for hardware watchpoint Hui Li
@ 2024-06-11 11:21 ` Hui Li
  2024-06-24 21:55 ` [PATCH v2 0/2] gdb: LoongArch: Add support for hardware watchpoint & breakpoint Tiezhu Yang
  2 siblings, 0 replies; 4+ messages in thread
From: Hui Li @ 2024-06-11 11:21 UTC (permalink / raw)
  To: gdb-patches; +Cc: Tiezhu Yang

LoongArch defines hardware watchpoint functions for fetch operations.
After the software configures the watchpoints for fetch, the processor
hardware will monitor the access addresses of the fetch operations and
trigger a watchpoint exception when the watchpoint setting conditions
are met.

Hardware watchpoints for fetch operations is used to implement hardware
breakpoint function on LoongArch. Refer to the following document for
hardware breakpoint.
https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN.html#control-and-status-registers-related-to-watchpoints

A simple test is as follows:

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

without this patch:

lihui@bogon:~$ gdb test
...
(gdb) start
...
Temporary breakpoint 1, main () at test.c:5
5               printf("start test\n");
(gdb) hbreak 8
No hardware breakpoint support in the target.

with this patch:

lihui@bogon:~$ gdb test
...
(gdb) start
...

Temporary breakpoint 1, main () at test.c:5
5               printf("start test\n");
(gdb) hbreak 8
Hardware assisted breakpoint 2 at 0x1200006ec: file test.c, line 8.
(gdb) c
Continuing.
start test
a = 1

Breakpoint 2, main () at test.c:8
8               printf("end test\n");
(gdb) c
Continuing.
end test
[Inferior 1 (process 25378) exited normally]

Signed-off-by: Hui Li <lihui@loongson.cn>
---
 gdb/loongarch-linux-nat.c          | 74 +++++++++++++++++++++++++++++-
 gdb/nat/loongarch-hw-point.c       | 37 +++++++++++++--
 gdb/nat/loongarch-hw-point.h       | 11 +++++
 gdb/nat/loongarch-linux-hw-point.c | 51 ++++++++++++++------
 gdb/nat/loongarch-linux-hw-point.h |  1 +
 gdb/nat/loongarch-linux.c          | 21 +++++++--
 include/elf/common.h               |  2 +
 7 files changed, 174 insertions(+), 23 deletions(-)

diff --git a/gdb/loongarch-linux-nat.c b/gdb/loongarch-linux-nat.c
index 9f5760066b3..873628775e2 100644
--- a/gdb/loongarch-linux-nat.c
+++ b/gdb/loongarch-linux-nat.c
@@ -54,6 +54,11 @@ class loongarch_linux_nat_target final : public linux_nat_trad_target
   bool stopped_by_watchpoint () override;
   bool stopped_data_address (CORE_ADDR *) override;
 
+  int insert_hw_breakpoint (struct gdbarch *gdbarch,
+			    struct bp_target_info *bp_tgt) override;
+  int remove_hw_breakpoint (struct gdbarch *gdbarch,
+			    struct bp_target_info *bp_tgt) override;
+
   /* Override the GNU/Linux inferior startup hook.  */
   void post_startup_inferior (ptid_t) override;
 
@@ -489,7 +494,8 @@ loongarch_linux_nat_target::can_use_hw_breakpoint (enum bptype type, int cnt,
     }
   else if (type == bp_hardware_breakpoint)
     {
-      return 0;
+      if (loongarch_num_bp_regs == 0)
+	return 0;
     }
   else
     gdb_assert_not_reached ("unexpected breakpoint type");
@@ -624,6 +630,72 @@ loongarch_linux_nat_target::stopped_by_watchpoint ()
   return stopped_data_address (&addr);
 }
 
+/* Insert a hardware-assisted breakpoint at BP_TGT->reqstd_address.
+   Return 0 on success, -1 on failure.  */
+
+int
+loongarch_linux_nat_target::insert_hw_breakpoint (struct gdbarch *gdbarch,
+						  struct bp_target_info *bp_tgt)
+{
+  int ret;
+  CORE_ADDR addr = bp_tgt->placed_address = bp_tgt->reqstd_address;
+  int len;
+  const enum target_hw_bp_type type = hw_execute;
+  struct loongarch_debug_reg_state *state
+	= loongarch_get_debug_reg_state (inferior_ptid.pid ());
+
+  gdbarch_breakpoint_from_pc (gdbarch, &addr, &len);
+
+  if (show_debug_regs)
+    gdb_printf (gdb_stdlog,
+		"insert_hw_breakpoint on entry (addr=0x%08lx, len=%d))\n",
+		(unsigned long) addr, len);
+
+  ret = loongarch_handle_breakpoint (type, addr, len, 1 /* is_insert */,
+				     inferior_ptid, state);
+
+  if (show_debug_regs)
+    {
+      loongarch_show_debug_reg_state (state,
+				      "insert_hw_breakpoint", addr, len, type);
+    }
+
+  return ret;
+}
+
+/* Remove a hardware-assisted breakpoint at BP_TGT->placed_address.
+   Return 0 on success, -1 on failure.  */
+
+int
+loongarch_linux_nat_target::remove_hw_breakpoint (struct gdbarch *gdbarch,
+						  struct bp_target_info *bp_tgt)
+{
+  int ret;
+  CORE_ADDR addr = bp_tgt->placed_address;
+  int len = 4;
+  const enum target_hw_bp_type type = hw_execute;
+  struct loongarch_debug_reg_state *state
+    = loongarch_get_debug_reg_state (inferior_ptid.pid ());
+
+  gdbarch_breakpoint_from_pc (gdbarch, &addr, &len);
+
+  if (show_debug_regs)
+    gdb_printf (gdb_stdlog,
+		"remove_hw_breakpoint on entry (addr=0x%08lx, len=%d))\n",
+		(unsigned long) addr, len);
+
+  ret = loongarch_handle_breakpoint (type, addr, len, 0 /* is_insert */,
+				     inferior_ptid, state);
+
+  if (show_debug_regs)
+    {
+      loongarch_show_debug_reg_state (state,
+				      "remove_hw_watchpoint", addr, len, type);
+    }
+
+  return ret;
+}
+
 /* Implement the virtual inf_ptrace_target::post_startup_inferior method.  */
 
 void
diff --git a/gdb/nat/loongarch-hw-point.c b/gdb/nat/loongarch-hw-point.c
index 44af61abd00..089f3bd49a9 100644
--- a/gdb/nat/loongarch-hw-point.c
+++ b/gdb/nat/loongarch-hw-point.c
@@ -27,6 +27,7 @@
 /* Number of hardware breakpoints/watchpoints the target supports.
    They are initialized with values obtained via ptrace.  */
 
+int loongarch_num_bp_regs;
 int loongarch_num_wp_regs;
 
 /* Given the hardware breakpoint or watchpoint type TYPE and its
@@ -112,7 +113,10 @@ loongarch_dr_state_insert_one_point (ptid_t ptid,
     }
   else
     {
-      return -1;
+      num_regs = loongarch_num_bp_regs;
+      dr_addr_p = state->dr_addr_bp;
+      dr_ctrl_p = state->dr_ctrl_bp;
+      dr_ref_count = state->dr_ref_count_bp;
     }
 
   ctrl = loongarch_point_encode_ctrl_reg (type, len);
@@ -184,7 +188,10 @@ loongarch_dr_state_remove_one_point (ptid_t ptid,
     }
   else
     {
-      return -1;
+      num_regs = loongarch_num_bp_regs;
+      dr_addr_p = state->dr_addr_bp;
+      dr_ctrl_p = state->dr_ctrl_bp;
+      dr_ref_count = state->dr_ref_count_bp;
     }
 
   ctrl = loongarch_point_encode_ctrl_reg (type, len);
@@ -214,6 +221,20 @@ loongarch_dr_state_remove_one_point (ptid_t ptid,
   return 0;
 }
 
+int
+loongarch_handle_breakpoint (enum target_hw_bp_type type, CORE_ADDR addr,
+			   int len, int is_insert, ptid_t ptid,
+			   struct loongarch_debug_reg_state *state)
+{
+  if (is_insert)
+    return loongarch_dr_state_insert_one_point (ptid, state, type, addr,
+						len, -1);
+  else
+    return loongarch_dr_state_remove_one_point (ptid, state, type, addr,
+						len, -1);
+}
+
+
 int
 loongarch_handle_watchpoint (enum target_hw_bp_type type, CORE_ADDR addr,
 			     int len, int is_insert, ptid_t ptid,
@@ -234,12 +255,12 @@ bool
 loongarch_any_set_debug_regs_state (loongarch_debug_reg_state *state,
 				    bool watchpoint)
 {
-  int count = watchpoint ? loongarch_num_wp_regs : 0;
+  int count = watchpoint ? loongarch_num_wp_regs : loongarch_num_bp_regs;
   if (count == 0)
     return false;
 
-  const CORE_ADDR *addr = watchpoint ? state->dr_addr_wp : 0;
-  const unsigned int *ctrl = watchpoint ? state->dr_ctrl_wp : 0;
+  const CORE_ADDR *addr = watchpoint ? state->dr_addr_wp : state->dr_addr_bp;
+  const unsigned int *ctrl = watchpoint ? state->dr_ctrl_wp : state->dr_ctrl_bp;
 
   for (int i = 0; i < count; i++)
     if (addr[i] != 0 || ctrl[i] != 0)
@@ -268,6 +289,12 @@ loongarch_show_debug_reg_state (struct loongarch_debug_reg_state *state,
 			   : "??unknown??"))));
   debug_printf (":\n");
 
+  debug_printf ("\tBREAKPOINTs:\n");
+  for (i = 0; i < loongarch_num_bp_regs; i++)
+    debug_printf ("\tBP%d: addr=%s, ctrl=0x%08x, ref.count=%d\n",
+		  i, core_addr_to_string_nz (state->dr_addr_bp[i]),
+		  state->dr_ctrl_bp[i], state->dr_ref_count_bp[i]);
+
   debug_printf ("\tWATCHPOINTs:\n");
   for (i = 0; i < loongarch_num_wp_regs; i++)
     debug_printf ("\tWP%d: addr=%s, ctrl=0x%08x, ref.count=%d\n",
diff --git a/gdb/nat/loongarch-hw-point.h b/gdb/nat/loongarch-hw-point.h
index 86e5aa3f452..fd79285a046 100644
--- a/gdb/nat/loongarch-hw-point.h
+++ b/gdb/nat/loongarch-hw-point.h
@@ -33,6 +33,7 @@
    Neither of these values may exceed the width of dr_changed_t
    measured in bits.  */
 
+#define LOONGARCH_HBP_MAX_NUM 8
 #define LOONGARCH_HWP_MAX_NUM 8
 
 
@@ -53,12 +54,18 @@
 
 struct loongarch_debug_reg_state
 {
+  /* hardware breakpoint */
+  CORE_ADDR dr_addr_bp[LOONGARCH_HBP_MAX_NUM];
+  unsigned int dr_ctrl_bp[LOONGARCH_HBP_MAX_NUM];
+  unsigned int dr_ref_count_bp[LOONGARCH_HBP_MAX_NUM];
+
   /* hardware watchpoint */
   CORE_ADDR dr_addr_wp[LOONGARCH_HWP_MAX_NUM];
   unsigned int dr_ctrl_wp[LOONGARCH_HWP_MAX_NUM];
   unsigned int dr_ref_count_wp[LOONGARCH_HWP_MAX_NUM];
 };
 
+extern int loongarch_num_bp_regs;
 extern int loongarch_num_wp_regs;
 
 /* Invoked when IDXth breakpoint/watchpoint register pair needs to be
@@ -68,6 +75,10 @@ void loongarch_notify_debug_reg_change (ptid_t ptid, int is_watchpoint,
 				      unsigned int idx);
 
 
+int loongarch_handle_breakpoint (enum target_hw_bp_type type, CORE_ADDR addr,
+				 int len, int is_insert, ptid_t ptid,
+				 struct loongarch_debug_reg_state *state);
+
 int loongarch_handle_watchpoint (enum target_hw_bp_type type, CORE_ADDR addr,
 				 int len, int is_insert, ptid_t ptid,
 				 struct loongarch_debug_reg_state *state);
diff --git a/gdb/nat/loongarch-linux-hw-point.c b/gdb/nat/loongarch-linux-hw-point.c
index cced5a388ab..fbc80fdc2a1 100644
--- a/gdb/nat/loongarch-linux-hw-point.c
+++ b/gdb/nat/loongarch-linux-hw-point.c
@@ -62,9 +62,6 @@ loongarch_dr_change_callback (struct lwp_info *lwp, int is_watchpoint,
   dr_changed_t *dr_changed_ptr;
   dr_changed_t dr_changed;
 
-  if (!is_watchpoint)
-    return -1;
-
   if (info == NULL)
     {
       info = XCNEW (struct arch_lwp_info);
@@ -74,14 +71,19 @@ loongarch_dr_change_callback (struct lwp_info *lwp, int is_watchpoint,
   if (show_debug_regs)
     {
       debug_printf ("loongarch_dr_change_callback: \n\tOn entry:\n");
-      debug_printf ("\ttid%d, dr_changed_wp=0x%s\n",
-		    tid, phex (info->dr_changed_wp, 8));
+      debug_printf ("\ttid%d, dr_changed_bp=0x%s, "
+		    "dr_changed_wp=0x%s\n", tid,
+		    phex (info->dr_changed_bp, 8),
+		    phex (info->dr_changed_wp, 8));
     }
 
-  dr_changed_ptr = &info->dr_changed_wp;
+  dr_changed_ptr = is_watchpoint ? &info->dr_changed_wp
+		   : &info->dr_changed_bp;
   dr_changed = *dr_changed_ptr;
 
-  gdb_assert (idx >= 0 && idx <= loongarch_num_wp_regs);
+  gdb_assert (idx >= 0
+	      && (idx <= (is_watchpoint ? loongarch_num_wp_regs
+			  : loongarch_num_bp_regs)));
 
   /* The actual update is done later just before resuming the lwp,
      we just mark that one register pair needs updating.  */
@@ -95,8 +97,10 @@ loongarch_dr_change_callback (struct lwp_info *lwp, int is_watchpoint,
 
   if (show_debug_regs)
     {
-      debug_printf ("\tOn exit:\n\ttid%d, dr_changed_wp=0x%s\n",
-		    tid, phex (info->dr_changed_wp, 8));
+      debug_printf ("\tOn exit:\n\ttid%d, dr_changed_bp=0x%s, "
+		    "dr_changed_wp=0x%s\n", tid,
+		    phex (info->dr_changed_bp, 8),
+		    phex (info->dr_changed_wp, 8));
     }
 
   return 0;
@@ -136,9 +140,9 @@ loongarch_linux_set_debug_regs (struct loongarch_debug_reg_state *state,
 
   memset (&regs, 0, sizeof (regs));
   iov.iov_base = &regs;
-  count = watchpoint ? loongarch_num_wp_regs : 0;
-  addr = watchpoint ? state->dr_addr_wp : 0;
-  ctrl = watchpoint ? state->dr_ctrl_wp : 0;
+  count = watchpoint ? loongarch_num_wp_regs : loongarch_num_bp_regs;
+  addr = watchpoint ? state->dr_addr_wp : state->dr_addr_bp;
+  ctrl = watchpoint ? state->dr_ctrl_wp : state->dr_ctrl_bp;
 
   if (count == 0)
     return;
@@ -151,7 +155,9 @@ loongarch_linux_set_debug_regs (struct loongarch_debug_reg_state *state,
       regs.dbg_regs[i].ctrl = ctrl[i];
     }
 
-  if (ptrace(PTRACE_SETREGSET, tid, NT_LOONGARCH_HW_WATCH, (void *) &iov))
+  if (ptrace(PTRACE_SETREGSET, tid,
+	     watchpoint ? NT_LOONGARCH_HW_WATCH : NT_LOONGARCH_HW_BREAK,
+	     (void *) &iov))
     {
       if (errno == EINVAL)
 	error (_("Invalid argument setting hardware debug registers"));
@@ -194,6 +200,25 @@ loongarch_linux_get_debug_reg_capacity (int tid)
       loongarch_num_wp_regs = 0;
     }
 
+  /* Get hardware breakpoint register info.  */
+  result = ptrace (PTRACE_GETREGSET, tid, NT_LOONGARCH_HW_BREAK, &iov);
+  if ( result == 0)
+    {
+      loongarch_num_bp_regs = LOONGARCH_DEBUG_NUM_SLOTS (dreg_state.dbg_info);
+      if (loongarch_num_bp_regs > LOONGARCH_HBP_MAX_NUM)
+	{
+	  warning (_("Unexpected number of hardware breakpoint registers"
+		     " reported by ptrace, got %d, expected %d."),
+		   loongarch_num_bp_regs, LOONGARCH_HBP_MAX_NUM);
+	  loongarch_num_bp_regs = LOONGARCH_HBP_MAX_NUM;
+	}
+    }
+  else
+    {
+      warning (_("Unable to determine the number of hardware breakpoints"
+		 " available."));
+      loongarch_num_bp_regs = 0;
+    }
 }
 
 /* Return the debug register state for process PID.  If no existing
diff --git a/gdb/nat/loongarch-linux-hw-point.h b/gdb/nat/loongarch-linux-hw-point.h
index 4086907e3c0..8d96443f5e0 100644
--- a/gdb/nat/loongarch-linux-hw-point.h
+++ b/gdb/nat/loongarch-linux-hw-point.h
@@ -93,6 +93,7 @@ struct arch_lwp_info
   /* When bit N is 1, it indicates the Nth hardware breakpoint or
      watchpoint register pair needs to be updated when the thread is
      resumed; see loongarch_linux_prepare_to_resume.  */
+  dr_changed_t dr_changed_bp;
   dr_changed_t dr_changed_wp;
 };
 
diff --git a/gdb/nat/loongarch-linux.c b/gdb/nat/loongarch-linux.c
index 088d0fcbf34..03a0aaf9fd9 100644
--- a/gdb/nat/loongarch-linux.c
+++ b/gdb/nat/loongarch-linux.c
@@ -43,7 +43,8 @@ loongarch_linux_prepare_to_resume (struct lwp_info *lwp)
   if (info == NULL)
     return;
 
-  if (DR_HAS_CHANGED (info->dr_changed_wp))
+  if (DR_HAS_CHANGED (info->dr_changed_bp)
+      || DR_HAS_CHANGED (info->dr_changed_wp))
     {
       ptid_t ptid = ptid_of_lwp (lwp);
       int tid = ptid.lwp ();
@@ -53,9 +54,19 @@ loongarch_linux_prepare_to_resume (struct lwp_info *lwp)
       if (show_debug_regs)
 	debug_printf ("prepare_to_resume thread %d\n", tid);
 
-      loongarch_linux_set_debug_regs (state, tid, 1);
-      DR_CLEAR_CHANGED (info->dr_changed_wp);
-
+      /* Watchpoints.  */
+      if (DR_HAS_CHANGED (info->dr_changed_wp))
+	{
+	  loongarch_linux_set_debug_regs (state, tid, 1);
+	  DR_CLEAR_CHANGED (info->dr_changed_wp);
+	}
+
+      /* Breakpoints.  */
+      if (DR_HAS_CHANGED (info->dr_changed_bp))
+	{
+	  loongarch_linux_set_debug_regs (state, tid, 0);
+	  DR_CLEAR_CHANGED (info->dr_changed_bp);
+	}
     }
 }
 
@@ -72,6 +83,8 @@ loongarch_linux_new_thread (struct lwp_info *lwp)
   /* If there are hardware breakpoints/watchpoints in the process then mark that
      all the hardware breakpoint/watchpoint register pairs for this thread need
      to be initialized (with data from arch_process_info.debug_reg_state).  */
+  if (loongarch_any_set_debug_regs_state (state, false))
+    DR_MARK_ALL_CHANGED (info->dr_changed_bp, loongarch_num_bp_regs);
   if (loongarch_any_set_debug_regs_state (state, true))
     DR_MARK_ALL_CHANGED (info->dr_changed_wp, loongarch_num_wp_regs);
 
diff --git a/include/elf/common.h b/include/elf/common.h
index 2d183342b49..c9920e7731a 100644
--- a/include/elf/common.h
+++ b/include/elf/common.h
@@ -751,6 +751,8 @@
 					/*   note name must be "LINUX".  */
 #define NT_LARCH_LBT    0xa04		/* LoongArch Binary Translation registers */
 					/*   note name must be "CORE".  */
+#define NT_LOONGARCH_HW_BREAK   0xa05	/* LoongArch hardware breakpoint registers */
+					/*   note name must be "LINUX".  */
 #define NT_LOONGARCH_HW_WATCH   0xa06	/* LoongArch hardware watchpoint registers */
 					/*   note name must be "LINUX".  */
 #define NT_RISCV_CSR    0x900		/* RISC-V Control and Status Registers */
-- 
2.38.1


^ permalink raw reply	[flat|nested] 4+ messages in thread

* Re: [PATCH v2 0/2] gdb: LoongArch: Add support for hardware watchpoint & breakpoint
  2024-06-11 11:21 [PATCH v2 0/2] gdb: LoongArch: Add support for hardware watchpoint & breakpoint Hui Li
  2024-06-11 11:21 ` [PATCH v2 1/2] gdb: LoongArch: Add support for hardware watchpoint Hui Li
  2024-06-11 11:21 ` [PATCH v2 2/2] gdb: LoongArch: Add support for hardware breakpoint Hui Li
@ 2024-06-24 21:55 ` Tiezhu Yang
  2 siblings, 0 replies; 4+ messages in thread
From: Tiezhu Yang @ 2024-06-24 21:55 UTC (permalink / raw)
  To: Hui Li, gdb-patches

On 6/11/24 19:21, Hui Li wrote:
> LoongArch defines hardware watchpoint functions for fetch and load/store
> operations. After the software configures the watchpoints for fetch and
> load/store, the processor hardware will monitor the access addresses of
> the fetch and load/store operations and trigger a watchpoint exception
> when the watchpoint setting conditions are met.
> 
> After this series, watch/rwatch/awatch/hbreak command are supported.

The related kernel patches have been merged into Linux 6.10-rc5,
so it is time to merge these gdb patches, pushed.

Thanks,
Tiezhu


^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2024-06-24 21:55 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-06-11 11:21 [PATCH v2 0/2] gdb: LoongArch: Add support for hardware watchpoint & breakpoint Hui Li
2024-06-11 11:21 ` [PATCH v2 1/2] gdb: LoongArch: Add support for hardware watchpoint Hui Li
2024-06-11 11:21 ` [PATCH v2 2/2] gdb: LoongArch: Add support for hardware breakpoint Hui Li
2024-06-24 21:55 ` [PATCH v2 0/2] gdb: LoongArch: Add support for hardware watchpoint & breakpoint Tiezhu Yang

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).